Build TeaDetails, the default export: a panel that shows one tea from a small tea shop. It takes an id prop (default 1), fetches /api/teas/ followed by the id, like /api/teas/3, and gets JSON like { id: 3, name: "Oolong", notes: "Roasted, with a hint of honey." }.
Show Loading… (with the single … character) while it waits, then the name in a heading and the notes in a paragraph. If the request fails (fetch rejects, or the response isn't ok), show Couldn't load this tea.
People click through teas fast and close the panel before it loads, so cancel what's no longer needed. Pass an AbortController's signal to fetch, and abort the request when the panel leaves the page and when id changes. The tests look at signal.aborted on the requests you make.
An aborted request isn't a failure. It must not show the error, not show its tea, and not log anything. Only the tea for the current id ever shows.
<TeaDetails id={3} />
// Loading…, then:
<h2>Oolong</h2>
<p>Roasted, with a hint of honey.</p>
const fetch = mockFetch(() => ({ id: 3, name: "Oolong", notes: "Roasted, with a hint of honey." }));
render(<App id={3} />);
expect(await screen.findByRole("heading", { name: "Oolong" })).toBeInTheDocument();
expect(screen.getByText("Roasted, with a hint of honey.")).toBeInTheDocument();
expect(fetch.calls.map((call) => call.url)).toEqual(["/api/teas/3"]);
const answer = deferred();
mockFetch(() => answer.promise);
render(<App id={3} />);
expect(screen.getByText("Loading…")).toBeInTheDocument();
answer.resolve({ id: 3, name: "Oolong", notes: "Roasted, with a hint of honey." });
expect(await screen.findByText("Oolong")).toBeInTheDocument();
expect(screen.queryByText("Loading…")).not.toBeInTheDocument();
const fetch = mockFetch(() => deferred().promise);
render(<App id={3} />);
await flush();
const { signal } = fetch.calls[0].init;
expect(signal).toBeTruthy();
expect(signal.aborted).toBe(false);
const fetch = mockFetch(() => deferred().promise);
const { unmount } = render(<App id={3} />);
unmount();
expect(fetch.calls[0].init.signal.aborted).toBe(true);
const fetch = mockFetch(() => deferred().promise);
const { rerender } = render(<App id={3} />);
rerender(<App id={4} />);
expect(fetch.calls.map((call) => call.url)).toEqual(["/api/teas/3", "/api/teas/4"]);
expect(fetch.calls[0].init.signal.aborted).toBe(true);
expect(fetch.calls[1].init.signal.aborted).toBe(false);
const first = deferred();
mockFetch((url) => (url === "/api/teas/3" ? first.promise : deferred().promise));
const { rerender } = render(<App id={3} />);
rerender(<App id={4} />);
await flush();
first.resolve({ id: 3, name: "Oolong", notes: "Roasted." });
await flush();
expect(screen.queryByText("Couldn't load this tea.")).not.toBeInTheDocument();
expect(screen.queryByText("Oolong")).not.toBeInTheDocument();
expect(screen.getByText("Loading…")).toBeInTheDocument();
const first = deferred();
mockFetch((url) => (url === "/api/teas/3" ? first.promise : { id: 4, name: "Assam", notes: "Malty and strong." }));
const { rerender } = render(<App id={3} />);
rerender(<App id={4} />);
expect(await screen.findByText("Assam")).toBeInTheDocument();
first.resolve({ id: 3, name: "Oolong", notes: "Roasted." });
await flush();
expect(screen.queryByText("Oolong")).not.toBeInTheDocument();
expect(screen.queryByText("Couldn't load this tea.")).not.toBeInTheDocument();
expect(screen.getByText("Assam")).toBeInTheDocument();
const answer = deferred();
mockFetch(() => answer.promise);
const { unmount } = render(<App id={3} />);
await flush();
unmount();
answer.resolve({ id: 3, name: "Oolong", notes: "Roasted." });
await flush();
expect(logs).toEqual([]);
mockFetch(() => fail(500));
render(<App id={3} />);
expect(await screen.findByText("Couldn't load this tea.")).toBeInTheDocument();
expect(screen.queryByText("Loading…")).not.toBeInTheDocument();
Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.
Ctrl + Enter runs the tests (⌘ + Enter on a Mac).
I'm here to help with Abort on unmount. I give hints, never the solution, so you get to solve it yourself. Ask for a hint, select code to ask about it, or press Ask Tuto why on a failing test.
Your first 3 answers need no account. for free to keep asking.