{"checkpoints":{"s-tj-1":[{"q":"<p>A user reports that splitting a $100.00 dinner between three people leaves the balances one cent off. Which test do you write first?</p>","options":[{"text":"A Playwright test that adds the dinner in the browser and reads the balances.","why":"It would reproduce the bug, but slowly and with the whole stack in play; when it fails you still have to find the rounding code."},{"text":"A screenshot test of the balances screen.","why":"A screenshot can show the wrong number but says nothing about which calculation produced it, and it breaks on any visual change."},{"text":"A stricter TypeScript type for the amount parameter.","why":"The values are already numbers; a type cannot tell 3333 from 3334 cents."},{"text":"A unit test that splits 10000 cents between 3 people and checks the shares add up to 10000.","why":"Correct. The bug is in pure logic, so the cheapest test that fails exactly when it is wrong is a millisecond unit test with the sum invariant."}],"answer":3},{"q":"<p>Assuming the imports are in place, what happens when this test runs?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">test</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'formats cents'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  expect</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">formatMoney</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1234</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toEqual</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'$12.34'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  expect</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">formatMoney</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">5</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toBe</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'$0.05'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">});</span></span></code></pre></div>\n","options":[{"text":"It passes if <code dir=\"ltr\">formatMoney</code> is correct: <code dir=\"ltr\">toEqual</code> compares primitives by value, just like <code dir=\"ltr\">toBe</code>.","why":"Correct. The difference between the two matchers only shows up for objects and arrays."},{"text":"It fails, because <code dir=\"ltr\">toEqual</code> can only be used with objects.","why":"<code dir=\"ltr\">toEqual</code> works with any value; for strings it behaves like <code dir=\"ltr\">toBe</code>."},{"text":"It fails, because a test may contain only one <code dir=\"ltr\">expect</code>.","why":"There is no such rule; one behaviour per test is the guideline, and both lines here check formatting."},{"text":"It only runs when <code dir=\"ltr\">globals: true</code> is set in the config.","why":"With explicit imports from <code dir=\"ltr\">vitest</code>, globals are not needed."}],"answer":0},{"q":"<p>Your <code dir=\"ltr\">package.json</code> has <code dir=\"ltr\">&quot;test&quot;: &quot;vitest&quot;</code>, and the GitHub Actions job runs <code dir=\"ltr\">npm test</code>. What happens after the tests finish in CI?</p>","options":[{"text":"The job hangs in watch mode until it hits the runner timeout.","why":"That is what would happen without CI detection; Vitest checks the <code dir=\"ltr\">CI</code> environment variable."},{"text":"Vitest exits with an error asking for the <code dir=\"ltr\">--run</code> flag.","why":"Vitest does not require the flag in CI; it chooses run mode itself."},{"text":"Vitest notices the <code dir=\"ltr\">CI</code> environment variable, runs once and exits with the result.","why":"Correct. Watch mode is only the default in an interactive local terminal."},{"text":"Vitest skips the tests because no terminal is attached.","why":"Tests run normally in CI; only the watching is turned off."}],"answer":2},{"q":"<p>A test fails with this output. What is the most likely bug?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>FAIL  src/tip.test.js &gt; withTip &gt; adds a 15% tip\nAssertionError: expected { tipCents: 300, totalCents: 2000 }\n  to deeply equal { tipCents: 300, totalCents: 2300 }</code></pre></div>\n","options":[{"text":"The tip is rounded the wrong way.","why":"The received <code dir=\"ltr\">tipCents</code> is 300, exactly as expected, so the tip calculation is fine."},{"text":"The test should use <code dir=\"ltr\">toBe</code> instead of <code dir=\"ltr\">toEqual</code>.","why":"<code dir=\"ltr\">toBe</code> would fail even for a correct result, because the function returns a new object."},{"text":"The percent is divided by 1000 instead of 100.","why":"Then the tip would be 30, not 300; the tip in the received object is right."},{"text":"The total is returned without adding the tip.","why":"Correct. <code dir=\"ltr\">totalCents</code> equals the subtotal (2000), so the function forgot to add <code dir=\"ltr\">tipCents</code>."}],"answer":3},{"q":"<p>Your project has 300 tests of pure money logic and 5 test files that render DOM. How should you configure the environment?</p>","options":[{"text":"Set <code dir=\"ltr\">environment: 'jsdom'</code> globally so every test can use the DOM.","why":"All 300 pure tests then pay for building a fake browser, and accidental DOM use in pure code goes unnoticed."},{"text":"Keep the default Node environment and add <code dir=\"ltr\">// @vitest-environment jsdom</code> to the top of the 5 DOM test files.","why":"Correct. Opt in per file where a DOM is needed and keep the rest fast and honest."},{"text":"Stub <code dir=\"ltr\">document</code> with a plain object in a setup file.","why":"A hand-made stub misses most DOM behaviour; jsdom or happy-dom exist precisely to provide it."},{"text":"Move the 5 DOM tests to Playwright.","why":"End-to-end tests are far slower; DOM behaviour like rendering a list is well served by jsdom."}],"answer":1}],"s-tj-2":[{"q":"<p>Splitwise-lite gives free premium to groups that spend $50.00 (5000 cents) or more. Which two inputs best test the rule?</p>","options":[{"text":"4999 and 5000","why":"Correct. They sit on either side of the boundary, the only pair that can tell <code dir=\"ltr\">&gt;=</code> from <code dir=\"ltr\">&gt;</code>."},{"text":"1000 and 10000","why":"Both are far from the boundary, so an off-by-one in the comparison passes both."},{"text":"0 and 5000","why":"Zero is a useful extra case, but without 4999 you cannot tell whether the cut-off is in the right place."},{"text":"5000 and 5001","why":"Both should qualify, so a bug that accepts 4999 too would not be caught."}],"answer":0},{"q":"<p>What should change in this test?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">test</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'rejects a negative amount'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  expect</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">saveExpense</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ description: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Taxi'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, amountCents: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">100</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }, api))</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    .rejects.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toThrow</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'positive'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">});</span></span></code></pre></div>\n","options":[{"text":"Nothing; it is correct as written.","why":"The <code dir=\"ltr\">rejects</code> assertion is never awaited, so the test does not wait for it; Vitest 4 marks such tests as failed."},{"text":"Replace <code dir=\"ltr\">toThrow</code> with <code dir=\"ltr\">toThrowError</code>.","why":"They are aliases; the missing <code dir=\"ltr\">await</code> is the problem."},{"text":"Wrap the call in <code dir=\"ltr\">try/catch</code> and assert in the <code dir=\"ltr\">catch</code>.","why":"That works only with <code dir=\"ltr\">expect.assertions(1)</code>, and it is clumsier than awaiting <code dir=\"ltr\">rejects</code>."},{"text":"Put <code dir=\"ltr\">await</code> in front of <code dir=\"ltr\">expect(...)</code> so the test waits for the rejection.","why":"Correct. Every <code dir=\"ltr\">resolves</code> or <code dir=\"ltr\">rejects</code> assertion should be awaited."}],"answer":3},{"q":"<p>While testing <code dir=\"ltr\">computeBalances</code>, a test replaces <code dir=\"ltr\">splitEvenly</code> with <code dir=\"ltr\">vi.fn().mockReturnValue([500, 500])</code>. Later <code dir=\"ltr\">splitEvenly</code> gets a rounding bug. What happens to that test?</p>","options":[{"text":"It fails, because <code dir=\"ltr\">computeBalances</code> now gets wrong shares.","why":"<code dir=\"ltr\">computeBalances</code> never calls the real <code dir=\"ltr\">splitEvenly</code> in that test, so it never sees the bug."},{"text":"It stays green, because the real <code dir=\"ltr\">splitEvenly</code> never runs in it.","why":"Correct. Mocking your own pure code removes exactly the integration that would catch this."},{"text":"Vitest warns that the mock no longer matches the implementation.","why":"Vitest does not compare mocks with real code; a mock is whatever you tell it to be."},{"text":"The test errors because mocks of internal modules are not allowed.","why":"Vitest allows them, which is why the judgement is yours."}],"answer":1},{"q":"<p>The second row of this table fails. What test name does Vitest report?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">test.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">each</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">([</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { total: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1000</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, people: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, first: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">334</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { total: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1001</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, people: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, first: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">251</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'splits $total between $people'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, ({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">total</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">people</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">first</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  expect</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">splitEvenly</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(total, people)[</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toBe</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(first);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">});</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">splits $total between $people</code>","why":"The <code dir=\"ltr\">$field</code> placeholders are filled from each row object."},{"text":"<code dir=\"ltr\">splits 1001 between 4 (row 2)</code>","why":"Vitest does not append the row number unless you use <code dir=\"ltr\">%#</code> in the name."},{"text":"<code dir=\"ltr\">splits 1001 between 4</code>","why":"Correct. Each row becomes its own test with the values substituted, so the failure names the input."},{"text":"<code dir=\"ltr\">splits [object Object] between undefined</code>","why":"<code dir=\"ltr\">$total</code> and <code dir=\"ltr\">$people</code> read those properties from each row object, so the real values appear in the name."}],"answer":2},{"q":"<p>A reminder is scheduled with <code dir=\"ltr\">setTimeout</code> to go out 24 hours after an expense is added. With <code dir=\"ltr\">vi.useFakeTimers()</code> active, which line makes the reminder fire in the test?</p>","options":[{"text":"<code dir=\"ltr\">await new Promise((r) =&gt; setTimeout(r, 86_400_000))</code>","why":"Under fake timers this <code dir=\"ltr\">setTimeout</code> is fake too, so it never fires and the test times out."},{"text":"<code dir=\"ltr\">vi.useRealTimers()</code>","why":"Switching back to real timers would mean waiting a real day."},{"text":"<code dir=\"ltr\">vi.setSystemTime(tomorrow)</code>","why":"It moves the clock that <code dir=\"ltr\">Date</code> reads, but it does not run timers that are due."},{"text":"<code dir=\"ltr\">vi.advanceTimersByTime(24 * 60 * 60 * 1000)</code>","why":"Correct. Advancing the fake clock runs every timer due in that window, instantly."}],"answer":3}],"s-tj-3":[{"q":"<p>The form shows validation errors in <code dir=\"ltr\">&lt;p role=&quot;alert&quot;&gt;Amount must be greater than zero&lt;/p&gt;</code>. Which query fits best?</p>","options":[{"text":"<code dir=\"ltr\">screen.getByRole('alert')</code>","why":"Correct. The role is what screen readers announce, so the test fails if the message stops being announced."},{"text":"<code dir=\"ltr\">container.querySelector('.error-text')</code>","why":"A class is styling; renaming it breaks the test without breaking anything users see."},{"text":"<code dir=\"ltr\">screen.getByTestId('amount-error')</code>","why":"Test ids are the last resort and prove nothing about how the message reaches users."},{"text":"<code dir=\"ltr\">screen.getByDisplayValue('Amount must be greater than zero')</code>","why":"Display value queries match the current value of form fields, not paragraph text."}],"answer":0},{"q":"<p><code dir=\"ltr\">load</code> resolves with <code dir=\"ltr\">{ Ben: -500 }</code>. This test fails with “Unable to find an element with the text”. What is the fix?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">JSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">test</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'shows balances'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  render</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">BalancePanel</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> loadBalances</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">load</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> />);</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  expect</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(screen.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">getByText</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Ben owes $5.00'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toBeInTheDocument</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">});</span></span></code></pre></div>\n","options":[{"text":"Wrap <code dir=\"ltr\">render</code> in <code dir=\"ltr\">waitFor</code>.","why":"<code dir=\"ltr\">waitFor</code> retries assertions; rendering repeatedly inside it does not help and causes side effects."},{"text":"Raise Vitest’s test timeout.","why":"The test fails immediately, not after a timeout: <code dir=\"ltr\">getByText</code> checks once, before the promise resolves."},{"text":"Make the test <code dir=\"ltr\">async</code> and use <code dir=\"ltr\">expect(await screen.findByText('Ben owes $5.00')).toBeInTheDocument()</code>.","why":"Correct. <code dir=\"ltr\">findByText</code> waits for the text to appear after the data loads."},{"text":"Use <code dir=\"ltr\">screen.queryByText('Ben owes $5.00')</code> instead.","why":"<code dir=\"ltr\">queryBy</code> also checks only once; it would return <code dir=\"ltr\">null</code> and the assertion would still fail."}],"answer":2},{"q":"<p>Component tests use MSW with <code dir=\"ltr\">server.listen({ onUnhandledRequest: 'error' })</code>. A refactor accidentally changes the request URL from <code dir=\"ltr\">/groups/:id</code> to <code dir=\"ltr\">/group/:id</code>. What happens?</p>","options":[{"text":"The tests pass, because MSW answers every request with an empty 200.","why":"MSW only answers requests that match a handler; it has no catch-all success response."},{"text":"The tests fail, because the request matches no handler and unhandled requests are errors.","why":"Correct. That option turns a wrong URL into a loud failure instead of a silent hang or a real network call."},{"text":"MSW silently forwards the request to the real network.","why":"With <code dir=\"ltr\">onUnhandledRequest: 'error'</code>, unmatched requests are reported as errors, not passed through quietly."},{"text":"Vitest skips the affected tests.","why":"Nothing is skipped; the failing request surfaces as a test failure."}],"answer":1},{"q":"<p>Which use of <code dir=\"ltr\">waitFor</code> is appropriate?</p>","options":[{"text":"<code dir=\"ltr\">await waitFor(() =&gt; user.click(screen.getByRole('button', { name: 'Save' })))</code>","why":"A click inside a retried callback may happen several times; interactions belong outside <code dir=\"ltr\">waitFor</code>."},{"text":"<code dir=\"ltr\">await waitFor(() =&gt; screen.findByText('Saved'))</code>","why":"<code dir=\"ltr\">findBy</code> already waits; wrapping it in <code dir=\"ltr\">waitFor</code> doubles the waiting and muddles the timeout."},{"text":"<code dir=\"ltr\">await waitFor(() =&gt; {})</code>","why":"An empty callback passes immediately and waits for nothing in particular."},{"text":"<code dir=\"ltr\">await waitFor(() =&gt; expect(onSave).toHaveBeenCalledTimes(1))</code>","why":"Correct. An assertion that is not about an element appearing, retried until it holds."}],"answer":3},{"q":"<p><code dir=\"ltr\">ExpenseList</code> has a “Hide settled” checkbox. What’s the best test for it?</p>","options":[{"text":"Click the checkbox with user-event and assert which list items are still on screen.","why":"Correct. It tests what the user sees and survives any change to how the state is stored."},{"text":"Assert that <code dir=\"ltr\">useState</code> was called with <code dir=\"ltr\">false</code> initially.","why":"That checks React internals, not behaviour; the list could still render wrongly."},{"text":"Take a snapshot before and after clicking.","why":"Snapshots record everything and explain nothing; reviewers cannot tell what should have changed."},{"text":"Call the component as a function and inspect the returned JSX.","why":"Calling components directly skips React rendering and hooks rules; render it instead."}],"answer":0}],"s-tj-4":[{"q":"<p>The amount field is <code dir=\"ltr\">&lt;label for=&quot;amount&quot;&gt;Amount&lt;/label&gt;&lt;input id=&quot;amount&quot; name=&quot;amount&quot; placeholder=&quot;0.00&quot;&gt;</code>. Which Playwright locator is best?</p>","options":[{"text":"<code dir=\"ltr\">page.locator('input[name=amount]')</code>","why":"It works, but depends on an attribute users never see and checks nothing about the label."},{"text":"<code dir=\"ltr\">page.getByPlaceholder('0.00')</code>","why":"Placeholders are a weaker signal than labels and often change or disappear."},{"text":"<code dir=\"ltr\">page.getByLabel('Amount')</code>","why":"Correct. It finds the field by its label, the way users and assistive tech do."},{"text":"<code dir=\"ltr\">page.locator('#amount')</code>","why":"Ids are implementation details that can change in a refactor."}],"answer":2},{"q":"<p>Logging in takes 3 seconds. Forty tests need a logged-in user but never change the account. What is the best approach?</p>","options":[{"text":"Log in through the UI in a <code dir=\"ltr\">beforeEach</code>.","why":"That adds 2 minutes of logins and hammers the auth service for no extra confidence."},{"text":"Let one test log in and run the others after it with serial mode.","why":"Chained tests cannot run in parallel or alone, and one failure cascades."},{"text":"Share one <code dir=\"ltr\">page</code> object between all tests in a module-level variable.","why":"Sharing a page breaks isolation; one test can leave state that changes another."},{"text":"Log in once in a setup project, save the storage state, and let the tests reuse it.","why":"Correct. Each test still gets its own context, already authenticated."}],"answer":3},{"q":"<p>In a failed test’s trace, the click on “Settle up” succeeded, and the DOM snapshot right after it shows a “Session expired, please sign in” toast. What should you investigate first?</p>","options":[{"text":"The test’s authentication setup, for example a stale saved storage state.","why":"Correct. The page did respond; it rejected the session, so the problem is in how the test logs in."},{"text":"Adding <code dir=\"ltr\">page.waitForTimeout(2000)</code> before the assertion.","why":"Waiting longer will not revive an expired session."},{"text":"Changing the Settle up locator.","why":"The trace shows the click landed on the right button."},{"text":"Upgrading Playwright, since this looks like a tool bug.","why":"The snapshot shows the app behaving correctly for an expired session; the test setup is the suspect."}],"answer":0},{"q":"<p>You want to test that the group page shows “Could not load this group” when the API answers 503. What do you do?</p>","options":[{"text":"Stop the backend server in CI before running that test.","why":"That breaks every other test running in parallel against the same server."},{"text":"Call <code dir=\"ltr\">page.route('**/api/groups/*', (route) =&gt; route.fulfill({ status: 503 }))</code> before <code dir=\"ltr\">page.goto</code>.","why":"Correct. Routing produces the rare response for this one test without touching the real server."},{"text":"Replace <code dir=\"ltr\">fetch</code> with <code dir=\"ltr\">vi.fn()</code> inside the Playwright test.","why":"<code dir=\"ltr\">vi</code> is Vitest; Playwright tests run in Node while the page runs in a real browser."},{"text":"Throw an error inside a <code dir=\"ltr\">beforeEach</code> hook.","why":"That fails the test before it starts instead of simulating the server response."}],"answer":1},{"q":"<p>A test always passes with <code dir=\"ltr\">--workers=1</code> and sometimes fails with <code dir=\"ltr\">--workers=4</code>. What is the first suspect?</p>","options":[{"text":"Timeouts that are too short for parallel runs.","why":"Possible, but the classic cause of order and parallelism sensitivity is shared state."},{"text":"A browser version mismatch between workers.","why":"All workers use the same installed browsers."},{"text":"Tests sharing data on the server, so parallel tests change what another one sees.","why":"Correct. Give each test its own data, for example through a fixture with unique names."},{"text":"Too many assertions in one test.","why":"The number of assertions does not interact with the number of workers."}],"answer":2}],"s-tj-5":[{"q":"<p>Test-first, you have <code dir=\"ltr\">parseAmount('12')</code> passing with <code dir=\"ltr\">return Number(input) * 100;</code>. Which next test forces the smallest useful step?</p>","options":[{"text":"A 500-row table of every format you can think of.","why":"Writing many tests at once forces a big leap; TDD moves one small failing test at a time."},{"text":"<code dir=\"ltr\">expect(() =&gt; parseAmount('abc')).toThrow()</code>","why":"Correct. It fails today (the code returns <code dir=\"ltr\">NaN</code>) and needs one small change: reject non-numbers."},{"text":"<code dir=\"ltr\">expect(parseAmount('12')).toBe(1200)</code> under a different name.","why":"It already passes, so it forces nothing new."},{"text":"A Playwright test of the whole expense form.","why":"Far too big a step, and slow feedback for a parsing rule."}],"answer":1},{"q":"<p><code dir=\"ltr\">src/money/settle-up.js</code> has no tests, but it does not appear in the Vitest coverage report at all. Why?</p>","options":[{"text":"The v8 provider cannot measure that file.","why":"v8 measures any JavaScript the tests load; the file was never loaded."},{"text":"The file is fully covered, so it is hidden.","why":"Covered files are listed too; nothing hides them."},{"text":"Coverage thresholds filter out files below the limit.","why":"Thresholds fail the run; they do not remove files from the report."},{"text":"<code dir=\"ltr\">coverage.include</code> is not set, so only files loaded by the tests are reported.","why":"Correct. Set <code dir=\"ltr\">include</code> (for example <code dir=\"ltr\">src/**/*.js</code>) so untested files show up at 0%."}],"answer":3},{"q":"<p>Stryker reports <code dir=\"ltr\">EqualityOperator: amountCents &lt;= 0 → amountCents &lt; 0</code> as Survived in <code dir=\"ltr\">saveExpense</code>’s validation. Which test kills it?</p>","options":[{"text":"Saving an expense with <code dir=\"ltr\">amountCents: 0</code> must reject.","why":"Correct. Zero is the only value where <code dir=\"ltr\">&lt;= 0</code> and <code dir=\"ltr\">&lt; 0</code> disagree."},{"text":"Saving an expense with <code dir=\"ltr\">amountCents: -100</code> must reject.","why":"Both versions reject -100, so the mutant survives."},{"text":"Saving an expense with <code dir=\"ltr\">amountCents: 4500</code> must resolve.","why":"Both versions accept 4500, so the mutant survives this test."},{"text":"Saving an expense with <code dir=\"ltr\">amountCents: 12.5</code> must reject.","why":"The integer check rejects 12.5 in both versions."}],"answer":0},{"q":"<p>In the GitHub Actions workflow, the Playwright report artifact is missing every time the tests fail. What is the upload step missing?</p>","options":[{"text":"<code dir=\"ltr\">needs: e2e</code>","why":"<code dir=\"ltr\">needs</code> orders jobs; the problem is a step being skipped inside the same job."},{"text":"<code dir=\"ltr\">retention-days: 30</code>","why":"Retention controls how long an artifact is kept, not whether it is uploaded."},{"text":"<code dir=\"ltr\">if: ${{ !cancelled() }}</code>, so it runs even after a failed test step.","why":"Correct. Steps are skipped after a failure by default; the condition makes the upload run anyway."},{"text":"<code dir=\"ltr\">--shard=1/1</code> on the test command.","why":"Sharding splits the suite; it has nothing to do with uploading reports."}],"answer":2},{"q":"<p>Which of these tests is worth writing?</p>","options":[{"text":"That lodash’s <code dir=\"ltr\">sortBy</code> sorts expenses by date.","why":"That tests the library, which its maintainers already do."},{"text":"That the error message has the class <code dir=\"ltr\">text-red-600</code>.","why":"Styling details make tests brittle without catching behaviour bugs."},{"text":"That a private helper is called exactly twice.","why":"It locks in the implementation and passes on real bugs."},{"text":"That one leftover cent goes to the first person in the split.","why":"Correct. It is documented money behaviour that a plausible bug would break."}],"answer":3}]},"final":[{"q":"<p>A form parses the amount and sends it through an API client. Which test gives the most confidence per minute of CI for that form?</p>","options":[{"text":"A component test that renders the form with the real parser and a faked network.","why":"Correct. It exercises the form, the parsing and the request together, in milliseconds, without a browser or server."},{"text":"A unit test of the API client alone.","why":"Useful, but it says nothing about whether the form uses the parser and the client correctly."},{"text":"An end-to-end test for every validation message.","why":"Each message is cheaper and more precise to check at the component level; save end-to-end for the main flow."},{"text":"Stricter TypeScript types on the form props.","why":"Types catch shape errors, not whether 12.5 becomes 1250 cents."}],"answer":0},{"q":"<p>What is the main problem with this test?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"5\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">test</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'withTip'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, () </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  expect</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">withTip</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2000</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">15</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).tipCents).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toBe</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">300</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  expect</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> withTip</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2000</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toThrow</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  expect</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">withTip</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2000</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).totalCents).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toBe</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2000</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">});</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">toBe</code> cannot compare numbers reliably.","why":"For integers like these, <code dir=\"ltr\">toBe</code> is exactly right."},{"text":"It checks three behaviours, so a failure in the first hides the others, and the name does not say what broke.","why":"Correct. Split it into three tests named after the rules they check."},{"text":"A test may contain only one <code dir=\"ltr\">expect</code> call.","why":"Several assertions about one behaviour are fine; the issue is three behaviours."},{"text":"<code dir=\"ltr\">withTip</code> must be awaited.","why":"<code dir=\"ltr\">withTip</code> is synchronous; there is nothing to await."}],"answer":1},{"q":"<p>A group must have between 2 and 12 members. Which inputs test the rule best?</p>","options":[{"text":"5, 6 and 7","why":"All in the middle of the valid range; boundary bugs pass every one."},{"text":"2 and 12","why":"They test the valid edges but not the invalid side of either boundary."},{"text":"0 and 100","why":"Far from both boundaries, so an off-by-one would not be noticed."},{"text":"1, 2, 12 and 13","why":"Correct. Just outside, and on, each boundary."}],"answer":3},{"q":"<p><code dir=\"ltr\">await expect(loadGroup('x', fetchMock)).rejects.toThrow()</code> passes both for the correct code and for a version that crashes with a <code dir=\"ltr\">TypeError</code> before it checks the status. How do you make the test meaningful?</p>","options":[{"text":"Use <code dir=\"ltr\">resolves</code> instead of <code dir=\"ltr\">rejects</code>.","why":"The promise should reject; switching to <code dir=\"ltr\">resolves</code> would make the correct code fail."},{"text":"Add more <code dir=\"ltr\">await</code> keywords around the call.","why":"The assertion is already awaited; what is missing is precision."},{"text":"Match the expected message, for example <code dir=\"ltr\">.rejects.toThrow('HTTP 404')</code>.","why":"Correct. Only the version that really checks the status produces that message."},{"text":"Replace <code dir=\"ltr\">toThrow</code> with <code dir=\"ltr\">toThrowError</code>.","why":"They are aliases with identical behaviour."}],"answer":2},{"q":"<p>Tests pass one by one but fail in the full run. An earlier test does <code dir=\"ltr\">vi.spyOn(Math, 'random').mockReturnValue(0)</code>. What is the fix?</p>","options":[{"text":"Restore mocks after each test, with <code dir=\"ltr\">vi.restoreAllMocks()</code> in <code dir=\"ltr\">afterEach</code> or <code dir=\"ltr\">restoreMocks: true</code> in the config.","why":"Correct. The spy replaced a method on a shared object and leaked into later tests."},{"text":"Run the test files one at a time.","why":"That hides the leak instead of removing it, and makes the suite slower."},{"text":"Raise Vitest’s test timeout.","why":"The failures are about leaked state, not time."},{"text":"Stop using <code dir=\"ltr\">Math.random</code> in production code.","why":"The production code is fine; the test forgot to clean up after itself."}],"answer":0},{"q":"<p>An icon-only button is rendered as <code dir=\"ltr\">&lt;button aria-label=&quot;Delete expense&quot;&gt;&lt;svg&gt;…&lt;/svg&gt;&lt;/button&gt;</code>. Which query should a Testing Library test use?</p>","options":[{"text":"<code dir=\"ltr\">screen.getByText('Delete expense')</code>","why":"There is no visible text; the name comes from <code dir=\"ltr\">aria-label</code>, which text queries do not read."},{"text":"<code dir=\"ltr\">screen.getByRole('button', { name: 'Delete expense' })</code>","why":"Correct. The accessible name comes from <code dir=\"ltr\">aria-label</code>, so the role query finds it the way a screen reader does."},{"text":"<code dir=\"ltr\">screen.getByTestId('delete')</code>","why":"There is a perfectly good accessible name; a test id would be a step down."},{"text":"<code dir=\"ltr\">container.querySelector('svg').closest('button')</code>","why":"It couples the test to the icon markup and ignores the accessible name."}],"answer":1},{"q":"<p>A “Saved” toast appears after saving and disappears a moment later. How do you assert that it disappears?</p>","options":[{"text":"<code dir=\"ltr\">expect(screen.getByText('Saved')).toBeNull()</code>","why":"<code dir=\"ltr\">getByText</code> throws while the toast is still there and never returns <code dir=\"ltr\">null</code>."},{"text":"<code dir=\"ltr\">expect(await screen.findByText('Saved')).toBeNull()</code>","why":"<code dir=\"ltr\">findByText</code> resolves with the element when it appears; it never resolves to <code dir=\"ltr\">null</code>."},{"text":"Sleep 5 seconds, then <code dir=\"ltr\">expect(screen.queryByText('Saved')).toBeNull()</code>","why":"A fixed sleep is slow and still races on a busy machine."},{"text":"<code dir=\"ltr\">await waitForElementToBeRemoved(() =&gt; screen.queryByText('Saved'))</code>","why":"Correct. It waits exactly until the toast is gone, or fails after the timeout."}],"answer":3},{"q":"<p>This Playwright check fails now and then in CI. What is the fix?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"ts\" data-index=\"6\"><div class=\"code-head\"><span class=\"code-title\">TypeScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> rows</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> page.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">getByRole</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'listitem'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">all</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">expect</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(rows.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">length</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toBe</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre></div>\n","options":[{"text":"Add <code dir=\"ltr\">await page.waitForLoadState('networkidle')</code> before it.","why":"Network idle is a poor proxy for the list being rendered, and the assertion still does not retry."},{"text":"Use <code dir=\"ltr\">await page.getByRole('listitem').count()</code> instead of <code dir=\"ltr\">.all()</code>.","why":"Still a one-time read followed by a non-retrying assertion."},{"text":"<code dir=\"ltr\">await expect(page.getByRole('listitem')).toHaveCount(3)</code>","why":"Correct. The web-first assertion re-counts until there are three items or the timeout passes."},{"text":"Set <code dir=\"ltr\">retries: 3</code> in the config.","why":"Retries hide the race instead of fixing it."}],"answer":2},{"q":"<p>End-to-end tests share one seeded group, and one of them deletes an expense from it. Other tests fail depending on the order. What is the best fix?</p>","options":[{"text":"A fixture that creates a uniquely named group per test through the API and deletes it afterwards.","why":"Correct. Each test owns its data, so order and parallelism stop mattering."},{"text":"Run the suite serially in a fixed order.","why":"It hides the dependency, slows the suite and lets one failure cascade."},{"text":"Reseed the database once before the whole suite.","why":"Tests still share and change the same data during the run."},{"text":"Enable three retries for those tests.","why":"Retries mask the collision without removing it."}],"answer":0},{"q":"<p>Which tool tells you that no test would notice if a <code dir=\"ltr\">&lt;</code> in your code became <code dir=\"ltr\">&lt;=</code>?</p>","options":[{"text":"Line coverage","why":"Line coverage only shows that the line ran."},{"text":"Branch coverage","why":"Branch coverage shows both sides of the condition ran, not that a test would fail if the boundary moved."},{"text":"The TypeScript compiler","why":"<code dir=\"ltr\">&lt;</code> and <code dir=\"ltr\">&lt;=</code> have the same types; the compiler cannot tell them apart."},{"text":"Mutation testing, for example StrykerJS","why":"Correct. It makes exactly that change and reports whether any test failed."}],"answer":3},{"q":"<p>Pull requests keep getting merged while the end-to-end job is red. What is the right fix?</p>","options":[{"text":"Rerun the job until it turns green, then merge.","why":"That trains people to ignore failures, including real ones."},{"text":"Make the test jobs required status checks on the main branch.","why":"Correct. A red required check blocks the merge, so the pipeline becomes a gate instead of a suggestion."},{"text":"Remove the end-to-end job to keep the pipeline green.","why":"That throws away the only check that the pieces work together."},{"text":"Run the end-to-end job only after merging.","why":"Then broken code reaches main before anyone learns about it."}],"answer":1},{"q":"<p>A component test mocks your own pure <code dir=\"ltr\">formatMoney</code> so it always returns <code dir=\"ltr\">'X'</code>. What is the best change?</p>","options":[{"text":"Keep the mock and add a snapshot of the output.","why":"The snapshot would record <code dir=\"ltr\">X</code> and still never test real formatting."},{"text":"Replace the mock with <code dir=\"ltr\">vi.mock</code> on the module.","why":"It is the same idea with a different tool; the real function still never runs."},{"text":"Use the real <code dir=\"ltr\">formatMoney</code>: it is fast, pure code you own, and the test then checks the real output.","why":"Correct. Mock at the boundaries; run your own pure logic for real."},{"text":"Delete the component test.","why":"The component still needs a test; it should simply stop faking the formatter."}],"answer":2}],"glossary":[{"id":"testing-trophy","term":"Testing trophy","definition":"A model of a test suite with four layers: static analysis, unit, integration and end-to-end tests. It is widest at integration, which usually gives the most confidence for its cost.","lesson":"l-tj-1-1"},{"id":"unit-test","term":"Unit test","definition":"A test that checks one function or module in isolation, usually in milliseconds. Ideal for pure logic with many edge cases, such as rounding money.","lesson":"l-tj-1-1"},{"id":"integration-test","term":"Integration test","definition":"A test that checks several units working together, such as a form component using the real parsing code with a faked network. It catches wiring bugs unit tests miss.","lesson":"l-tj-1-1"},{"id":"end-to-end-test","term":"End-to-end test","definition":"A test that drives the real application in a real browser, through the UI, API and data layer. The most realistic and the most expensive kind of test.","lesson":"l-tj-4-1"},{"id":"watch-mode","term":"Watch mode","definition":"Vitest’s default locally: after a run it waits, and when you save a file it reruns only the tests affected by that change, using Vite’s module graph.","lesson":"l-tj-1-2"},{"id":"aaa","term":"Arrange, Act, Assert","definition":"A three-part shape for tests: build the inputs, perform the one action under test, then state what should now be true. It makes tests easy to scan.","lesson":"l-tj-1-3"},{"id":"assertion","term":"Assertion","definition":"A check inside a test that throws when a value is not what you expected, such as <code dir=\"ltr\">expect(total).toBe(1000)</code>. A test fails when an assertion throws.","lesson":"l-tj-1-3"},{"id":"implementation-detail","term":"Implementation detail","definition":"How code produces its result (private helpers, internal state, call order), as opposed to the behaviour callers observe. Tests coupled to it break on harmless refactors.","lesson":"l-tj-2-1"},{"id":"invariant","term":"Invariant","definition":"A property that holds for every valid input, such as “the shares add up to the total”. Asserting an invariant across several inputs catches whole families of bugs.","lesson":"l-tj-2-1"},{"id":"boundary-value","term":"Boundary value analysis","definition":"Testing inputs just below, on and just above each limit in a rule, such as 0 and 0.01 or two and three decimals, because bugs cluster at those edges.","lesson":"l-tj-2-2"},{"id":"table-driven-test","term":"Table-driven test","definition":"A test written once and run for each row of a table of inputs and expected outputs. In Vitest, <code dir=\"ltr\">test.each</code> creates one named test per row.","lesson":"l-tj-2-2"},{"id":"stub","term":"Stub","definition":"A stand-in collaborator that returns canned answers so a test can control a dependency, for example an API object whose <code dir=\"ltr\">post</code> method always resolves with an id.","lesson":"l-tj-2-3"},{"id":"test-double","term":"Test double","definition":"Any stand-in for a real collaborator in a test: stubs return canned answers, spies record calls, mocks are spies you assert on, and fakes are simpler working versions.","lesson":"l-tj-2-4"},{"id":"spy","term":"Spy","definition":"A function or method wrapper that records how it was called. <code dir=\"ltr\">vi.spyOn(object, method)</code> watches a real method and can also override it until restored.","lesson":"l-tj-2-4"},{"id":"fake-timers","term":"Fake timers","definition":"A test clock that replaces <code dir=\"ltr\">setTimeout</code>, <code dir=\"ltr\">setInterval</code> and <code dir=\"ltr\">Date</code>, so tests can skip ahead with calls like <code dir=\"ltr\">vi.advanceTimersByTime(2000)</code> instead of waiting in real time.","lesson":"l-tj-2-4"},{"id":"accessible-name","term":"Accessible name","definition":"The name assistive technology announces for an element, computed from <code dir=\"ltr\">aria-labelledby</code>, <code dir=\"ltr\">aria-label</code>, an associated label or its text. Role queries match on it.","lesson":"l-tj-3-1"},{"id":"query-priority","term":"Query priority","definition":"Testing Library’s recommended order for finding elements: by role, label, placeholder, text or display value first; then alt text and title; test ids last.","lesson":"l-tj-3-1"},{"id":"user-event","term":"user-event","definition":"A Testing Library package that simulates whole user interactions, including pointer, focus and keyboard events, instead of dispatching single events like <code dir=\"ltr\">fireEvent</code> does.","lesson":"l-tj-3-2"},{"id":"findby","term":"findBy query","definition":"An asynchronous Testing Library query that retries until a matching element appears, resolving with it or rejecting after a timeout (one second by default).","lesson":"l-tj-3-3"},{"id":"msw","term":"MSW (Mock Service Worker)","definition":"A library that intercepts network requests in Node and the browser and answers them with handlers you define, so your real fetch and data code still run in tests.","lesson":"l-tj-3-4"},{"id":"locator","term":"Locator","definition":"In Playwright, a lazy, strict description of how to find an element, such as <code dir=\"ltr\">page.getByRole('button', { name: 'Save' })</code>. It is resolved again on every action.","lesson":"l-tj-4-1"},{"id":"web-first-assertion","term":"Web-first assertion","definition":"A Playwright assertion on a locator, such as <code dir=\"ltr\">toHaveText</code>, that re-checks the page until it passes or times out, instead of reading a value once.","lesson":"l-tj-4-1"},{"id":"fixture","term":"Fixture","definition":"In Playwright, a value a test requests by name, like <code dir=\"ltr\">page</code> or a custom <code dir=\"ltr\">group</code>, with setup before <code dir=\"ltr\">use()</code> and teardown after it, created only for tests that need it.","lesson":"l-tj-4-2"},{"id":"trace","term":"Trace","definition":"A Playwright recording of a test run with every action, DOM snapshots before and after it, console output and network calls, opened in the trace viewer.","lesson":"l-tj-4-3"},{"id":"flaky-test","term":"Flaky test","definition":"A test that sometimes passes and sometimes fails without any code change, usually because of timing, shared data or uncontrolled time. It teaches teams to ignore failures.","lesson":"l-tj-4-3"},{"id":"tdd","term":"Test-driven development","definition":"Writing one failing test, making it pass with the simplest code, then refactoring with the tests green, in short repeated cycles known as red, green, refactor.","lesson":"l-tj-5-1"},{"id":"code-coverage","term":"Code coverage","definition":"The share of statements, branches, functions and lines that ran during the tests. It shows untested code but not whether the executed code was actually checked.","lesson":"l-tj-5-2"},{"id":"mutation-testing","term":"Mutation testing","definition":"Grading a test suite by making small deliberate bugs (mutants) in the code and rerunning the tests; killed mutants were caught, survived mutants reveal weak tests.","lesson":"l-tj-5-3"},{"id":"equivalent-mutant","term":"Equivalent mutant","definition":"A mutant whose change does not alter any behaviour a caller could observe, so no reasonable test can kill it. It should be ignored, not chased.","lesson":"l-tj-5-3"},{"id":"required-check","term":"Required status check","definition":"A CI job that must pass before a pull request can merge into a protected branch, turning the test pipeline from a suggestion into a gate.","lesson":"l-tj-5-4"}]}