Build OrderStatus, the default export: the "Where's my tea?" panel for one order. It takes an orderId prop (default 1), and asks /api/order/ followed by the id, like /api/order/5, right away. The answer is JSON like { status: "shipped" }, where the status is preparing, shipped, or delivered.
Show Loading… (with the single … character) until the first answer, then Status: shipped. Under it, show Last updated: followed by the time the latest answer arrived: read it with Date.now() and show it with new Date(time).toLocaleTimeString().
Ask again 2 seconds after each answer, until the status is delivered; then stop for good. Never ask again while a request is still waiting for its answer, however slow it is.
If a check fails (fetch rejects, or the response isn't ok), show Couldn't check the order., keep the last status on screen, and try again 2 seconds later. The next good answer hides the message.
When the panel leaves the page, stop: no timer left behind and no more requests. If orderId changes, start over for the new order.
<OrderStatus orderId={5} />
<p>Status: shipped</p>
<p>Last updated: 10:42:07</p>
useFakeTimers();
const fetch = mockFetch(() => ({ status: "preparing" }));
render(<App orderId={5} />);
expect(screen.getByText("Loading…")).toBeInTheDocument();
await flush();
expect(screen.getByText("Status: preparing")).toBeInTheDocument();
expect(fetch.calls.map((call) => call.url)).toEqual(["/api/order/5"]);
const clock = useFakeTimers();
const statuses = ["preparing", "preparing", "shipped"];
const fetch = mockFetch(() => ({ status: statuses[Math.min(fetch.calls.length, statuses.length) - 1] }));
render(<App orderId={5} />);
await flush();
await clock.advance(1999);
expect(fetch.calls).toHaveLength(1);
await clock.advance(1);
expect(fetch.calls).toHaveLength(2);
await clock.advance(2000);
expect(fetch.calls).toHaveLength(3);
expect(screen.getByText("Status: shipped")).toBeInTheDocument();
expect(fetch.calls.every((call) => call.url === "/api/order/5")).toBe(true);
const clock = useFakeTimers();
const fetch = mockFetch(() => ({ status: fetch.calls.length === 1 ? "shipped" : "delivered" }));
render(<App orderId={5} />);
await flush();
await clock.advance(2000);
expect(screen.getByText("Status: delivered")).toBeInTheDocument();
await clock.advance(10000);
expect(fetch.calls).toHaveLength(2);
expect(clock.pending()).toBe(0);
const clock = useFakeTimers();
mockFetch(() => ({ status: "shipped" }));
render(<App orderId={5} />);
await flush();
expect(screen.getByText("Last updated: " + new Date(clock.now()).toLocaleTimeString())).toBeInTheDocument();
await clock.advance(2000);
expect(screen.getByText("Last updated: " + new Date(clock.now()).toLocaleTimeString())).toBeInTheDocument();
const clock = useFakeTimers();
const slow = deferred();
const fetch = mockFetch(() => (fetch.calls.length === 1 ? slow.promise : { status: "shipped" }));
render(<App orderId={5} />);
await clock.advance(5000);
expect(fetch.calls).toHaveLength(1);
slow.resolve({ status: "preparing" });
await flush();
expect(screen.getByText("Status: preparing")).toBeInTheDocument();
await clock.advance(1999);
expect(fetch.calls).toHaveLength(1);
await clock.advance(1);
expect(fetch.calls).toHaveLength(2);
const clock = useFakeTimers();
const fetch = mockFetch(() => {
const call = fetch.calls.length;
if (call === 2) return fail(503);
return { status: call === 1 ? "preparing" : "shipped" };
});
render(<App orderId={5} />);
await flush();
await clock.advance(2000);
expect(screen.getByText("Couldn't check the order.")).toBeInTheDocument();
expect(screen.getByText("Status: preparing")).toBeInTheDocument();
await clock.advance(2000);
expect(fetch.calls).toHaveLength(3);
expect(screen.getByText("Status: shipped")).toBeInTheDocument();
expect(screen.queryByText("Couldn't check the order.")).not.toBeInTheDocument();
const clock = useFakeTimers();
const second = deferred();
const fetch = mockFetch(() => (fetch.calls.length === 2 ? second.promise : { status: "preparing" }));
const { unmount } = render(<App orderId={5} />);
await flush();
await clock.advance(2000);
expect(fetch.calls).toHaveLength(2);
unmount();
second.resolve({ status: "shipped" });
await flush();
expect(clock.pending()).toBe(0);
await clock.advance(10000);
expect(fetch.calls).toHaveLength(2);
expect(logs).toEqual([]);
const clock = useFakeTimers();
const fetch = mockFetch((url) => ({ status: url === "/api/order/5" ? "preparing" : "shipped" }));
const { rerender } = render(<App orderId={5} />);
await flush();
await clock.advance(1000);
rerender(<App orderId={6} />);
await flush();
expect(screen.getByText("Status: shipped")).toBeInTheDocument();
await clock.advance(2000);
expect(fetch.calls.map((call) => call.url)).toEqual(["/api/order/5", "/api/order/6", "/api/order/6"]);
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 Polling order status. 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.