Public Access
docs: Sprint 14 — Vitest for useOnboarding (Q4) across all 6 running docs
Sprint 14 (commit28f3212) adds Vitest + happy-dom + @testing-library/react + @testing-library/jest-dom as devDeps and locks the useOnboarding state-transition contract with 7 unit tests in 25 ms. Lifts the 'no new npm deps' rule for testing-only. Runtime bundle unchanged (503.82 kB). No pre-existing WIP files touched. Sprint 9's bug1562929(onComplete wired to .reset, the inverse op) shipped a post-deploy fix the same day. Sprint 14 prevents recurrence at npm test time. This commit updates the 6 running docs that track sprints: - .agent/plan.md — header changed to 'Active sprint: Sprint 14'; Sprint 14 section (S14.1-S14.4 + Done when + Out of scope) added after the Sprint 13 section. - .agent/context.md — Sprint 14 decisions (D1-D7), open Q1, and file:line references added. - Review/sprint14-verification.md — NEW: deploy + 7-case test table + verification commands + Case 7 S9-bug-catch proof + 5-risk table + open Q1 (component-level tests). - Review/ui-nielsen-audit.md — Sprint 14 status block (T7.1-T7.3) added after the Sprint 13 block. - fix-ui-audit.md — Sprint 14 section (T7.1-T7.5) added after the Sprint 13 section. - Review/handoff-ui-audit.md — Batch J line in deploy list, Sprint 14 section after Sprint 13, TL;DR Sprint 14 line, Last-updated footer updated. (Did NOT replace the Sprint 7/8/TL;DR/environment-quirks/active-risks content that was at the bottom of the file — that material is preserved unchanged.) - docs/HANDOFF.md — Sprint 14 section added after Sprint 13, Last-updated footer updated. All 6 docs now reflect Sprint 14. §Future backlog remaining: F9-full (local Ollama model pull on the host). Q4 (Vitest) is now closed. Sprint 14 is the smallest sprint in the cycle (devDeps + 1 test file + 2 config files + 2 scripts) and the first that adds a test infrastructure layer.
This commit is contained in:
@@ -469,3 +469,35 @@ The Sprint 11 "Generate Meal Plan" CTA was library-only. Sprint 13 splits it int
|
|||||||
- `frontend/src/pages/Dashboard.tsx:775-870` — `renderPromptModal` (the Sprint 13 modal)
|
- `frontend/src/pages/Dashboard.tsx:775-870` — `renderPromptModal` (the Sprint 13 modal)
|
||||||
- `Review/sprint13-verification.md` — new file (deploy + 3-step browser smoke + 4 API curls + a11y check + 6-risk table)
|
- `Review/sprint13-verification.md` — new file (deploy + 3-step browser smoke + 4 API curls + a11y check + 6-risk table)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sprint 14 — Vitest for `useOnboarding` (Q4)
|
||||||
|
|
||||||
|
### Decisions
|
||||||
|
|
||||||
|
- **D1 — Lift "no new npm deps" for testing-only.** Vitest + happy-dom + @testing-library/react + @testing-library/jest-dom go under devDependencies. Runtime bundle size unchanged.
|
||||||
|
- **D2 — `happy-dom` over `jsdom`.** Lighter (7x smaller), faster startup, sufficient for hooks-only tests. No need for full DOM emulation in Sprint 14.
|
||||||
|
- **D3 — Test only the hook, not the `<OnboardingTour/>` component.** The S9 bug class is at the hook/callback wiring level. Component tests (focus, arrow keys, dialog a11y) are a different scope and a future sprint.
|
||||||
|
- **D4 — `renderHook` from `@testing-library/react`** (not a custom harness). The hook is plain React, no router or query client dependencies, so no `wrapper` option is needed.
|
||||||
|
- **D5 — `npm test` runs `vitest run` (no watch).** CI-friendly. `npm run test:watch` for local dev.
|
||||||
|
- **D6 — `markComplete` test locks the S9 bug class.** A future refactor that wires `onComplete → reset` (the original bug) would set state to false but leave localStorage at '1'; a future refactor that wires `onReset → markComplete` (the inverse) would set state to true without clearing localStorage. Case 3 (`markComplete` does NOT clear localStorage) + Case 4 (`reset` clears localStorage AND flips state) lock both directions.
|
||||||
|
- **D7 — No backend tests this sprint.** Venv on `docker-willester` is broken (Nix symlinks to `/run/current-system/sw/bin/python`). Backend pytest skipped. Frontend-only sprint.
|
||||||
|
|
||||||
|
### Open questions
|
||||||
|
|
||||||
|
- Q1 — Cover the `<OnboardingTour/>` component itself (focus, arrow keys, dialog a11y) in a future sprint? Default: yes, future sprint. Adds @testing-library/user-event for keyboard simulation.
|
||||||
|
|
||||||
|
### Sprint 14 file:line references
|
||||||
|
|
||||||
|
- `frontend/src/components/OnboardingTour.tsx:78-93` — `readComplete` / `writeComplete` / `clearComplete` helpers (the localStorage I/O seam)
|
||||||
|
- `frontend/src/components/OnboardingTour.tsx:103-133` — `useOnboarding` hook (returns `{reset, show, markComplete, isComplete}`)
|
||||||
|
- `frontend/src/components/OnboardingTour.tsx:109` — initial `useState<boolean>(readComplete)` (the localStorage → state bridge)
|
||||||
|
- `frontend/src/components/OnboardingTour.tsx:113-116` — `reset` callback: clears localStorage + flips state to false
|
||||||
|
- `frontend/src/components/OnboardingTour.tsx:118-121` — `show` callback: identical to `reset` (intentional mirror)
|
||||||
|
- `frontend/src/components/OnboardingTour.tsx:128-130` — `markComplete` callback: flips state to true only (the inverse op from `reset`)
|
||||||
|
- `frontend/src/App.tsx:104-117` — wires `onComplete → markComplete` and `onReset → reset` (must not be inverted)
|
||||||
|
- `frontend/vitest.config.ts` (NEW) — happy-dom env + setup file
|
||||||
|
- `frontend/vitest-setup.ts` (NEW) — @testing-library/jest-dom matchers
|
||||||
|
- `frontend/src/components/OnboardingTour.test.tsx` (NEW) — 6 cases (S14.3)
|
||||||
|
- `frontend/package.json` (MODIFIED) — devDeps + scripts
|
||||||
|
|
||||||
|
|||||||
+55
-3
@@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
Goal: bring implementation back into alignment with `Review/reviewconcensus.md`. Stop building forward features until the deferred-risk spikes and the verification matrix pass.
|
Goal: bring implementation back into alignment with `Review/reviewconcensus.md`. Stop building forward features until the deferred-risk spikes and the verification matrix pass.
|
||||||
|
|
||||||
## Active sprint: Sprint 8 — "Deny" semantics (C + Z, hard-filter escalation)
|
## Active sprint: Sprint 14 — Vitest for `useOnboarding` (Q4)
|
||||||
|
|
||||||
**Owner:** this agent. **Status:** code complete (`npm run build` green, 21/21 planner tests pass excluding 1 pre-existing unrelated failure), awaiting user commit + deploy. **Tracking:** `Review/sprint8-verification.md` (deploy + smoke), `.agent/plan.md` (checklist), `.agent/context.md` (decisions + open Qs).
|
**Owner:** this agent. **Status:** starting. **Tracking:** `Review/sprint14-verification.md` (deploy + smoke), `.agent/plan.md` (checklist), `.agent/context.md` (decisions + open Qs).
|
||||||
|
|
||||||
**User policy decision (2026-06-05, exact):** "Hard filter. If it is denied this week twice, it should be considered denied for good." — collapses the design to **C + Z** with a server-side 2-denial auto-escalation.
|
**Why now (Q4 rationale):** Sprint 9 (F1 Onboarding Tour) shipped a hand-rolled ~420-line component, then immediately regressed in `1562929`: `onComplete` was wired to `useOnboarding().reset()` (inverse op) so the X/Skip/Esc dismiss path actually re-showed the tour. The fix split the API into `onComplete`/`onReset` callbacks with `markComplete` (dismiss) vs `reset` (re-show). The bug class is "two inverse operations share one state setter"; the only durable prevention is unit tests. ~1 hr sprint, locks the seam.
|
||||||
|
|
||||||
|
**Sprint 14 lifts the "no new npm deps" rule for testing-only.** Runtime deps unchanged.
|
||||||
|
|
||||||
### S8.1 — Migration: `0016_denial_decay_and_scope.py` (NEW)
|
### S8.1 — Migration: `0016_denial_decay_and_scope.py` (NEW)
|
||||||
|
|
||||||
@@ -501,3 +503,53 @@ User reported post-deploy: "The tour window looks great, but Clicking the X nor
|
|||||||
- **Prompt engineering / quality iteration.** The prompt is a first cut. If the LLM returns 0 picks or 21 identical recipes, the operator can iterate on the prompt. Out of scope for the initial ship.
|
- **Prompt engineering / quality iteration.** The prompt is a first cut. If the LLM returns 0 picks or 21 identical recipes, the operator can iterate on the prompt. Out of scope for the initial ship.
|
||||||
- **Multi-week plans.** One week at a time.
|
- **Multi-week plans.** One week at a time.
|
||||||
- **Save the prompt as a template** for reuse. Future sprint.
|
- **Save the prompt as a template** for reuse. Future sprint.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Active sprint: Sprint 14 — Vitest for `useOnboarding` (Q4)
|
||||||
|
|
||||||
|
### S14.1 — Dependencies (devDeps only)
|
||||||
|
|
||||||
|
- [ ] Add `vitest@^1.6.0` — the runner.
|
||||||
|
- [ ] Add `happy-dom@^14.7.0` — DOM env (lighter than jsdom, faster startup; 7x smaller).
|
||||||
|
- [ ] Add `@testing-library/react@^14.2.0` — render + assert helper.
|
||||||
|
- [ ] Add `@testing-library/jest-dom@^6.4.0` — `.toBeInTheDocument()` etc.
|
||||||
|
- [ ] All four go under `devDependencies`. Lifts the "no new npm deps" rule for testing-only.
|
||||||
|
|
||||||
|
### S14.2 — Config
|
||||||
|
|
||||||
|
- [ ] `frontend/vitest.config.ts` — uses Vite's plugin-react (already a devDep), sets `environment: 'happy-dom'`, points `setupFiles: ['./vitest-setup.ts']`, reuses `tsconfig.json` paths.
|
||||||
|
- [ ] `frontend/vitest-setup.ts` — imports `@testing-library/jest-dom/vitest` (auto-extends `expect` with DOM matchers).
|
||||||
|
- [ ] `package.json` scripts: add `"test": "vitest run --reporter=default"` (no watch by default — CI-friendly) + `"test:watch": "vitest"`.
|
||||||
|
|
||||||
|
### S14.3 — Tests for `useOnboarding` (the S9 bug class)
|
||||||
|
|
||||||
|
- [ ] `frontend/src/components/OnboardingTour.test.tsx` — render the hook via a tiny `<TestHarness/>` consumer; `renderHook` from `@testing-library/react`.
|
||||||
|
- [ ] **Case 1 — clean init:** clear localStorage; `isComplete === false`.
|
||||||
|
- [ ] **Case 2 — persisted init:** `localStorage.setItem('mealplanner:onboarding-complete', '1')`; `isComplete === true` on mount.
|
||||||
|
- [ ] **Case 3 — `markComplete` sets state, does NOT clear localStorage:** after `markComplete()`, `isComplete === true` and the localStorage key is still `'1'`. **This is the S9 bug lock.**
|
||||||
|
- [ ] **Case 4 — `reset` clears localStorage AND flips state to false:** after `reset()`, `isComplete === false` and the localStorage key is removed.
|
||||||
|
- [ ] **Case 5 — `show` is a mirror of `reset`:** after `show()`, same assertions as Case 4.
|
||||||
|
- [ ] **Case 6 — localStorage throw is silently swallowed:** stub `localStorage.getItem` to throw; hook still returns `isComplete: false`, does not crash.
|
||||||
|
|
||||||
|
### S14.4 — Verify + commit
|
||||||
|
|
||||||
|
- [ ] `cd frontend && npm test` runs all 6 cases green.
|
||||||
|
- [ ] `npm run build` still green (vitest's types shouldn't conflict with vite's).
|
||||||
|
- [ ] `Review/sprint14-verification.md` written.
|
||||||
|
- [ ] All 6 running docs updated with the Sprint 14 status block.
|
||||||
|
- [ ] Commit on host + push.
|
||||||
|
|
||||||
|
### Done when (Sprint 14)
|
||||||
|
|
||||||
|
- All boxes above ticked.
|
||||||
|
- `npm test` shows 6 passing in <5 s.
|
||||||
|
- `npm run build` still green.
|
||||||
|
- The S9 bug class is locked: any future regression that wires `onComplete → reset` (or `onReset → markComplete`) trips Case 3.
|
||||||
|
|
||||||
|
### Out of scope (Sprint 14)
|
||||||
|
|
||||||
|
- **Component-level tests for `<OnboardingTour/>` itself** (the dialog, focus management, arrow-key navigation). The hook covers the S9 bug class; component tests are a different scope. Future sprint.
|
||||||
|
- **Tests for `recipes` API client or `useOnboarding` callers.** Not the S9 bug class. Future sprint.
|
||||||
|
- **Tests for the backend.** The venv on `docker-willester` is broken; running pytest locally requires Nix fixes. Out of scope.
|
||||||
|
- **F9-full (local Ollama model pull).** Opt-in based on cloud-billing feedback only. `_ask_llm` is the single seam — F9-full only needs to swap the URL + model name.
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ If you are a new agent continuing this work, do this **in order**:
|
|||||||
- **Batch G:** Sprint 11 (one `git pull`, `docker compose up -d --build frontend` — frontend-only, no migration, no backend rebuild).
|
- **Batch G:** Sprint 11 (one `git pull`, `docker compose up -d --build frontend` — frontend-only, no migration, no backend rebuild).
|
||||||
- **Batch H:** Sprint 12 (one `git pull`, `docker compose up -d --build backend frontend` — backend has the new `recipe_search.py` router, frontend has the new toggle).
|
- **Batch H:** Sprint 12 (one `git pull`, `docker compose up -d --build backend frontend` — backend has the new `recipe_search.py` router, frontend has the new toggle).
|
||||||
- **Batch I:** Sprint 13 (one `git pull`, `docker compose up -d --build backend frontend` — backend has the new `llm_plan.py` router, frontend has the new prompt modal).
|
- **Batch I:** Sprint 13 (one `git pull`, `docker compose up -d --build backend frontend` — backend has the new `llm_plan.py` router, frontend has the new prompt modal).
|
||||||
4. **Open issues** in `.agent/plan.md` (the "Phase R1-R3" section is a prior plan; the **Sprint 13 active-sprint** section is the current state) and in `.agent/context.md` (decisions + open Qs for the current sprint).
|
- **Batch J:** Sprint 14 (one `git pull`, `npm install` in `frontend/`, `npm test` to confirm 7/7, then `docker compose up -d --build frontend` — no backend change, no migration; the 5 new devDeps land on `npm install`).
|
||||||
|
4. **Open issues** in `.agent/plan.md` (the "Phase R1-R3" section is a prior plan; the **Sprint 14 active-sprint** section is the current state) and in `.agent/context.md` (decisions + open Qs for the current sprint).
|
||||||
5. **Do not** touch the pre-existing WIP files: `backend/app/api/recipes.py`, `backend/app/schemas/recipe.py`, `nginx/nginx.conf` (untouched since before this work; user's to manage).
|
5. **Do not** touch the pre-existing WIP files: `backend/app/api/recipes.py`, `backend/app/schemas/recipe.py`, `nginx/nginx.conf` (untouched since before this work; user's to manage).
|
||||||
6. **When you commit,** use the `fix(ui):`, `feat(ui):`, `refactor(frontend):`, `docs(review):` Conventional Commit style. Force-add new files in `frontend/src/lib/` (the `.gitignore` line 17 `lib/` is a pre-existing bug that catches it).
|
6. **When you commit,** use the `fix(ui):`, `feat(ui):`, `refactor(frontend):`, `docs(review):` Conventional Commit style. Force-add new files in `frontend/src/lib/` (the `.gitignore` line 17 `lib/` is a pre-existing bug that catches it).
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ If you are a new agent continuing this work, do this **in order**:
|
|||||||
|
|
||||||
- Sprints 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8: code committed and build green. Sprint 1 deployed. Sprints 2-8 awaiting user deploy.
|
- Sprints 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8: code committed and build green. Sprint 1 deployed. Sprints 2-8 awaiting user deploy.
|
||||||
- The §Future backlog is now empty: F1 (onboarding) shipped as Sprint 9; the dead "Generate Meal Plan" CTA shipped as Sprint 11; F8 (Spoonacular) shipped as Sprint 12; F9-lite shipped as Sprint 13. All four are deployment-pending. F9-full (local Ollama model pull) is the only remaining §Future item — opt-in based on cloud-billing feedback.
|
- The §Future backlog is now empty: F1 (onboarding) shipped as Sprint 9; the dead "Generate Meal Plan" CTA shipped as Sprint 11; F8 (Spoonacular) shipped as Sprint 12; F9-lite shipped as Sprint 13. All four are deployment-pending. F9-full (local Ollama model pull) is the only remaining §Future item — opt-in based on cloud-billing feedback.
|
||||||
|
- **Sprint 14 (Vitest for `useOnboarding`, Q4) code complete on 2026-06-05.** 7/7 tests green in ~25 ms, `npm run build` green, no runtime dep change. Awaiting commit + push. Closes the open Q4 from Sprint 9.
|
||||||
- Pre-existing repo issues: 1 failing test (`test_filter_blocks_by_cost` — verified pre-Sprint 8), `.gitignore` `lib/` bug, no CI. Documented.
|
- Pre-existing repo issues: 1 failing test (`test_filter_blocks_by_cost` — verified pre-Sprint 8), `.gitignore` `lib/` bug, no CI. Documented.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -131,6 +133,18 @@ If you are a new agent continuing this work, do this **in order**:
|
|||||||
|
|
||||||
**Tracking docs:** `Review/sprint13-verification.md` (deploy + 3-step browser smoke + 4 API curls + a11y check + 6-risk table), `Review/ui-nielsen-audit.md` Sprint 13 status block, `fix-ui-audit.md` T7.1–T7.5, this file, `docs/HANDOFF.md` Sprint 13 section.
|
**Tracking docs:** `Review/sprint13-verification.md` (deploy + 3-step browser smoke + 4 API curls + a11y check + 6-risk table), `Review/ui-nielsen-audit.md` Sprint 13 status block, `fix-ui-audit.md` T7.1–T7.5, this file, `docs/HANDOFF.md` Sprint 13 section.
|
||||||
|
|
||||||
|
### Sprint 14 — Vitest for `useOnboarding` (Q4) (user-driven)
|
||||||
|
|
||||||
|
**Status: CODE COMPLETE on 2026-06-05. 7/7 tests green in ~25 ms. `npm run build` green. Awaiting user commit + push.** No backend change, no migration, no runtime dep change (5 new devDeps only).
|
||||||
|
|
||||||
|
**Root cause (one-liner):** Sprint 9 (F1 Onboarding Tour) shipped a hand-rolled ~420-line component; the bug `1562929` shipped a post-deploy fix the same day (`onComplete` was wired to `useOnboarding().reset()` — the inverse op). Q4 (open question from Sprint 9) was "add Vitest to lock `useOnboarding` state transitions." Sprint 14 lifts the "no new npm deps" rule for testing-only and locks the bug class at `npm test` time.
|
||||||
|
|
||||||
|
**Scope (5 boxes):** 4 new devDeps (`vitest@^1.6.0`, `happy-dom@^14.7.0`, `@testing-library/react@^14.2.0`, `@testing-library/jest-dom@^6.4.0`) + 1 tsc dep (`@types/node@^20`) + 2 new scripts (`test`, `test:watch`) + 2 new config files (`vitest.config.ts`, `vitest-setup.ts`) + 1 new test file (`OnboardingTour.test.tsx`, 7 cases). **No backend change. No migration. No runtime dep change. No pre-existing WIP files touched.**
|
||||||
|
|
||||||
|
**Case 7 is the load-bearing test:** Sprint 9's bug was at the App.tsx call site, not inside `useOnboarding`. Cases 1-6 lock the hook contract; Case 7 is a static check on `App.tsx` that uses `node:fs/promises` to read the source as a string, runs two regex matches to capture the arrow bodies of `onComplete={...}` and `onReset={...}`, and asserts each body calls the right `onboarding.*` method. Verified: flipping `markComplete` → `reset` in `App.tsx` makes Case 7 fail.
|
||||||
|
|
||||||
|
**Tracking docs:** `Review/sprint14-verification.md` (deploy + test commands + 5-risk table + open Q1), `Review/ui-nielsen-audit.md` Sprint 14 status block, `fix-ui-audit.md` T7.1–T7.5, this file, `docs/HANDOFF.md` Sprint 14 section.
|
||||||
|
|
||||||
### Sprint 7 — Fix webui "empty meal plan" (date-semantics mismatch)
|
### Sprint 7 — Fix webui "empty meal plan" (date-semantics mismatch)
|
||||||
|
|
||||||
**Status: COMMITTED `09c7525` on 2026-06-05. Build green.** Awaiting user to `git pull` + run the SQL fix + rebuild.
|
**Status: COMMITTED `09c7525` on 2026-06-05. Build green.** Awaiting user to `git pull` + run the SQL fix + rebuild.
|
||||||
@@ -408,4 +422,4 @@ cd frontend && npm run build
|
|||||||
|
|
||||||
Trust the build output. Trust the smoke checklist. Don't trust the deployment host's UI until the user confirms. The verification model is "I shipped, you verified, you reported, I fixed" — the agent in this role never sees the live UI directly.
|
Trust the build output. Trust the smoke checklist. Don't trust the deployment host's UI until the user confirms. The verification model is "I shipped, you verified, you reported, I fixed" — the agent in this role never sees the live UI directly.
|
||||||
|
|
||||||
**Last updated: 2026-06-05** — Sprint 1 deployed; Sprints 2-6 awaiting user deploy; **Sprint 7 (`09c7525`), Sprint 8 (`efd1fc6`), Sprint 9 (F1 Onboarding Tour) + post-deploy fix (`1562929`), Sprint 10 (Deny Forever on Recipes), Sprint 11 (Wire the dead "Generate Meal Plan" CTA), Sprint 12 (F8 Spoonacular search), and Sprint 13 (F9-lite Ollama Cloud plan synthesis) committed on 2026-06-05, awaiting user deploy**. See the "How to take over" and "Pending user deploy" sections at the top of this file.
|
**Last updated: 2026-06-05** — Sprint 1 deployed; Sprints 2-6 awaiting user deploy; **Sprint 7 (`09c7525`), Sprint 8 (`efd1fc6`), Sprint 9 (F1 Onboarding Tour) + post-deploy fix (`1562929`), Sprint 10 (Deny Forever on Recipes), Sprint 11 (Wire the dead "Generate Meal Plan" CTA), Sprint 12 (F8 Spoonacular search), and Sprint 13 (F9-lite Ollama Cloud plan synthesis) committed on 2026-06-05, awaiting user deploy; Sprint 14 (Vitest for `useOnboarding`) code complete, 7/7 tests green, awaiting commit + push**. See the "How to take over" and "Pending user deploy" sections at the top of this file.
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# Sprint 14 Verification — Vitest for `useOnboarding` (Q4)
|
||||||
|
|
||||||
|
**Date:** 2026-06-05. **Owner:** this agent. **Status:** code complete, 7/7 tests green, `npm run build` green, awaiting commit + push.
|
||||||
|
|
||||||
|
## What Sprint 14 does
|
||||||
|
|
||||||
|
Adds Vitest + @testing-library/react + happy-dom to the frontend devDeps and locks the `useOnboarding` state-transition contract with 7 unit tests. The S9 bug class (wiring `onComplete` to a call that flips `isComplete` to `false`, e.g. `onboarding.reset()`) is now caught at `npm test` time. Sprint 9 (F1 Onboarding Tour) shipped a hand-rolled ~420-line component; the bug `1562929` shipped a post-deploy fix the same day. Sprint 14 prevents recurrence.
|
||||||
|
|
||||||
|
## Why this sprint, why this scope
|
||||||
|
|
||||||
|
- **Q4** (open question from Sprint 9): "Add Vitest to lock `useOnboarding` state transitions." Cost: ~1 hr. Value: prevents the S9 bug class. Decision: lift the "no new npm deps" rule for **testing-only** (devDeps).
|
||||||
|
- **Hook-only, not component-level.** The S9 bug was at the App.tsx call-site. Sprint 14 includes one integration test (Case 7) that statically reads `App.tsx` and asserts the wiring. Component-level tests (focus, arrow keys, dialog a11y) are a future sprint.
|
||||||
|
|
||||||
|
## Files added
|
||||||
|
|
||||||
|
- `frontend/vitest.config.ts` — happy-dom env, setup file, `src/**/*.test.{ts,tsx}` glob.
|
||||||
|
- `frontend/vitest-setup.ts` — `@testing-library/jest-dom/vitest` matchers.
|
||||||
|
- `frontend/src/components/OnboardingTour.test.tsx` — 7 cases.
|
||||||
|
|
||||||
|
## Files modified
|
||||||
|
|
||||||
|
- `frontend/package.json` — 4 new devDeps (`vitest`, `happy-dom`, `@testing-library/react`, `@testing-library/jest-dom`) + 1 devDep for tsc (`@types/node`) + 2 scripts (`test`, `test:watch`).
|
||||||
|
- `.agent/plan.md` — S14 sections (S14.1-S14.4 + Done when + Out of scope).
|
||||||
|
- `.agent/context.md` — S14 decisions (D1-D7), open question (Q1), file:line references.
|
||||||
|
|
||||||
|
## Test cases (S14.3)
|
||||||
|
|
||||||
|
| # | Case | What it locks |
|
||||||
|
|---|------|---------------|
|
||||||
|
| 1 | clean init | `isComplete === false` when localStorage is empty |
|
||||||
|
| 2 | persisted init | `isComplete === true` when `localStorage.getItem(KEY) === '1'` |
|
||||||
|
| 3 | `markComplete` | state → true, localStorage **stays** at `'1'` (locks one direction of the S9 bug) |
|
||||||
|
| 4 | `reset` | localStorage cleared, state → false |
|
||||||
|
| 5 | `show` | identical to `reset` (intentional mirror) |
|
||||||
|
| 6 | localStorage throw on read | silently swallowed, `isComplete === false`, no crash |
|
||||||
|
| 7 | App.tsx wiring | static check: `onComplete` calls `markComplete`, `onReset` calls `reset`; neither inverts (locks the original S9 bug at the call site) |
|
||||||
|
|
||||||
|
## Verification commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh docker-willester
|
||||||
|
cd /home/peter/MealPlanner/frontend
|
||||||
|
|
||||||
|
# 1. install (already done on host; rerun if pulling to a fresh checkout)
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 2. run tests
|
||||||
|
npm test
|
||||||
|
# → 7 passed in ~25 ms
|
||||||
|
|
||||||
|
# 3. build (still green; vitest is devDeps only)
|
||||||
|
npm run build
|
||||||
|
# → tsc 0 errors, vite built in ~2.6 s, bundle 503.82 kB unchanged
|
||||||
|
|
||||||
|
# 4. prove Case 7 catches the bug (manual)
|
||||||
|
sed -i 's|onboarding.markComplete()|onboarding.reset()|' src/App.tsx
|
||||||
|
npm test
|
||||||
|
# → 1 failed (Case 7: onCompleteBody does not match /markComplete/)
|
||||||
|
sed -i 's|onboarding.reset()|onboarding.markComplete()|' src/App.tsx # restore
|
||||||
|
npm test
|
||||||
|
# → 7 passed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
No backend change. No migration. No new runtime dep. The 5 new devDeps land on `npm install` on the host.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh docker-willester
|
||||||
|
cd /home/peter/MealPlanner
|
||||||
|
git pull
|
||||||
|
cd frontend && npm install && npm test # confirm 7/7 in CI
|
||||||
|
cd .. && docker compose up -d --build frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
If the user wants Vitest in CI (currently not wired), that is a follow-up — outside Sprint 14 scope.
|
||||||
|
|
||||||
|
## What Sprint 14 does NOT do
|
||||||
|
|
||||||
|
- **No component-level tests for `<OnboardingTour/>`.** Focus, arrow keys, dialog a11y, portal positioning. Future sprint. Would add `@testing-library/user-event` for keyboard simulation.
|
||||||
|
- **No backend tests.** Venv on `docker-willester` is broken (Nix symlinks to `/run/current-system/sw/bin/python`). Frontend-only sprint.
|
||||||
|
- **No CI integration.** Tests are local-only. Wiring them into a CI runner is a follow-up.
|
||||||
|
|
||||||
|
## Risk table
|
||||||
|
|
||||||
|
| Risk | Mitigation | Status |
|
||||||
|
|------|------------|--------|
|
||||||
|
| Vitest conflicts with Vite 5 config | vitest.config.ts uses `defineConfig` from `vitest/config` which extends Vite's; tested, no conflict | Resolved |
|
||||||
|
| `node:*` imports in tests break tsc | Added `@types/node` as devDep | Resolved |
|
||||||
|
| Tests slow CI | 7 cases run in 25 ms; not a concern | N/A |
|
||||||
|
| Devs forget to run `npm test` before commit | Follow-up: pre-commit hook or CI; not in Sprint 14 | Open |
|
||||||
|
| Future refactor adds a hook dependency that breaks Case 7's regex | Case 7 uses a single-shot regex match; if `App.tsx` gains a `markComplete` call in unrelated code (e.g. button), the regex still works because the `onComplete` arrow body is the captured group | Resolved (verified) |
|
||||||
|
|
||||||
|
## Open question for follow-up
|
||||||
|
|
||||||
|
**Q1 — Should Sprint 15 add component-level tests for `<OnboardingTour/>` (focus, arrow keys, dialog a11y)?** Default: yes, future sprint. Adds `@testing-library/user-event`. ~1.5 hr.
|
||||||
@@ -139,6 +139,13 @@ The app looks polished on the surface (Tailwind palette, clean cards, working to
|
|||||||
> - **Verification log:** `Review/sprint13-verification.md` (deploy + 3-step browser smoke + 4 API curls + a11y check + 6-risk table). Deploy is `git pull` + `docker compose up -d --build backend frontend` (no migration, no new dependencies).
|
> - **Verification log:** `Review/sprint13-verification.md` (deploy + 3-step browser smoke + 4 API curls + a11y check + 6-risk table). Deploy is `git pull` + `docker compose up -d --build backend frontend` (no migration, no new dependencies).
|
||||||
> - **No new dependencies. No migration. Admin path unchanged.**
|
> - **No new dependencies. No migration. Admin path unchanged.**
|
||||||
>
|
>
|
||||||
|
> **Sprint 14 status (in progress 2026-06-05, code complete, awaiting commit + push):** Vitest for `useOnboarding` (Q4) — locks the S9 bug class with 7 unit tests. Sprint 9 (F1 Onboarding Tour) shipped a hand-rolled ~420-line component; the bug `1562929` shipped a post-deploy fix the same day. Sprint 14 prevents recurrence at `npm test` time.
|
||||||
|
> - **T7.1** 4 new devDeps: `vitest@^1.6.0`, `happy-dom@^14.7.0`, `@testing-library/react@^14.2.0`, `@testing-library/jest-dom@^6.4.0` + `@types/node@^20` (tsc). Lifts the "no new npm deps" rule for testing-only. Runtime bundle unchanged.
|
||||||
|
> - **T7.2** `frontend/vitest.config.ts` (NEW) — happy-dom env, setup file, `src/**/*.test.{ts,tsx}` glob. `frontend/vitest-setup.ts` (NEW) — `@testing-library/jest-dom/vitest` matchers. `package.json` scripts: `test` (vitest run, no watch) + `test:watch`.
|
||||||
|
> - **T7.3** `frontend/src/components/OnboardingTour.test.tsx` (NEW) — 7 cases: clean init, persisted init, `markComplete` (state → true, localStorage **stays** at `'1'`), `reset` (localStorage cleared + state → false), `show` mirror, localStorage throw silently swallowed, App.tsx wiring static check (catches the original S9 bug `onComplete → reset` at the call site). 7/7 pass in ~25 ms.
|
||||||
|
> - **Verification log:** `Review/sprint14-verification.md`. `npm run build` still green (bundle 503.82 kB unchanged). No migration. No backend change. Deploy is `git pull` + `npm install` (frontend) + `docker compose up -d --build frontend`.
|
||||||
|
> - **No new runtime dependencies. No migration. Admin path unchanged. Component-level tests for `<OnboardingTour/>` (focus, arrow keys, dialog a11y) deferred to a future sprint.**
|
||||||
|
>
|
||||||
> **Sprint 6 status (commit `8ad4ef6`, awaiting deploy):** Two §Future items, both with design decisions captured in the commit message.
|
> **Sprint 6 status (commit `8ad4ef6`, awaiting deploy):** Two §Future items, both with design decisions captured in the commit message.
|
||||||
> - **F3** Bulk 'add checked to pantry' on ShoppingList. Backend `POST /api/pantry/bulk` accepts `{items: HomePantryCreate[]}` and returns per-item status (`added` / `updated` / `skipped`) with totals. Per-item failure model: unknown ingredient → `skipped` with reason, not a 4xx. Frontend ShoppingList gains a primary `Add N to pantry` button next to the existing Reset button; toast reports `added X, updated Y, skipped Z`; only the items that actually landed are removed from the checked Set. **Scope decision:** ShoppingList only (the checked Set was the natural substrate; Pantry would need new multi-select UI).
|
> - **F3** Bulk 'add checked to pantry' on ShoppingList. Backend `POST /api/pantry/bulk` accepts `{items: HomePantryCreate[]}` and returns per-item status (`added` / `updated` / `skipped`) with totals. Per-item failure model: unknown ingredient → `skipped` with reason, not a 4xx. Frontend ShoppingList gains a primary `Add N to pantry` button next to the existing Reset button; toast reports `added X, updated Y, skipped Z`; only the items that actually landed are removed from the checked Set. **Scope decision:** ShoppingList only (the checked Set was the natural substrate; Pantry would need new multi-select UI).
|
||||||
> - **F4** Plan the whole week on Dashboard. Backend `POST /api/meals/{id}/fill-empty-slots` with body `{meal_types: [str, ...]}` returns `FillEmptySlotsResult { filled: [{day, meal_type, item}], failed: [{day, meal_type, reason}] }`. Iterates day 1..7 in order; skips already-occupied slots; picks a recipe (prefer un-used, fall back to any) and inserts as `pending`. Per-slot failure model — never aborts mid-batch. Frontend Dashboard gets a primary `Plan the week` button (next to the Sprint 5 week-nav control) with a dropdown: `Dinners only` / `All meals`. Toast reports partial-success precisely: `Planned 12 of 21 meal slots — 9 failed (e.g. <reason>)`.
|
> - **F4** Plan the whole week on Dashboard. Backend `POST /api/meals/{id}/fill-empty-slots` with body `{meal_types: [str, ...]}` returns `FillEmptySlotsResult { filled: [{day, meal_type, item}], failed: [{day, meal_type, reason}] }`. Iterates day 1..7 in order; skips already-occupied slots; picks a recipe (prefer un-used, fall back to any) and inserts as `pending`. Per-slot failure model — never aborts mid-batch. Frontend Dashboard gets a primary `Plan the week` button (next to the Sprint 5 week-nav control) with a dropdown: `Dinners only` / `All meals`. Toast reports partial-success precisely: `Planned 12 of 21 meal slots — 9 failed (e.g. <reason>)`.
|
||||||
|
|||||||
+29
-1
@@ -302,7 +302,7 @@ Trust the tests. Trust the live runs. Don't trust prose claims that something is
|
|||||||
**Current open proposals:**
|
**Current open proposals:**
|
||||||
- `docs/proposals/2026-05-23-feedback-driven-recipe-discovery.md` — pending user approval. No code yet (per the 2026-05-23 section below).
|
- `docs/proposals/2026-05-23-feedback-driven-recipe-discovery.md` — pending user approval. No code yet (per the 2026-05-23 section below).
|
||||||
|
|
||||||
**Last updated: 2026-06-05** — UI/UX audit & fix cycle (Sprints 1, 2, 3, 4, 5, 6, 7, 8, 9) complete. 20 findings closed (5 P0 + 6 P1 + 3 P2 + 6 §Future), code committed across 13 commits, build green. Sprint 1 deployed; Sprints 2-9 awaiting deploy. **Sprint 7 (`09c7525`, awaiting user deploy)** aligns "this week" to the upcoming Monday. **Sprint 8 (`efd1fc6`, awaiting user deploy)** implements the user's "Deny" semantics decision. **Sprint 9 (committed 2026-06-05, awaiting user deploy)** ships the F1 Onboarding Tour. **Sprint 10 (committed 2026-06-05, awaiting user deploy)** ships the "Deny Forever" on Recipes. **Sprint 11 (committed 2026-06-05, awaiting user deploy)** wires the dead "Generate Meal Plan" CTA. **Sprint 12 (committed 2026-06-05, awaiting user deploy)** ships the F8 Spoonacular search. **Sprint 13 (committed 2026-06-05, awaiting user deploy)** ships the F9-lite Ollama Cloud plan synthesis. See Sprint 7 + Sprint 8 + Sprint 9 + Sprint 10 + Sprint 11 + Sprint 12 + Sprint 13 sections below. Full UI-audit handoff at `Review/handoff-ui-audit.md`.
|
**Last updated: 2026-06-05** — UI/UX audit & fix cycle (Sprints 1, 2, 3, 4, 5, 6, 7, 8, 9) complete. 20 findings closed (5 P0 + 6 P1 + 3 P2 + 6 §Future), code committed across 13 commits, build green. Sprint 1 deployed; Sprints 2-9 awaiting deploy. **Sprint 7 (`09c7525`, awaiting user deploy)** aligns "this week" to the upcoming Monday. **Sprint 8 (`efd1fc6`, awaiting user deploy)** implements the user's "Deny" semantics decision. **Sprint 9 (committed 2026-06-05, awaiting user deploy)** ships the F1 Onboarding Tour. **Sprint 10 (committed 2026-06-05, awaiting user deploy)** ships the "Deny Forever" on Recipes. **Sprint 11 (committed 2026-06-05, awaiting user deploy)** wires the dead "Generate Meal Plan" CTA. **Sprint 12 (committed 2026-06-05, awaiting user deploy)** ships the F8 Spoonacular search. **Sprint 13 (committed 2026-06-05, awaiting user deploy)** ships the F9-lite Ollama Cloud plan synthesis. **Sprint 14 (code complete 2026-06-05, awaiting commit + push)** ships Vitest for `useOnboarding` (Q4) — 7/7 tests green. See Sprint 7 + Sprint 8 + Sprint 9 + Sprint 10 + Sprint 11 + Sprint 12 + Sprint 13 + Sprint 14 sections below. Full UI-audit handoff at `Review/handoff-ui-audit.md`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -428,6 +428,34 @@ Trust the tests. Trust the live runs. Don't trust prose claims that something is
|
|||||||
|
|
||||||
**Path forward to F9-full:** the `_ask_llm` helper is the single seam. F9-full only needs to swap the URL (`https://ollama.com/v1` → `http://localhost:11434`) and model name (`kimi-k2.6:cloud` → local). The endpoint code, prompt, and validation stay unchanged.
|
**Path forward to F9-full:** the `_ask_llm` helper is the single seam. F9-full only needs to swap the URL (`https://ollama.com/v1` → `http://localhost:11434`) and model name (`kimi-k2.6:cloud` → local). The endpoint code, prompt, and validation stay unchanged.
|
||||||
|
|
||||||
|
### Sprint 14 — Vitest for `useOnboarding` (Q4) (user-driven) — CODE COMPLETE 2026-06-05
|
||||||
|
|
||||||
|
**User direction (2026-06-05):** "Sprint 14: Vitest (Q4)." Q4 (open question from Sprint 9) was "add Vitest to lock `useOnboarding` state transitions." Sprint 9's bug `1562929` shipped a post-deploy fix the same day. Sprint 14 lifts the "no new npm deps" rule for testing-only and locks the bug class at `npm test` time.
|
||||||
|
|
||||||
|
**Scope (5 boxes):**
|
||||||
|
1. **4 new devDeps** — `vitest@^1.6.0`, `happy-dom@^14.7.0`, `@testing-library/react@^14.2.0`, `@testing-library/jest-dom@^6.4.0` (runtime bundle unchanged) + 1 tsc dep `@types/node@^20` (needed for `node:fs/promises` in Case 7).
|
||||||
|
2. **2 new config files** — `frontend/vitest.config.ts` (happy-dom env, setup file, `src/**/*.test.{ts,tsx}` glob) + `frontend/vitest-setup.ts` (loads `@testing-library/jest-dom/vitest`).
|
||||||
|
3. **2 new scripts** — `npm test` (`vitest run --reporter=default`, no watch, CI-friendly) + `npm test:watch` (`vitest`).
|
||||||
|
4. **1 new test file** — `frontend/src/components/OnboardingTour.test.tsx` (7 cases). Case 7 is the load-bearing test: a static check on `App.tsx` source that catches the original S9 bug `onComplete → reset` at the call site. Verified by inverting the wiring and watching Case 7 fail.
|
||||||
|
5. **§Future backlog status after Sprint 14:** Q4 (Vitest) ✓. F9-full (local Ollama model pull) is the only remaining item — opt-in based on cloud-billing feedback.
|
||||||
|
|
||||||
|
**Test coverage (7 cases):**
|
||||||
|
1. Clean init — `isComplete === false` when localStorage is empty.
|
||||||
|
2. Persisted init — `isComplete === true` when `localStorage.getItem(KEY) === '1'`.
|
||||||
|
3. `markComplete` — state → true, localStorage **stays** at `'1'`.
|
||||||
|
4. `reset` — localStorage cleared, state → false.
|
||||||
|
5. `show` — mirror of `reset` (intentional).
|
||||||
|
6. localStorage throw on read — silently swallowed, `isComplete === false`, no crash.
|
||||||
|
7. App.tsx wiring — `onComplete` calls `markComplete`, `onReset` calls `reset`; neither inverts.
|
||||||
|
|
||||||
|
**Build + tests:** `npm test` — 7/7 cases pass in ~25 ms. `npm run build` — tsc 0 errors, vite built in ~2.6 s, bundle 503.82 kB unchanged. No backend change. No migration. No runtime dep change.
|
||||||
|
|
||||||
|
**Deploy:** `git pull` + `cd frontend && npm install && npm test` (confirm 7/7) + `cd .. && docker compose up -d --build frontend`. No backend rebuild. Verification: `Review/sprint14-verification.md` (deploy + test commands + 5-risk table + open Q1).
|
||||||
|
|
||||||
|
**No regression expected:** Sprint 14 is devDeps + new test files only. No existing source files modified. Pre-existing WIP (`backend/app/api/recipes.py`, `backend/app/schemas/recipe.py`, `nginx/nginx.conf`) untouched.
|
||||||
|
|
||||||
|
**Open question:** Q1 — should Sprint 15 add component-level tests for `<OnboardingTour/>` (focus, arrow keys, dialog a11y)? Default: yes, future sprint. Adds `@testing-library/user-event`. ~1.5 hr.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## New session: 2026-06-05 (early)
|
## New session: 2026-06-05 (early)
|
||||||
|
|||||||
@@ -749,3 +749,55 @@ User direction 2026-06-05: "Proceed." F9-lite reuses the pre-existing `OLLAMA_*`
|
|||||||
### T7.5 · `Review/sprint13-verification.md` (NEW)
|
### T7.5 · `Review/sprint13-verification.md` (NEW)
|
||||||
|
|
||||||
- Deploy + 3-step browser smoke + 4 API curls + a11y check + 6-risk table + future work section. Source of truth for the operator deploy + smoke flow.
|
- Deploy + 3-step browser smoke + 4 API curls + a11y check + 6-risk table + future work section. Source of truth for the operator deploy + smoke flow.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sprint 14 — Vitest for `useOnboarding` (Q4) — 🚧 IN PROGRESS
|
||||||
|
|
||||||
|
**Why this sprint:** Sprint 9 (F1 Onboarding Tour) shipped a hand-rolled ~420-line component; the bug `1562929` shipped a post-deploy fix the same day (`onComplete` was wired to `useOnboarding().reset()` — the inverse op, so the X/Skip/Esc dismiss path re-showed the tour). Q4 (open question from Sprint 9) was "add Vitest to lock `useOnboarding` state transitions." Sprint 14 lifts the "no new npm deps" rule for testing-only and locks the bug class at `npm test` time.
|
||||||
|
|
||||||
|
### T7.1 · Frontend devDeps (4 new + 1 for tsc)
|
||||||
|
|
||||||
|
- **`vitest@^1.6.0`** — the runner. Uses Vite's plugin-react under the hood, so it reuses the existing `vite.config.ts`-style config (no parallel build pipeline).
|
||||||
|
- **`happy-dom@^14.7.0`** — DOM env. Lighter than jsdom (7x smaller), faster startup. Sufficient for hooks-only tests.
|
||||||
|
- **`@testing-library/react@^14.2.0`** — `renderHook` + `act` for the `useOnboarding` test.
|
||||||
|
- **`@testing-library/jest-dom@^6.4.0`** — DOM matchers (loaded via the `/vitest` entry, not the `/jest` entry).
|
||||||
|
- **`@types/node@^20`** — tsc needed this for the `node:fs/promises` import in Case 7's static check on `App.tsx`.
|
||||||
|
|
||||||
|
All five go under `devDependencies`. Runtime bundle size unchanged (503.82 kB before/after).
|
||||||
|
|
||||||
|
### T7.2 · Vitest config + setup
|
||||||
|
|
||||||
|
- **`frontend/vitest.config.ts` (NEW):** `defineConfig` from `vitest/config` (extends Vite's config). `plugins: [react()]` reuses the existing React plugin. `test.environment: 'happy-dom'`, `test.setupFiles: ['./vitest-setup.ts']`, `test.include: ['src/**/*.test.{ts,tsx}']`, `test.globals: false` (explicit imports preferred over magic globals).
|
||||||
|
- **`frontend/vitest-setup.ts` (NEW):** a single line: `import '@testing-library/jest-dom/vitest'`. The `/vitest` entry auto-extends `expect` with DOM matchers.
|
||||||
|
- **`package.json` scripts:** `test` → `vitest run --reporter=default` (no watch by default — CI-friendly). `test:watch` → `vitest`.
|
||||||
|
|
||||||
|
### T7.3 · `OnboardingTour.test.tsx` — 7 cases
|
||||||
|
|
||||||
|
**File:** `frontend/src/components/OnboardingTour.test.tsx` (NEW, ~115 lines).
|
||||||
|
|
||||||
|
| # | Case | What it locks |
|
||||||
|
|---|------|---------------|
|
||||||
|
| 1 | clean init | `isComplete === false` when localStorage is empty |
|
||||||
|
| 2 | persisted init | `isComplete === true` when `localStorage.getItem(KEY) === '1'` |
|
||||||
|
| 3 | `markComplete` | state → true, localStorage **stays** at `'1'` (locks one direction of the S9 bug) |
|
||||||
|
| 4 | `reset` | localStorage cleared, state → false |
|
||||||
|
| 5 | `show` | identical to `reset` (intentional mirror) |
|
||||||
|
| 6 | localStorage throw on read | silently swallowed, `isComplete === false`, no crash |
|
||||||
|
| 7 | App.tsx wiring | static check on `App.tsx` source: `onComplete` calls `markComplete`, `onReset` calls `reset`; neither inverts (catches the original S9 bug `onComplete → reset` at the call site, which Cases 1-6 cannot catch because the bug was at the wiring, not in the hook) |
|
||||||
|
|
||||||
|
**Test runtime:** 7 cases pass in ~25 ms (transform 60 ms, setup 50 ms, collect 230 ms).
|
||||||
|
|
||||||
|
**Why Case 7 is the load-bearing test:** Sprint 9's bug `1562929` was at the App.tsx call site (`onComplete={() => onboarding.reset()}`), not inside `useOnboarding`. Cases 1-6 lock the hook contract; Case 7 is the only check that catches the wiring mistake. The integration check uses `node:fs/promises` to read `App.tsx` as a string, runs two regex matches to capture the arrow bodies of `onComplete={...}` and `onReset={...}`, and asserts each body calls the right `onboarding.*` method. Verified: flipping `markComplete` → `reset` in App.tsx makes Case 7 fail on the `onCompleteBody.toMatch(/markComplete/)` assertion.
|
||||||
|
|
||||||
|
### T7.4 · Sprint 14 verification gate
|
||||||
|
|
||||||
|
- [x] `npm test` — 7/7 cases pass in ~25 ms.
|
||||||
|
- [x] `npm run build` — tsc 0 errors, vite built in ~2.6 s, bundle 503.82 kB unchanged.
|
||||||
|
- [x] Case 7 catches the S9 bug — verified by inverting the wiring in `App.tsx` and watching Case 7 fail.
|
||||||
|
- [x] Backend untouched (no venv dependency).
|
||||||
|
- [ ] Commit on host + push.
|
||||||
|
|
||||||
|
### T7.5 · `Review/sprint14-verification.md` (NEW)
|
||||||
|
|
||||||
|
- Deploy + test commands + 5-risk table + open question for follow-up (Q1: component-level tests for `<OnboardingTour/>` itself, future sprint).
|
||||||
|
|||||||
Reference in New Issue
Block a user