You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .claude/rules/testing.md
+46Lines changed: 46 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,6 +28,52 @@ E2E files: **must** use `.spec.ts` extension (`.test.ts` not detected).
28
28
29
29
Route unit tests: `src/routes/**/*.test.ts` is included by `vite.config.ts`. **Never use `+` as a filename prefix** — SvelteKit reserves it and `pnpm check` will error. Name route test files `page_server.test.ts`, not `+page.server.test.ts`.
30
30
31
+
## Test Environment
32
+
33
+
Default is `node` (set in `vite.config.ts`). Only files touching the real DOM (`window` / `document` / `localStorage`) opt in with a top-of-file `// @vitest-environment jsdom`. **Never set jsdom globally** — most tests are pure units and per-file jsdom construction is ~5.5x slower.
34
+
35
+
### Toggling `browser` per describe
36
+
37
+
**Never register `vi.mock('$app/environment')` twice in one file.** Every `vi.mock` is hoisted above the imports and runs once before any test, so a `{ browser: false }` mock written inside an SSR `describe`/`beforeEach` silently overwrites the `{ browser: true }` one at the top — the whole file ends up pinned to `browser = false`, the localStorage branches never execute, and tests that "verify" them become false-positives while still passing.
38
+
39
+
Use **one dynamic mock** driven by a `vi.hoisted` flag, and toggle the flag in `beforeEach`. Default the flag to `false` so singletons constructed at import time stay SSR-safe:
store=newMyStore(); // constructed after the flag flips — reads localStorage
58
+
});
59
+
// …
60
+
});
61
+
62
+
describe('MyStore in SSR', () => {
63
+
beforeEach(() => {
64
+
browserState.value=false;
65
+
});
66
+
// …
67
+
});
68
+
```
69
+
70
+
**Never assert a browser branch on the import-time singleton.** The flag is `false` while the module graph is evaluated, so the exported singleton is always built in SSR mode and never touches localStorage — asserting on it under `browser = true` is the same false-positive as the double-`vi.mock` above. Construct a fresh instance inside the browser `describe` (which is why the store class, not just the singleton, needs a named export). The singleton is still worth one assertion: that import-time construction is SSR-safe.
71
+
72
+
In jsdom files, do **not** stub localStorage with `vi.stubGlobal` — use jsdom's real `Storage` and assert on state (`localStorage.getItem(key)`), not on spy calls. Cover both SSR guards with separate cases, since one assertion cannot carry both:
73
+
74
+
-**read guard**: pre-seed localStorage, construct a fresh store, expect the _default_ (the pre-seeded value stays in storage, so do not expect `getItem(key)` to be null here)
75
+
-**write guard**: start from empty localStorage, call the setter, expect `getItem(key)` to still be null
Copy file name to clipboardExpand all lines: compose.yaml
+2-2Lines changed: 2 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -6,8 +6,8 @@ services:
6
6
- '5555:5555'
7
7
tty: true
8
8
volumes:
9
-
- .:/usr/src/app:cached
10
-
- ./node_modules:/usr/src/app/node_modules:cached
9
+
- .:/usr/src/app
10
+
- ./node_modules:/usr/src/app/node_modules
11
11
environment:
12
12
- NODE_ENV=development
13
13
- DATABASE_URL=postgresql://db_user:db_password@db:5432/test_db?pgbouncer=true&connection_limit=10&connect_timeout=60&statement_timeout=60000 # Note: Local server cannot start if port is set to db:6543.
0 commit comments