Repository navigation
testing library block wizard patterns
title: "Test Patterns — Block Creation Wizard" type: pattern-guide domain: frontend framework: react testing: "vitest + @testing-library/react" component: "BlockWizardShell" tags: [testing-library, react-query, vitest, wizard, cache] created: 2026-10-07
Tests for the District Average block wizard cover dirty-tab navigation, reporting-unit-scoped React Query cache entries, and the current simulated create response.
Render BlockWizardShell with a panel that reads and changes the shared dirty
context. Use userEvent to mark the panel dirty and select another tab. Verify
that the confirmation dialog appears and the original tab stays selected until
the user chooses an action.
const user = userEvent.setup();
render(
<BlockWizardShell
panels={[
<DirtyStateControls key="details" />,
<p key="area">Area panel</p>,
]}
/>,
);
await user.click(await screen.findByRole("button", { name: "Mark dirty" }));
await user.click(screen.getByRole("tab", { name: "Area calculator" }));
expect(await screen.findByRole("dialog")).toBeTruthy();
await user.click(screen.getByRole("button", { name: "Stay on tab" }));
expect(
screen
.getByRole("tab", { name: "Block details" })
.getAttribute("aria-selected"),
).toBe("true");
expect(screen.getByText("Dirty")).toBeTruthy();Also cover discard: choose Discard changes, then assert that the destination panel appears and the dirty indicator clears. Try another tab after choosing Stay on tab; the dialog should appear again because the dirty state remains. A clean panel should switch tabs without a dialog.
Use accessible tab, dialog, and button roles. Check the selected tab through its
aria-selected value. The shell owns the guard. Child panels mark or clear the
shared state through useBlockWizardDirty and must render under
BlockWizardShell's provider.
Block-list query keys include the reporting-unit ID. Seed two keys, assert each returns its own value, and remove both entries afterward:
const firstKey = queryKeys.block.list(468);
const secondKey = queryKeys.block.list(469);
queryClient.setQueryData(firstKey, { page: { totalElements: 1 } });
queryClient.setQueryData(secondKey, { page: { totalElements: 3 } });
expect(queryClient.getQueryData(firstKey)).toEqual({
page: { totalElements: 1 },
});
expect(queryClient.getQueryData(secondKey)).toEqual({
page: { totalElements: 3 },
});
queryClient.removeQueries({ queryKey: firstKey });
queryClient.removeQueries({ queryKey: secondKey });The application exports a singleton queryClient. Remove entries created by a
test so state does not leak. For component tests that mount
QueryClientProvider, create a client per test when provider isolation matters;
disable retries in test defaults where appropriate.
ReportingUnitService.createBlock does not call a backend endpoint yet. It
waits 300 ms, then returns a fabricated draft resource with id: Date.now(),
version: 0, and timestamps from the current time. Its service test uses fake
timers, pins system time, advances the timer, and verifies that the HTTP request
mock was not called.
Treat this as a stub, not a backend integration test. When the create endpoint
ships, replace the stub assertions with request and response contract tests.
The service source marks the doRequest POST as the swap point.
The cancellation test attaches a rejection handler before calling cancel().
It then advances fake timers and awaits the derived promise, which resolves to
the captured CancelError object. The test also checks isCancelled:
const promise = service.createBlock(468, { blockType: "DISTRICT_AVERAGE" });
const settledPromise = promise.catch((error: unknown) => error);
promise.cancel();
await vi.advanceTimersByTimeAsync(300);
await expect(settledPromise).resolves.toMatchObject({ name: "CancelError" });
expect(promise.isCancelled).toBe(true);This records the tested assertion pattern only. The branch commit does not establish a deeper runtime or framework cause for the rejection-handling change.
This wiki documents NR Waste Plus for users and developers. Verify security and deployment guidance against the current application configuration before applying it elsewhere.
- Report an issue or ask a question in the GitHub repository.
- Follow the Contributing Guidelines to propose an update.
- Read the Code of Conduct.
Collection indexes support human browsing and machine retrieval. Keep both current when adding or moving wiki pages.
NR Waste Plus Wiki | GitHub Repository