feat(web): PLAN §4.8 — MSW, Knip, and Vitest coverage gate#21
Merged
feat(web): PLAN §4.8 — MSW, Knip, and Vitest coverage gate#21
Conversation
- Wire MSW in tests (handlers, server, vitest setup) and migrate page tests off vi.mock of the API client. - Add formatFetchError for Solid createResource / Error.cause behaviour; use it on Home and RecipePage. - Configure Knip (knip.json) and root/turbo scripts; tighten RecipeDetailPanel props export for Knip. - Vitest coverage: 100% lines, statements, and functions on included sources; exclude registry UI and api client; omit branch threshold (v8 noise on JSX). - CI runs web knip and pnpm --filter web test:coverage; ignore coverage output in apps/web .gitignore. - Raise apps/web TypeScript target/lib to ES2024 for Promise.withResolvers in tests. - Fix assert-no-function-declarations so thresholds.functions does not match as a function declaration. Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
Implements PLAN §4.8 for the web app by introducing MSW-backed Vitest tests, adding Knip dead-code checks, and enforcing a strict Vitest coverage gate in CI, alongside improved UI error formatting for API failures.
Changes:
- Add MSW test infrastructure (handlers/server/Vitest setup) and migrate existing page/app tests to
server.use(...)overrides. - Introduce
formatFetchErrorand update pages to surface API JSON{ message }errors in destructive alerts. - Add Knip + Vitest coverage scripts/config, wire them into Turbo and GitHub Actions CI (and ignore
coverage/output).
Reviewed changes
Copilot reviewed 23 out of 24 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| turbo.json | Adds Turbo tasks for test:coverage and knip. |
| pnpm-lock.yaml | Locks new dev dependencies (MSW, Knip, Vitest coverage provider) and transitive deps. |
| PLAN.md | Marks PLAN §4.8 as completed. |
| package.json | Adds root scripts for test:coverage and knip via Turbo. |
| apps/web/vite.config.ts | Adds Vitest setup + env and configures v8 coverage thresholds/exclusions. |
| apps/web/tsconfig.app.json | Bumps target/lib to ES2024 for Promise.withResolvers in tests. |
| apps/web/src/test/vitest-setup.ts | Global MSW server lifecycle hooks for Vitest. |
| apps/web/src/test/msw/server.ts | Creates a shared MSW node server with default handlers. |
| apps/web/src/test/msw/handlers.ts | Defines default MSW handlers for recipe list/detail endpoints. |
| apps/web/src/test/msw/constants.ts | Centralizes the API base URL used by MSW tests. |
| apps/web/src/pages/RecipePage.tsx | Uses formatFetchError for user-facing error alerts. |
| apps/web/src/pages/RecipePage.test.tsx | Switches RecipePage tests from API mocking to MSW handlers. |
| apps/web/src/pages/RecipePage.missing-id.test.tsx | Adds coverage for empty route param id behavior. |
| apps/web/src/pages/Home.tsx | Uses formatFetchError for user-facing error alerts. |
| apps/web/src/pages/Home.test.tsx | Switches Home tests from API mocking to MSW handlers (incl. loading gate). |
| apps/web/src/lib/format-fetch-error.ts | Adds error unwrapping/formatting helper for UI alerts. |
| apps/web/src/lib/format-fetch-error.test.ts | Adds unit tests for formatFetchError. |
| apps/web/src/components/recipe-detail/RecipeDetailPanel.tsx | Makes props type non-exported (Knip cleanup). |
| apps/web/src/App.test.tsx | Migrates App tests to MSW and adds navigation coverage. |
| apps/web/scripts/assert-no-function-declarations.mjs | Tightens regex to avoid false positives on functions:. |
| apps/web/package.json | Adds test:coverage and knip scripts + dev dependencies. |
| apps/web/knip.json | Adds Knip configuration and ignores for generated/UI/test files. |
| apps/web/.gitignore | Ignores coverage output directory. |
| .github/workflows/ci.yml | Adds web Knip + web coverage-gated Vitest runs to CI. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Keep local API test runs fast while preserving the 100% coverage gate in CI, and clarify the default MSW test handlers to match their actual behaviour. Made-with: Cursor
mcalthrop
commented
Apr 7, 2026
Add page-level coverage for null and loading responses so the web test suite exercises the authored fetch-state branches. Keep the Vitest config aligned with Solid's current branch-coverage limitation. Made-with: Cursor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes PLAN §4.8: MSW-backed tests, Knip, and a Vitest coverage gate in CI.
Changes
server.useinstead of mocking@/api.formatFetchErrorunwraps SolidcreateResource/Error.causeso API JSON messages surface in alerts.knip.jsonwith sensible ignores (generated API barrel, UI primitives, tests); non-exportedRecipeDetailPanelProps.src/components/ui/**andsrc/api/client.tsexcluded; branch threshold omitted (v8 is noisy on JSX).coverage/gitignored.pnpm --filter web knipandpnpm --filter web test:coverage.apps/webtarget/libES2024 forPromise.withResolversin tests.assert-no-function-declarationsuses\bfunction\bsothresholds.functionsis not a false positive.Testing
pnpm lintpnpm --filter web test:coveragepnpm --filter web knippnpm buildMade with Cursor