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/sveltekit.md
+22Lines changed: 22 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -76,3 +76,25 @@ The same pattern applies to `url.searchParams.get()` in `+server.ts` handlers.
76
76
## Page Component Props
77
77
78
78
SvelteKit page components (`+page.svelte`) accept only `data` and `form` as props (`svelte/valid-prop-names-in-kit-pages`). Commented-out features that reference other props are not "dead code" — remove only the violating prop declaration, preserve the feature code.
79
+
80
+
## load() — Group Related Model Fields as Objects
81
+
82
+
When a `load()` function returns fields from the same domain model (e.g., `AtCoderAccount`),
83
+
group them as an object rather than flattening to top-level keys.
84
+
Apply default values at this boundary so the page component typically does not need to handle `undefined`.
description: E2E testing rules and patterns (Playwright)
3
+
paths:
4
+
- '**/*.spec.ts'
5
+
- 'e2e/**'
6
+
---
7
+
8
+
# E2E Tests (Playwright)
9
+
10
+
## No Path Aliases
11
+
12
+
The `e2e/` directory is outside SvelteKit's build pipeline — `$lib`, `$features`, and other path aliases are not resolved. Define string constant values as local constants with a reference comment:
13
+
14
+
```typescript
15
+
// Mirrors WorkBookTab.SOLUTION from $features/workbooks/types/workbook
16
+
const TAB_SOLUTION ='solution';
17
+
```
18
+
19
+
Avoid importing values from `src/` in E2E test files. Type-only imports (`import type`) are acceptable since they are erased at compile time:
20
+
21
+
```typescript
22
+
// Bad: runtime import — path alias not resolved in e2e/
Playwright has no native `test.each`. Use `for...of` loops — the official recommended pattern.
46
+
47
+
**Single test with a loop** — use when testing a sequence or workflow within one test:
48
+
49
+
```typescript
50
+
// Mirrors TaskGrade from $lib/types/task — do not import from src/ in E2E files
51
+
const GRADES = ['Q10', 'Q9', 'Q8'] asconst;
52
+
53
+
for (const grade ofGRADES) {
54
+
awaitgradeButton(page, grade).click();
55
+
awaitexpect(page).toHaveURL(`?grades=${grade}`);
56
+
}
57
+
```
58
+
59
+
**Multiple tests from parameters** — use when each parameter represents an independent case:
60
+
61
+
```typescript
62
+
const GRADES = ['Q10', 'Q9', 'Q8'] asconst;
63
+
64
+
for (const grade ofGRADES) {
65
+
test(`filters by grade ${grade}`, async ({ page }) => {
66
+
awaitpage.goto('/tasks');
67
+
awaitgradeButton(page, grade).click();
68
+
awaitexpect(page).toHaveURL(`?grades=${grade}`);
69
+
});
70
+
}
71
+
```
72
+
73
+
## Assertions
74
+
75
+
After an interaction that changes element state (active tab, toggle, selection), assert the _new_ state — not just that the element is visible, which may have been true before the interaction. Assert an active CSS class, `aria-selected`, or similar attribute instead of `toBeVisible()`.
76
+
77
+
## Flowbite Toggle
78
+
79
+
Flowbite's `Toggle` renders an `sr-only``<input type="checkbox">` inside a `<label>`. Clicking the input directly fails because the visual `<span>` sibling intercepts pointer events. Click the label wrapper instead:
The same pattern applies to any Flowbite component that visually overlays its native input (e.g. `Checkbox`, `Radio`).
90
+
91
+
## Strict Mode: Scope Locators to the Content Area
92
+
93
+
When the navbar and page body both contain a link or button with the same text (e.g., a breadcrumb and a nav link share the same label), `getByRole` in strict mode will find multiple matches and throw. Scope the locator to the page's content container:
Use `.container` (page content wrapper) to exclude the global navbar. Prefer the narrowest scope that remains stable — breadcrumb `nav` inside `.container` is more precise than `.container` alone when the link only appears there.
105
+
106
+
## Conditional Skip Based on Runtime State
107
+
108
+
When a test depends on DB or session state that may vary across environments (e.g., a user's AtCoder verification status), use `test.skip(condition, reason)` inside the test body instead of a static `test.skip`. This way the test runs automatically when the precondition is met:
Copy file name to clipboardExpand all lines: .claude/rules/testing.md
+40-85Lines changed: 40 additions & 85 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -57,20 +57,6 @@ E2E test files must use the `.spec.ts` extension. `playwright.config.ts` matches
57
57
- Use `toBe(true)` / `toBe(false)` over `toBeTruthy()` / `toBeFalsy()`
58
58
- For DB query tests, assert `orderBy`, `include`, and other significant parameters with `expect.objectContaining` — not just `where`. When a returned field (e.g. `authorName`) depends on an `include` relation, that `include` clause must be part of the assertion, or a regression in the query shape will go undetected
59
59
- Enum membership: `in` traverses the prototype chain; use `Object.hasOwn(Enum, value)` instead
60
-
-**E2E state transitions**: after an interaction that changes element state (active tab, toggle, selection), assert the _new_ state — not just that the element is visible, which may have been true before the interaction. Assert an active CSS class, `aria-selected`, or similar attribute instead of `toBeVisible()`
61
-
62
-
## Cleanup in Tests
63
-
64
-
Wrap DB-mutating cleanup in `try/finally` — a failing assertion skips cleanup and contaminates later tests:
65
-
66
-
```typescript
67
-
try {
68
-
awaitdoSomething();
69
-
expect(result).toBe(expected);
70
-
} finally {
71
-
awaitrestoreState();
72
-
}
73
-
```
74
60
75
61
## Test Data
76
62
@@ -79,9 +65,22 @@ try {
79
65
- After `.filter()` on fixtures, verify actual contents — same ID may refer to a different entity after fixture updates
80
66
-**Description ↔ code path alignment**: when a test name describes a specific scenario (e.g. "tie-break"), verify the fixture actually exercises that code path. A test that passes without reaching the branch it claims to cover gives false confidence
81
67
82
-
## Mock Helpers
68
+
## Coverage
69
+
70
+
- Run `pnpm coverage` for coverage report
71
+
- Target: 80% lines, 80% branches
72
+
73
+
## Test Order Mirrors Source Order
74
+
75
+
Order `describe` blocks in service and utils test files to match the declaration order of functions in the source file. Misalignment makes it harder to cross-reference tests and implementation.
76
+
77
+
## Service Layer Unit Tests
83
78
84
-
Extract repeated mock patterns into helpers in the test file. For Prisma service tests, define the return type alias once and use it across all helpers:
79
+
Service tests mock Prisma via `vi.mock('$lib/server/database', ...)` — no real DB mutations occur.
80
+
81
+
### Mock Helpers
82
+
83
+
Extract repeated mock patterns into helpers in the test file. Define the return type alias once and use it across all helpers:
@@ -104,27 +103,24 @@ function mockCount(value: number) {
104
103
105
104
Extract `mockFindUnique`, `mockFindMany`, and `mockCount` as the standard trio for service tests that touch a single Prisma model. Add `mockCreate`, `mockTransaction`, and `mockDelete` when those operations are also tested.
106
105
107
-
##Component Vitest Unit Tests
106
+
### Cleanup for Integration Tests and Tests with Real Side Effects
108
107
109
-
Omit Vitest unit tests for a Svelte component when **both** conditions hold:
108
+
This does not apply to standard service layer unit tests that use Prisma mocks.
110
109
111
-
1. The component is template-only (no logic beyond prop bindings and basic conditionals)
112
-
2. The component is covered by E2E tests
113
-
114
-
When a component contains extracted logic (e.g. derived values, event handlers, utility calls), add unit tests for that logic in the nearest `utils/` file instead of testing the component directly.
115
-
116
-
## Testing Extracted Utilities
117
-
118
-
- Add tests at extraction time, not later
119
-
- For URL manipulation: assert the original URL is not mutated
120
-
- For multi-column operations (e.g., DnD): assert both source and destination columns
110
+
If a test performs real DB mutations, file system changes, external API calls, or other stateful side effects that persist beyond the test (e.g., integration tests, seed scripts), wrap assertions in `try/finally` — a failing assertion skips cleanup and contaminates later tests:
121
111
122
-
## Coverage
112
+
```typescript
113
+
try {
114
+
awaitdoSomething();
115
+
expect(result).toBe(expected);
116
+
} finally {
117
+
awaitrestoreState();
118
+
}
119
+
```
123
120
124
-
- Run `pnpm coverage` for coverage report
125
-
- Target: 80% lines, 70% branches
121
+
This is not needed for standard service unit tests that use Prisma mocks.
126
122
127
-
##Service Layer Split for Testability
123
+
### File Split for Testability
128
124
129
125
When a service file mixes DB operations and pure functions, split it into two files:
130
126
@@ -133,64 +129,23 @@ When a service file mixes DB operations and pure functions, split it into two fi
133
129
134
130
Stop the split if internal helpers (e.g. `fetchUnplacedWorkbooks`) would be fragmented across files — cohesion matters more than the split itself.
135
131
136
-
## HTTP Mocking
137
-
138
-
Use Nock for external HTTP calls. See `src/test/lib/clients/` for examples.
139
-
140
-
## Test Order Mirrors Source Order
141
-
142
-
Order `describe` blocks in service and utils test files to match the declaration order of functions in the source file. Misalignment makes it harder to cross-reference tests and implementation.
143
-
144
-
## E2E Tests
145
-
146
-
### No Path Aliases
147
-
148
-
The `e2e/` directory is outside SvelteKit's build pipeline — `$lib`, `$features`, and other path aliases are not resolved. Define URL string values as local constants with a reference comment:
149
-
150
-
```typescript
151
-
// Mirrors WorkBookTab.SOLUTION from $features/workbooks/types/workbook
152
-
const TAB_SOLUTION ='solution';
153
-
```
154
-
155
-
Avoid importing types from `src/` in E2E test files.
156
-
157
-
### Describe Hierarchy
158
-
159
-
When a `describe` block for a user role grows large, split it by behavioral dimension rather than adding more flat `test()` calls:
Playwright has no native `test.each`. Use `for...of` loops — the official recommended pattern:
134
+
E2E tests are complementary to, not a substitute for, unit tests. Add Vitest unit tests for any component logic (derived values, event handlers, utility calls) by extracting it to the nearest `utils/` file and testing there.
173
135
174
-
```typescript
175
-
// Mirrors TaskGrade from $lib/types/task — do not import from src/ in E2E files
176
-
const GRADES = ['Q10', 'Q9', 'Q8'] asconst;
136
+
You may omit a component-level Vitest test when **both** conditions hold:
177
137
178
-
for (const grade ofGRADES) {
179
-
awaitgradeButton(grade).click();
180
-
awaitexpect(page).toHaveURL(`?grades=${grade}`);
181
-
}
182
-
```
138
+
1. The component is template-only (no logic beyond prop bindings and simple `{#if}`/`{#each}` blocks that only render — no inline function calls, ternaries with side effects, derived computations, or nested logic)
139
+
2. The component's rendering paths are covered by E2E tests
183
140
184
-
### Flowbite Toggle
141
+
When a component contains extracted logic (e.g. derived values, event handlers, utility calls), add unit tests for that logic in the nearest `utils/` file instead of testing the component directly.
185
142
186
-
Flowbite's `Toggle` renders an `sr-only``<input type="checkbox">` inside a `<label>`. Clicking the input directly fails because the visual `<span>` sibling intercepts pointer events. Click the label wrapper instead:
0 commit comments