Skip to content

Commit 3babbee

Browse files
committed
docs: document Corvu mock prop forwarding and render helper side effects
1 parent e3fe145 commit 3babbee

2 files changed

Lines changed: 59 additions & 12 deletions

File tree

projects/birdhouse/docs/code-review/testing.md

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,60 @@ createEffect(() => {
3030

3131
---
3232

33-
## Coming Soon
34-
35-
This guide will cover:
36-
- Component testing patterns
37-
- Testing user interactions
38-
- Snapshot testing
39-
- Mock strategies
40-
41-
**For now, see:**
42-
- `src/components/ui/Button.test.tsx` - Example component test
43-
- `src/workspace-config/components/WorkspaceConfigDialog.test.tsx` - Resource error testing
44-
- Run: `bun run test` or `bun run test:watch`
33+
## Corvu Component Mocks Are Load-Bearing
34+
35+
When a Corvu primitive (Dialog, Popover, Drawer, etc.) is mocked in a test file, the mock's
36+
prop interface becomes part of the test infrastructure. If the real component adds a new event
37+
handler prop — `onKeyUp`, `onFocus`, `onPointerDown`, etc. — and the mock doesn't forward it,
38+
the handler silently disappears. The test won't fail with a clear error; it will just never fire.
39+
40+
**Symptom:** You add a new event handler to a Corvu component in production code. Tests that
41+
exercise that handler pass zero calls to the mock function, even though the handler works in
42+
the browser.
43+
44+
**Cause:** The mock `Dialog.Content` (or equivalent) only forwards the props it was originally
45+
written to accept. New props are ignored unless explicitly added.
46+
47+
**Fix:** Keep the mock's prop type in sync with what the component actually uses. When you add
48+
a handler to `Dialog.Content` in the component, add the same prop to the mock:
49+
50+
```tsx
51+
// In the test file's vi.mock("corvu/dialog", ...) block:
52+
Dialog.Content = (props: {
53+
children: JSX.Element;
54+
class?: string;
55+
onKeyDown?: (e: KeyboardEvent) => void;
56+
onKeyUp?: (e: KeyboardEvent) => void; // ← add when component uses it
57+
}) => (
58+
<div role="presentation" onKeyDown={props.onKeyDown} onKeyUp={props.onKeyUp}>
59+
{props.children}
60+
</div>
61+
);
62+
```
63+
64+
The mock in `AgentSearchDialog.test.tsx` is the canonical reference for this pattern.
65+
66+
---
67+
68+
## Test Helper Side Effects
69+
70+
Test files often define small helpers like `renderDialog()` that set up shared mutable state
71+
before rendering. If that helper resets state you've already configured, tests silently use the
72+
wrong setup.
73+
74+
**Symptom:** You set a shared variable (e.g. `mockModalStack`) before calling a render helper,
75+
but the test behaves as if your assignment never happened.
76+
77+
**Cause:** The render helper resets the variable as a side effect.
78+
79+
**Fix:** Document the side effects on the helper, and in tests that need non-default state,
80+
set the variable *after* calling the helper:
81+
82+
```ts
83+
// renderDialog resets mockModalStack as a side effect — set it after if needed
84+
renderDialog();
85+
mockModalStack = [{ type: "agent-search", id: "main" }, { type: "agent", id: "agent-1" }];
86+
await screen.findByLabelText("Search agent messages");
87+
```
88+
89+
See `AgentSearchDialog.test.tsx` for a working example of both patterns.

projects/birdhouse/frontend/src/components/AgentSearchDialog.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ const formatEpochTimestamp = () =>
158158
minute: "2-digit",
159159
});
160160

161+
// Resets mockModalStack as a side effect. If a test needs a different stack,
162+
// set mockModalStack after calling renderDialog(), not before.
161163
const renderDialog = (open = true) => {
162164
mockModalStack = open ? [{ type: "agent-search", id: "main" }] : [];
163165
render(() => <AgentSearchDialog />);

0 commit comments

Comments
 (0)