Files
Ready2Blob/.planning/phases/04-review-download-security/04-01-PLAN.md
T
kawaandClaude Sonnet 4.6 405bf477b4 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>
2026-03-27 10:23:14 +01:00

208 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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-01DOWN-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 13.
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>