Wave 0 TDD stubs, Wave 1 parallel (registry + ReviewStep), Wave 2 act() fix. Covers TECH-01 through TECH-05. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.3 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 05-tech-debt | 03 | execute | 2 |
|
|
true |
|
|
Purpose: TECH-05 — clean test output with zero act() warnings makes CI signal trustworthy and prevents false positives. Output: Both test files use act()-safe patterns; no production code changes.
<execution_context> @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/phases/05-tech-debt/05-CONTEXT.md @.planning/phases/05-tech-debt/05-RESEARCH.md @.planning/phases/05-tech-debt/05-01-SUMMARY.md @.planning/phases/05-tech-debt/05-02-SUMMARY.mduserEvent v14 API (already installed: @testing-library/user-event 14.6.1):
import userEvent from '@testing-library/user-event';
// Per test or per describe block
const user = userEvent.setup();
// ...
await user.click(element); // replaces fireEvent.click(element)
await user.type(input, text); // replaces fireEvent.change(input, { target: { value: text } })
The user setup() instance must be created INSIDE describe() or it() (not at module level) to avoid state leakage between tests.
BackendSelectionStep.test.tsx — which fireEvent calls to migrate:
- fireEvent.change(nameInput, { target: { value: '...' } }) → await user.type(nameInput, '...') NOTE: user.type() appends to existing value. If input has a defaultValue, use user.clear() first or set up user.type with full value. Alternative: fireEvent.change is safe for setting values (no state updates) — only fireEvent.click triggers async state. Can keep fireEvent.change and only replace fireEvent.click.
- fireEvent.click(azureButton) → await user.click(azureButton)
- All it() callbacks that contain await user.click() MUST be async
vi.useFakeTimers pattern for ReviewStep (for OutputBlock's setCopied setTimeout):
import { beforeEach, afterEach, vi } from 'vitest';
beforeEach(() => {
vi.useFakeTimers();
// Note: existing beforeEach in ReviewStep.test.tsx runs vi.clearAllMocks() and sets up stubs
// Add vi.useFakeTimers() call to the EXISTING beforeEach block (not a new one)
});
afterEach(() => {
vi.useRealTimers();
});
Root cause reference:
- BackendSelectionStep: fireEvent.click on a backend card → handleSubmit → async React Hook Form validation → dispatch → WizardProvider state update — all without act() wrapping
- ReviewStep: fireEvent.click on Copy button → setCopied(true) → setTimeout(() => setCopied(false), 2000) — timer fires after test assertion, producing act() warning from OutputBlock
1. Remove fireEvent from the @testing-library/react import (keep render, screen, waitFor)
2. Add import: `import userEvent from '@testing-library/user-event';`
3. For each it() that calls fireEvent.click on a backend card button, convert to userEvent pattern:
- Add `const user = userEvent.setup();` at the top of the it() body
- Make the it() callback async
- Replace `fireEvent.click(azureButton)` with `await user.click(azureButton)`
- Replace `fireEvent.change(nameInput, { target: { value: 'my-remote' } })` with `await user.clear(nameInput); await user.type(nameInput, 'my-remote');`
OR keep fireEvent.change for input value setting and only replace fireEvent.click (fireEvent.change does not trigger act()-needing state updates in this component — either approach is valid)
Specific tests to migrate (all 5 tests in BackendSelectionStep.test.tsx that use fireEvent):
- WIZD-01: 'clicking a backend card dispatches SET_BACKEND_TYPE and SET_STEP'
- WIZD-04: 'shows inline error after first Next attempt with invalid name'
- WIZD-04: 'accepts alphanumeric, dashes, and underscores'
- WIZD-04: 'rejects names with spaces or special characters'
- WIZD-04: 'renders remote name input at the top of the step' (if it uses fireEvent — check)
Tests that only use screen queries and no interactions (e.g., 'renders Azure Blob Storage card') do NOT need changes.
Anti-patterns to avoid:
- Do NOT wrap fireEvent.click in act() manually — replace with userEvent instead
- Do NOT use legacy `userEvent.click()` shorthand — always use `userEvent.setup()` + `await user.click()`
- userEvent.setup() must be inside the test body, not at module level
After changes, verify no fireEvent.click calls remain in the file.
1. Add `afterEach` to the existing imports from vitest: `import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';`
2. Add `vi.useFakeTimers()` at the start of the existing `beforeEach` block (before vi.clearAllMocks()):
```typescript
beforeEach(() => {
vi.useFakeTimers(); // ADD THIS LINE FIRST
vi.clearAllMocks();
vi.mocked(downloadZip).mockResolvedValue(undefined);
vi.stubGlobal('navigator', { clipboard: { writeText: vi.fn().mockResolvedValue(undefined) } });
vi.stubGlobal('URL', { createObjectURL: vi.fn(() => 'blob:mock'), revokeObjectURL: vi.fn() });
});
```
3. Add a new `afterEach` block after beforeEach:
```typescript
afterEach(() => {
vi.useRealTimers();
});
```
This suppresses the act() warning from OutputBlock's `setTimeout(() => setCopied(false), 2000)` — the timer is frozen during tests and never fires unexpectedly.
No other changes needed — all existing assertions remain valid with fake timers active.
The new TECH-01 and TECH-02 tests from Plan 00 also benefit from this change (no warnings from their interactions either).
<success_criteria>
- BackendSelectionStep.test.tsx uses userEvent.setup() + await user.click() for all card interactions
- ReviewStep.test.tsx uses vi.useFakeTimers() in beforeEach and vi.useRealTimers() in afterEach
- Full Vitest run produces zero "act(" warning lines
- All 5 requirements (TECH-01 through TECH-05) verified green in the full test suite
- npx tsc --noEmit: zero errors (phase gate) </success_criteria>