Quiz - Component tests, off by default
A teammate’s PR adds a component test for an async Server Component that fetches and renders this org’s invoices. They argue it’s worth testing because the list is a high-traffic surface. What’s the senior response?
Which of these components meet a trigger to reach for RTL? Select all that apply.
<DateRangePicker> shared across Reports, Invoices, and Filters.<Section> wrapper whose only job is to add vertical spacing.Under globals: false, you write your first *.dom.test.tsx files and they pass individually but start failing in a full run with “found multiple elements.” What’s missing from the jsdom setup file?
afterEach(cleanup) — without a global afterEach for RTL to auto-hook, each rendered tree stays mounted into the next test, so queries match elements from a previous render.ResizeObserver polyfill — the missing browser API leaves stale nodes mounted across tests.vi.resetAllMocks() in afterEach — without it the navigation mock returns the previous test’s elements.afterEach; with globals: false it doesn’t, so you register afterEach(cleanup) by hand. Skip it and trees leak across tests, and a query that should match one element finds two. Polyfills and mock-resets fix different problems, not stale DOM.Your render helper calls userEvent.setup() with no options. A teammate suggests copying userEvent.setup({ delay: null }) from an older tutorial to speed tests up. Is that the right default?
userEvent.setup() is the correct default; the docs discourage { delay: null } and warn it causes unexpected behavior, especially with fake timers.{ delay: null } removes inter-keystroke delays and is the recommended default for any test suite.render helper, never inside an individual it block.userEvent.setup() is the default this suite uses. { delay: null } is discouraged and interacts badly with fake timers; the one documented escape hatch for a timer-driven test is { advanceTimers: vi.advanceTimersByTime }, reached for only when a timer actually forces it.A Confirm button carries class btn-primary, id submit, text Confirm, aria-label="Confirm purchase", role button, and data-testid="submit-btn". You’re proving a user can click it. Which query belongs at the top of the ladder?
screen.getByRole('button', { name: /confirm purchase/i })screen.getByTestId('submit-btn')screen.getByText('Confirm')<div>; getByText('Confirm') ignores the role and could also match a tooltip, throwing on the ambiguity.You want to assert that no validation error is shown before the user submits. Which query family is correct, and why?
queryBy* — it returns null when nothing matches, which is the only family that lets a “not present” assertion run.getBy* — it’s the default for any presence check, and .not.toBeInTheDocument() flips it to a negative.findBy* — absence is an async condition, so you must wait to confirm nothing appeared.getBy* throws the instant it finds no match — before your expect ever runs — so the test fails with “could not find an element” instead of cleanly asserting absence. queryBy* returns null for the negative case. findBy* is for elements that appear after async work, not for proving something never showed up.A subscribe-form component test mocks createSubscription, clicks Subscribe, and its only assertion is expect(createSubscription).toHaveBeenCalledWith({ plan: 'pro', seats: 5 }). It passes. What’s wrong with it?
status region, or a declined-card alert), which survives a field rename and fails for the right reason. Reading back the row would be mocking too deep — that effect belongs to the seam test.For the date-range picker test, why pin time with vi.setSystemTime(new Date('2026-05-14')) before asserting that pressing {ArrowRight} moves focus to “15 May”?
userEvent.keyboard needs fake timers installed, and setSystemTime is what installs them.Quiz complete
Score by topic