- SUMMARY.md: userEvent v14 migration + vi.useFakeTimers() for ReviewStep - STATE.md: advanced to completed 05-03, added patterns as decisions - ROADMAP.md: phase 5 now 4/4 plans complete (Complete status) - REQUIREMENTS.md: TECH-05 marked complete
334 lines
11 KiB
Markdown
334 lines
11 KiB
Markdown
---
|
||
phase: 03-wizard-ui
|
||
plan: "01"
|
||
type: execute
|
||
wave: 1
|
||
depends_on: []
|
||
files_modified:
|
||
- vite.config.ts
|
||
- src/App.test.tsx
|
||
- src/components/wizard/BackendSelectionStep.test.tsx
|
||
- src/components/wizard/RemoteConfigStep.test.tsx
|
||
- src/components/wizard/StepIndicator.test.tsx
|
||
autonomous: true
|
||
requirements:
|
||
- WIZD-01
|
||
- WIZD-02
|
||
- WIZD-03
|
||
- WIZD-04
|
||
- BACK-01
|
||
- BACK-02
|
||
- BACK-03
|
||
|
||
must_haves:
|
||
truths:
|
||
- "Vitest runs React component tests without 'document is not defined' errors"
|
||
- "All four test stub files exist and fail RED with descriptive 'not yet implemented' messages"
|
||
- "npx vitest run exits non-zero (tests fail) confirming stubs are RED"
|
||
artifacts:
|
||
- path: "vite.config.ts"
|
||
provides: "jsdom test environment config"
|
||
contains: "test: { environment: 'jsdom'"
|
||
- path: "src/App.test.tsx"
|
||
provides: "Wave 0 stub for WIZD-02 step routing"
|
||
- path: "src/components/wizard/BackendSelectionStep.test.tsx"
|
||
provides: "Wave 0 stubs for WIZD-01, WIZD-04"
|
||
- path: "src/components/wizard/RemoteConfigStep.test.tsx"
|
||
provides: "Wave 0 stubs for BACK-01, BACK-02, BACK-03"
|
||
- path: "src/components/wizard/StepIndicator.test.tsx"
|
||
provides: "Wave 0 stubs for WIZD-03"
|
||
key_links:
|
||
- from: "vite.config.ts"
|
||
to: "src/**/*.test.tsx"
|
||
via: "test.environment: 'jsdom'"
|
||
pattern: "environment.*jsdom"
|
||
---
|
||
|
||
<objective>
|
||
Set up the Wave 0 test infrastructure: configure Vitest for jsdom component testing and create failing test stubs for all Phase 3 wizard components.
|
||
|
||
Purpose: Establish the RED baseline before implementation — tests must fail in a descriptive way so Plans 03–05 can drive to GREEN.
|
||
Output: Updated vite.config.ts and four test stub files.
|
||
</objective>
|
||
|
||
<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>
|
||
|
||
<context>
|
||
@.planning/PROJECT.md
|
||
@.planning/ROADMAP.md
|
||
@.planning/STATE.md
|
||
@.planning/phases/03-wizard-ui/03-CONTEXT.md
|
||
@.planning/phases/03-wizard-ui/03-RESEARCH.md
|
||
@.planning/phases/03-wizard-ui/03-VALIDATION.md
|
||
|
||
<interfaces>
|
||
<!-- Key types and contracts the executor needs. Extracted from codebase. -->
|
||
|
||
From src/store/types.ts:
|
||
```typescript
|
||
export interface WizardState {
|
||
currentStep: number;
|
||
remote: {
|
||
name: string;
|
||
backendType: BackendType | null;
|
||
params: Record<string, string>;
|
||
};
|
||
deployment: {
|
||
includeInstall: boolean;
|
||
configPath: 'machine-wide' | 'user-profile';
|
||
scriptTargets: ('intune' | 'rmm')[];
|
||
};
|
||
}
|
||
|
||
export type WizardAction =
|
||
| { type: 'SET_STEP'; payload: number }
|
||
| { type: 'SET_BACKEND_TYPE'; payload: BackendType }
|
||
| { type: 'SET_REMOTE_NAME'; payload: string }
|
||
| { type: 'SET_REMOTE_PARAMS'; payload: Record<string, string> }
|
||
| { type: 'SET_DEPLOYMENT'; payload: Partial<WizardState['deployment']> }
|
||
| { type: 'RESET' };
|
||
```
|
||
|
||
From src/schemas/registry.ts:
|
||
```typescript
|
||
export type BackendType = 'azureblob' | 's3' | 's3-compatible';
|
||
```
|
||
|
||
From src/store/context.tsx:
|
||
```typescript
|
||
export function useWizard(): WizardContextValue;
|
||
export function WizardProvider({ children }: { children: React.ReactNode }): JSX.Element;
|
||
```
|
||
</interfaces>
|
||
</context>
|
||
|
||
<tasks>
|
||
|
||
<task type="auto">
|
||
<name>Task 1: Configure Vitest jsdom environment in vite.config.ts</name>
|
||
<files>vite.config.ts</files>
|
||
<action>
|
||
Update vite.config.ts to add a `test` block inside `defineConfig`. The existing config has only `plugins: [react(), tailwindcss()]`. Add:
|
||
|
||
```typescript
|
||
import { defineConfig } from 'vite';
|
||
import react from '@vitejs/plugin-react';
|
||
import tailwindcss from '@tailwindcss/vite';
|
||
|
||
export default defineConfig({
|
||
plugins: [react(), tailwindcss()],
|
||
test: {
|
||
environment: 'jsdom',
|
||
passWithNoTests: true,
|
||
},
|
||
});
|
||
```
|
||
|
||
Do NOT add `/// <reference types="vitest" />` — this is not needed with vitest ^4.x when using `test` config inside `defineConfig`. The `passWithNoTests: true` is already established project convention (see STATE.md decision from Phase 01-01).
|
||
</action>
|
||
<verify>
|
||
<automated>npx vitest run --reporter=verbose 2>&1 | head -20</automated>
|
||
</verify>
|
||
<done>Vitest starts without "document is not defined" errors. If no test files exist yet the suite exits 0 due to passWithNoTests: true.</done>
|
||
</task>
|
||
|
||
<task type="auto">
|
||
<name>Task 2: Create Wave 0 test stubs (RED baseline for all 7 requirements)</name>
|
||
<files>
|
||
src/App.test.tsx,
|
||
src/components/wizard/BackendSelectionStep.test.tsx,
|
||
src/components/wizard/RemoteConfigStep.test.tsx,
|
||
src/components/wizard/StepIndicator.test.tsx
|
||
</files>
|
||
<action>
|
||
Create the `src/components/wizard/` directory (it does not exist yet). Create four test stub files. Each stub imports the component it will test and throws a todo error — this gives RED tests with clear names rather than import errors.
|
||
|
||
NOTE: The component files do NOT exist yet. Import them anyway. Vitest will fail at import with "Cannot find module" — this is the correct RED state.
|
||
|
||
**src/App.test.tsx**
|
||
```typescript
|
||
// @vitest-environment jsdom
|
||
// Covers WIZD-02: App renders the correct step component for currentStep 0, 1, 2
|
||
import { describe, it, expect } from 'vitest';
|
||
|
||
describe('App — step routing', () => {
|
||
it('renders BackendSelectionStep when currentStep is 0', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('renders RemoteConfigStep when currentStep is 1', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('renders DeploymentStep when currentStep is 2', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
});
|
||
```
|
||
|
||
**src/components/wizard/BackendSelectionStep.test.tsx**
|
||
```typescript
|
||
// @vitest-environment jsdom
|
||
// Covers WIZD-01: card grid renders Azure Blob, Amazon S3, S3-Compatible (Azure first)
|
||
// Covers WIZD-04: remote name field validates alphanumeric/dash/underscore
|
||
import { describe, it, expect } from 'vitest';
|
||
|
||
describe('BackendSelectionStep', () => {
|
||
describe('WIZD-01: backend card grid', () => {
|
||
it('renders Azure Blob Storage card', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('renders Amazon S3 card', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('renders S3-Compatible card', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('Azure Blob is listed before S3 in DOM order', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('clicking a backend card dispatches SET_BACKEND_TYPE and SET_STEP', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
});
|
||
|
||
describe('WIZD-04: remote name field', () => {
|
||
it('renders remote name input at the top of the step', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('shows no error before first Next attempt', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('shows inline error after first Next attempt with invalid name', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('accepts alphanumeric, dashes, and underscores', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('rejects names with spaces or special characters', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
});
|
||
});
|
||
```
|
||
|
||
**src/components/wizard/RemoteConfigStep.test.tsx**
|
||
```typescript
|
||
// @vitest-environment jsdom
|
||
// Covers BACK-01: Azure Blob config form — account + SAS/Key toggle, both values preserved
|
||
// Covers BACK-02: Amazon S3 config form — access_key_id, secret_access_key, region
|
||
// Covers BACK-03: S3-Compatible config form — same as S3 plus endpoint field
|
||
import { describe, it, expect } from 'vitest';
|
||
|
||
describe('RemoteConfigStep', () => {
|
||
describe('BACK-01: Azure Blob form', () => {
|
||
it('renders Storage Account Name field', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('shows SAS URL field by default (default auth method)', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('switching auth toggle to Access Key shows key field and hides SAS URL', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('switching auth toggle does not clear the hidden field value', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
});
|
||
|
||
describe('BACK-02: Amazon S3 form', () => {
|
||
it('renders access_key_id field', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('renders secret_access_key field', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('renders region field', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
});
|
||
|
||
describe('BACK-03: S3-Compatible form', () => {
|
||
it('renders endpoint field in addition to S3 fields', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
});
|
||
});
|
||
```
|
||
|
||
**src/components/wizard/StepIndicator.test.tsx**
|
||
```typescript
|
||
// @vitest-environment jsdom
|
||
// Covers WIZD-03: going back preserves remote.params; deployment options are untouched
|
||
import { describe, it, expect } from 'vitest';
|
||
|
||
describe('StepIndicator', () => {
|
||
describe('WIZD-03: back navigation preserves state', () => {
|
||
it('clicking a completed step dispatches SET_STEP', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('clicking back to step 0 dispatches SET_REMOTE_PARAMS({}) to clear params', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('clicking back to step 0 does NOT dispatch RESET (deployment preserved)', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('step 0 shows as active when currentStep is 0', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
|
||
it('completed steps are clickable', () => {
|
||
expect.fail('not yet implemented');
|
||
});
|
||
});
|
||
});
|
||
```
|
||
</action>
|
||
<verify>
|
||
<automated>npx vitest run --reporter=verbose 2>&1 | tail -30</automated>
|
||
</verify>
|
||
<done>
|
||
All four test files exist. Vitest runs and reports failures (RED). The failures are either "Cannot find module" (acceptable — component files not yet created) or "not yet implemented" (from expect.fail). No passing tests exist yet. vite.config.ts has jsdom environment.
|
||
</done>
|
||
</task>
|
||
|
||
</tasks>
|
||
|
||
<verification>
|
||
Run after both tasks complete:
|
||
```bash
|
||
npx vitest run --reporter=verbose 2>&1
|
||
```
|
||
Expected: Vitest runs (no "document is not defined"), tests fail RED with module-not-found or "not yet implemented" errors, suite reports failures. The key outcome is that jsdom works and tests are scaffolded.
|
||
</verification>
|
||
|
||
<success_criteria>
|
||
- vite.config.ts has `test: { environment: 'jsdom', passWithNoTests: true }`
|
||
- Four test files exist under src/ with descriptive failing test names
|
||
- `npx vitest run` runs without crashing the vitest process itself (failures are expected)
|
||
- Test names directly trace to requirements (WIZD-01, WIZD-02, WIZD-03, WIZD-04, BACK-01, BACK-02, BACK-03)
|
||
</success_criteria>
|
||
|
||
<output>
|
||
After completion, create `.planning/phases/03-wizard-ui/03-01-SUMMARY.md`
|
||
</output>
|