docs(04-review-download-security): create phase 4 plan
5 plans across 5 waves: Wave 0 TDD stubs, utilities + OutputBlock, ReviewStep implementation, App.tsx wiring, human verify checkpoint. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
---
|
||||
phase: 04-review-download-security
|
||||
plan: 01
|
||||
type: tdd
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/components/wizard/ReviewStep.test.tsx
|
||||
- src/store/reducer.test.ts
|
||||
autonomous: true
|
||||
requirements: [CONF-02, CONF-03, DOWN-01, DOWN-02, DOWN-03, DOWN-04, DOWN-05, DOWN-06, SECU-01, SECU-02, SECU-03]
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "All ReviewStep requirement behaviors have named, failing test stubs (RED)"
|
||||
- "SECU-03 is verified by a spy on Storage.prototype.setItem in reducer.test.ts"
|
||||
- "Mock setup for URL.createObjectURL, URL.revokeObjectURL, navigator.clipboard.writeText exists in test file"
|
||||
- "npm test runs without crashes (passWithNoTests: true covers missing implementation)"
|
||||
artifacts:
|
||||
- path: "src/components/wizard/ReviewStep.test.tsx"
|
||||
provides: "RED test stubs for CONF-02, CONF-03, DOWN-01–DOWN-06, SECU-01, SECU-02"
|
||||
exports: []
|
||||
- path: "src/store/reducer.test.ts"
|
||||
provides: "SECU-03 assertion added to existing reducer test suite"
|
||||
contains: "spyOn(Storage.prototype, 'setItem')"
|
||||
key_links:
|
||||
- from: "src/components/wizard/ReviewStep.test.tsx"
|
||||
to: "src/components/wizard/ReviewStep"
|
||||
via: "import ReviewStep"
|
||||
pattern: "import.*ReviewStep"
|
||||
- from: "src/components/wizard/ReviewStep.test.tsx"
|
||||
to: "src/utils/downloadFile"
|
||||
via: "import downloadFile (mocked)"
|
||||
pattern: "vi.mock.*downloadFile"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Write Wave 0 TDD stubs: all failing test cases for Phase 4 requirements, plus SECU-03 assertion in existing reducer tests.
|
||||
|
||||
Purpose: Establish the RED baseline before any implementation. Tests define the contract that implementation plans must satisfy. Following the established project pattern from Phases 1–3.
|
||||
Output: ReviewStep.test.tsx (stub file, RED tests for all 11 req IDs) + SECU-03 assertion in reducer.test.ts.
|
||||
</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/phases/04-review-download-security/04-RESEARCH.md
|
||||
@.planning/phases/04-review-download-security/04-VALIDATION.md
|
||||
|
||||
@src/store/types.ts
|
||||
@src/store/context.tsx
|
||||
@src/store/reducer.test.ts
|
||||
@src/generators/index.ts
|
||||
</context>
|
||||
|
||||
<interfaces>
|
||||
<!-- Key contracts the executor needs — extracted from codebase. No exploration needed. -->
|
||||
|
||||
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')[];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
From src/store/context.tsx:
|
||||
```typescript
|
||||
export function useWizard(): { state: WizardState; dispatch: React.Dispatch<WizardAction> }
|
||||
export function WizardProvider({ children }: { children: React.ReactNode }): JSX.Element
|
||||
```
|
||||
|
||||
From src/generators/index.ts:
|
||||
```typescript
|
||||
export { buildRcloneConf } from './rclone-conf';
|
||||
export { buildIntuneInstall } from './intune-install';
|
||||
export { buildIntuneDetection } from './intune-detection';
|
||||
export { buildRmmScript } from './rmm-script';
|
||||
// All accept (state: WizardState): string
|
||||
// buildRcloneConf throws when backendType is null or name is empty
|
||||
```
|
||||
</interfaces>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Write ReviewStep.test.tsx with RED stubs for all Phase 4 requirements</name>
|
||||
<files>src/components/wizard/ReviewStep.test.tsx</files>
|
||||
<behavior>
|
||||
- CONF-02: rclone.conf preview text is present in rendered output when state has a valid backendType
|
||||
- CONF-03: clicking "Copy" on the rclone.conf block calls navigator.clipboard.writeText with the conf content
|
||||
- DOWN-01: clicking "Download rclone.conf" calls downloadFile with content and filename 'rclone.conf'
|
||||
- DOWN-02: clicking "Download Intune Install" calls downloadFile with content and filename 'intune-install.ps1'
|
||||
- DOWN-03: clicking "Download Intune Detection" calls downloadFile with content and filename 'intune-detection.ps1'
|
||||
- DOWN-04: clicking "Download RMM script" calls downloadFile with content and filename 'rmm-script.ps1'
|
||||
- DOWN-05: clicking "Download ZIP" calls downloadZip with all 4 file entries
|
||||
- DOWN-06: each output block has a copy button that calls clipboard.writeText with that block's content
|
||||
- SECU-01: download buttons are disabled when security checkbox is unchecked; enabled after checking
|
||||
- SECU-02: rendered output contains text about no data being sent to a server
|
||||
</behavior>
|
||||
<action>
|
||||
Create src/components/wizard/ReviewStep.test.tsx. Follow the established project pattern:
|
||||
- Use `expect.fail('not yet implemented')` for each stub (named RED failure, not import-error RED)
|
||||
- Import `{ describe, it, expect, vi, beforeEach }` from 'vitest'
|
||||
- Import `{ render, screen, fireEvent }` from '@testing-library/react'
|
||||
- Import `ReviewStep` from './ReviewStep' (will not exist yet — that is fine; tests will fail at import or at stub)
|
||||
- Import `* as downloadFileModule` from '../../utils/downloadFile' for mocking
|
||||
- Import `* as downloadZipModule` from '../../utils/downloadZip' for mocking
|
||||
|
||||
File-level mock setup (before describe block):
|
||||
```typescript
|
||||
vi.mock('../../utils/downloadFile', () => ({ downloadFile: vi.fn() }));
|
||||
vi.mock('../../utils/downloadZip', () => ({ downloadZip: vi.fn() }));
|
||||
```
|
||||
|
||||
In a beforeEach, stub global APIs:
|
||||
```typescript
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('navigator', {
|
||||
clipboard: { writeText: vi.fn().mockResolvedValue(undefined) },
|
||||
});
|
||||
vi.stubGlobal('URL', {
|
||||
createObjectURL: vi.fn(() => 'blob:mock'),
|
||||
revokeObjectURL: vi.fn(),
|
||||
});
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
```
|
||||
|
||||
Helper: create a WizardProvider wrapper with a fully populated state (azureblob backend, name='my-remote', params={account:'acct',key:'k'}, deployment defaults) for rendering ReviewStep in tests.
|
||||
|
||||
Write one `describe` block per requirement ID (embed ID in describe name for traceability, e.g. `describe('CONF-02: live rclone.conf preview', ...)`). Each describe contains an `it` that calls `expect.fail('not yet implemented')`.
|
||||
|
||||
Do NOT write real assertions yet — the whole file should be stubs that fail with 'not yet implemented' (except the import/mock wiring which must work).
|
||||
|
||||
Run: `npm test -- src/components/wizard/ReviewStep.test.tsx` — expect FAIL (RED) because ReviewStep does not exist yet. That is the correct Wave 0 state.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npm test -- src/components/wizard/ReviewStep.test.tsx 2>&1 | tail -20</automated>
|
||||
</verify>
|
||||
<done>ReviewStep.test.tsx exists with named stubs for all 10 requirement behaviors (CONF-02, CONF-03, DOWN-01 through DOWN-06, SECU-01, SECU-02). npm test fails with import or stub errors for ReviewStep only — NOT with TypeScript compilation errors in the test file itself.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Add SECU-03 assertion to reducer.test.ts</name>
|
||||
<files>src/store/reducer.test.ts</files>
|
||||
<behavior>
|
||||
- SECU-03: localStorage.setItem and sessionStorage.setItem are never called during any wizard reducer dispatch
|
||||
</behavior>
|
||||
<action>
|
||||
Edit src/store/reducer.test.ts. Add a new `it` block at the end of the existing `describe('wizardReducer', ...)` block:
|
||||
|
||||
```typescript
|
||||
it('SECU-03: never writes to localStorage or sessionStorage', () => {
|
||||
const setItemSpy = vi.spyOn(Storage.prototype, 'setItem');
|
||||
// Dispatch every action type to cover all reducer branches
|
||||
wizardReducer(INITIAL_STATE, { type: 'SET_STEP', payload: 1 });
|
||||
wizardReducer(INITIAL_STATE, { type: 'SET_BACKEND_TYPE', payload: 'azureblob' });
|
||||
wizardReducer(INITIAL_STATE, { type: 'SET_REMOTE_NAME', payload: 'test' });
|
||||
wizardReducer(INITIAL_STATE, { type: 'SET_REMOTE_PARAMS', payload: { key: 'val' } });
|
||||
wizardReducer(INITIAL_STATE, { type: 'SET_DEPLOYMENT', payload: { includeInstall: true } });
|
||||
wizardReducer(INITIAL_STATE, { type: 'RESET' });
|
||||
expect(setItemSpy).not.toHaveBeenCalled();
|
||||
setItemSpy.mockRestore();
|
||||
});
|
||||
```
|
||||
|
||||
Also add `vi` to the import line: change `import { describe, it, expect }` to `import { describe, it, expect, vi }`.
|
||||
|
||||
Run: `npm test -- src/store/reducer.test.ts` — expect GREEN (reducer already does not touch storage).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npm test -- src/store/reducer.test.ts 2>&1 | tail -10</automated>
|
||||
</verify>
|
||||
<done>reducer.test.ts has the SECU-03 test and it passes GREEN. All pre-existing reducer tests still pass.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
After both tasks: `npm test` runs without TypeScript errors in reviewed files. reducer.test.ts is fully GREEN. ReviewStep.test.tsx fails with stub or module-not-found errors (RED — expected).
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- src/components/wizard/ReviewStep.test.tsx exists with 10 named describe/it blocks (one per requirement behavior)
|
||||
- src/store/reducer.test.ts has SECU-03 assertion and passes fully GREEN
|
||||
- No new TypeScript compilation errors anywhere in the test files
|
||||
- Wave 0 baseline established: RED for ReviewStep, GREEN for SECU-03
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/04-review-download-security/04-01-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user