claude-toolkit 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +54 -13
  3. package/bin/cli.ts +75 -19
  4. package/package.json +6 -7
  5. package/src/generator.ts +64 -26
  6. package/src/refresh-hint.ts +95 -0
  7. package/bin/postinstall.mjs +0 -69
  8. package/docs/README.md +0 -59
  9. package/docs/agents/code-reviewer.md +0 -76
  10. package/docs/agents/github-workflow.md +0 -98
  11. package/docs/best-practices/capacitor/README.md +0 -64
  12. package/docs/best-practices/capacitor/capacitor-8.md +0 -60
  13. package/docs/best-practices/capacitor/capgo-setup.md +0 -151
  14. package/docs/best-practices/capacitor/channels-and-rollouts.md +0 -101
  15. package/docs/best-practices/capacitor/live-updates-ota.md +0 -75
  16. package/docs/best-practices/capacitor/security-encryption.md +0 -68
  17. package/docs/best-practices/solidjs/README.md +0 -43
  18. package/docs/best-practices/solidjs/anti-patterns.md +0 -166
  19. package/docs/best-practices/solidjs/component-patterns.md +0 -131
  20. package/docs/best-practices/solidjs/context-and-global-state.md +0 -131
  21. package/docs/best-practices/solidjs/control-flow.md +0 -124
  22. package/docs/best-practices/solidjs/data-fetching.md +0 -205
  23. package/docs/best-practices/solidjs/effects-and-lifecycle.md +0 -113
  24. package/docs/best-practices/solidjs/performance.md +0 -100
  25. package/docs/best-practices/solidjs/props-patterns.md +0 -100
  26. package/docs/best-practices/solidjs/reactivity-model.md +0 -104
  27. package/docs/best-practices/solidjs/signals-and-state.md +0 -78
  28. package/docs/best-practices/solidjs/stores-and-nested-state.md +0 -111
  29. package/docs/best-practices/solidjs/typescript-integration.md +0 -186
  30. package/docs/best-practices/testing/README.md +0 -84
  31. package/docs/best-practices/testing/playwright-e2e.md +0 -649
  32. package/docs/best-practices/testing/storybook-interaction.md +0 -455
  33. package/docs/best-practices/testing/vitest-unit.md +0 -451
  34. package/docs/best-practices/typescript/README.md +0 -45
  35. package/docs/best-practices/typescript/any-and-unknown.md +0 -73
  36. package/docs/best-practices/typescript/deriving-vs-decoupling.md +0 -83
  37. package/docs/best-practices/typescript/discriminated-unions.md +0 -75
  38. package/docs/best-practices/typescript/enums-alternatives.md +0 -72
  39. package/docs/best-practices/typescript/essential-patterns.md +0 -119
  40. package/docs/best-practices/typescript/generics-patterns.md +0 -105
  41. package/docs/best-practices/typescript/micro-opinions.md +0 -87
  42. package/docs/best-practices/typescript/runtime-validation.md +0 -62
  43. package/docs/best-practices/typescript/satisfies-operator.md +0 -100
  44. package/docs/best-practices/typescript/tsconfig-cheat-sheet.md +0 -160
  45. package/docs/best-practices/typescript/type-organization.md +0 -64
  46. package/docs/best-practices/typescript/type-vs-interface.md +0 -80
  47. package/docs/commands/code-quality.md +0 -36
  48. package/docs/commands/onboard.md +0 -63
  49. package/docs/commands/pr-review.md +0 -91
  50. package/docs/commands/pr-summary.md +0 -50
  51. package/docs/commands/proto-check.md +0 -59
  52. package/docs/commands/ticket.md +0 -56
  53. package/docs/skills/code-style.md +0 -26
  54. package/docs/skills/systematic-debugging.md +0 -70
  55. package/docs/skills/testing-patterns.md +0 -140
  56. package/docs/skills/typescript-conventions.md +0 -283
  57. package/docs/skills/verification-before-completion.md +0 -103
  58. package/docs/stacks/capacitor-ota.md +0 -145
  59. package/docs/stacks/cloudflare-d1-kv.md +0 -144
  60. package/docs/stacks/esnext-idioms.md +0 -25
  61. package/docs/stacks/i18n-typesafe.md +0 -141
  62. package/docs/stacks/playwright-patterns.md +0 -179
  63. package/docs/stacks/protobuf-contracts.md +0 -85
  64. package/docs/stacks/rust-wasm-patterns.md +0 -106
  65. package/docs/stacks/solidjs-patterns.md +0 -110
  66. package/docs/stacks/storybook-patterns.md +0 -177
  67. package/docs/stacks/vanilla-extract-patterns.md +0 -115
  68. package/docs/stacks/vite-vitest-patterns.md +0 -485
@@ -1,100 +0,0 @@
1
- # Props Patterns
2
-
3
- > Sources: [Props](https://docs.solidjs.com/concepts/components/props), [Props Tutorials](https://www.solidjs.com/tutorial/props_defaults) — SolidJS Docs
4
-
5
- ## The Cardinal Rule: Never Destructure Props
6
-
7
- Props use **getters** internally for reactive tracking. Destructuring extracts the value once, severing the reactive connection permanently.
8
-
9
- ```typescript
10
- // WRONG — breaks reactivity
11
- const MyComponent = ({ name, count }) => {
12
- return <div>{name}: {count}</div>; // Never updates
13
- };
14
-
15
- // WRONG — same problem
16
- const MyComponent = (props) => {
17
- const { name } = props; // Captured once, never reactive
18
- return <div>{name}</div>;
19
- };
20
-
21
- // CORRECT — preserves reactivity
22
- const MyComponent = (props) => {
23
- return <div>{props.name}: {props.count}</div>;
24
- };
25
- ```
26
-
27
- ## `mergeProps` for Defaults
28
-
29
- Set default prop values without breaking reactivity:
30
-
31
- ```typescript
32
- import { mergeProps } from "solid-js";
33
-
34
- const Button = (props) => {
35
- const merged = mergeProps({ variant: "primary", size: "md" }, props);
36
- return <button class={`btn-${merged.variant} btn-${merged.size}`}>
37
- {merged.children}
38
- </button>;
39
- };
40
- ```
41
-
42
- `mergeProps` resolves properties in reverse order — later objects override earlier ones while preserving reactive tracking.
43
-
44
- ## `splitProps` for Prop Forwarding
45
-
46
- Safely separate local props from props to forward to children:
47
-
48
- ```typescript
49
- import { splitProps } from "solid-js";
50
-
51
- const Input = (props) => {
52
- const [local, inputProps] = splitProps(props, ["label", "error"]);
53
-
54
- return (
55
- <div>
56
- <label>{local.label}</label>
57
- <input {...inputProps} />
58
- <Show when={local.error}>
59
- <span class="error">{local.error}</span>
60
- </Show>
61
- </div>
62
- );
63
- };
64
- ```
65
-
66
- You can split into multiple groups:
67
-
68
- ```typescript
69
- const [style, events, rest] = splitProps(props, ["class", "style"], ["onClick", "onInput"]);
70
- ```
71
-
72
- ## `children` Helper
73
-
74
- Accessing `props.children` multiple times can create duplicate elements. Use the `children` helper to resolve children once:
75
-
76
- ```typescript
77
- import { children } from "solid-js";
78
-
79
- const ColoredList = (props) => {
80
- const resolved = children(() => props.children);
81
-
82
- return <div class="colored">{resolved()}</div>;
83
- };
84
- ```
85
-
86
- The helper resolves any dynamic children into a flat array, memoized for safe repeated access.
87
-
88
- ## Call Signals Before Passing as Props
89
-
90
- When passing a signal value to a child component, call the getter. Components shouldn't need to know whether a prop came from a signal or a static value.
91
-
92
- ```typescript
93
- // CORRECT — child receives a plain value
94
- <UserCard name={userName()} age={userAge()} />
95
-
96
- // Also valid — passing a reactive getter for the child to track
97
- <UserCard name={userName} /> // child would read props.name()
98
- ```
99
-
100
- The first pattern is simpler and recommended for most cases. The second is useful when you want the child to participate in fine-grained tracking.
@@ -1,104 +0,0 @@
1
- # Reactivity Model
2
-
3
- > Sources: [Fine-Grained Reactivity](https://docs.solidjs.com/advanced-concepts/fine-grained-reactivity), [Intro to Reactivity](https://docs.solidjs.com/concepts/intro-to-reactivity) — SolidJS Docs
4
-
5
- ## The Core Difference
6
-
7
- SolidJS uses **fine-grained reactivity**. Updates target specific DOM elements that depend on changed data — not entire component trees. Components run **once**; only the reactive expressions inside them re-execute.
8
-
9
- This means:
10
- - No virtual DOM diffing
11
- - No component re-renders
12
- - No stale closure bugs
13
- - Updates are surgical and automatic
14
-
15
- ## How It Works
16
-
17
- ### Signals, Effects, and the Subscription Model
18
-
19
- The reactive system has two key elements: **signals** (data sources) and **observers** (effects, memos, JSX expressions).
20
-
21
- 1. A global `currentSubscriber` variable tracks the active observer
22
- 2. When an observer runs, it sets itself as the current subscriber
23
- 3. Any signal read during that execution registers the subscriber
24
- 4. When a signal's value changes, all registered subscribers re-execute
25
- 5. The subscriber reference is cleared after execution
26
-
27
- ```typescript
28
- const [count, setCount] = createSignal(0);
29
-
30
- // This effect becomes a subscriber of `count`
31
- createEffect(() => {
32
- console.log(count()); // Reading count() registers the dependency
33
- });
34
-
35
- setCount(1); // Notifies the effect, which re-runs
36
- ```
37
-
38
- ### Equality Checks
39
-
40
- Signals only notify subscribers when the value **actually changes**. Redundant updates (setting the same value) are ignored.
41
-
42
- ```typescript
43
- setCount(0); // If count is already 0, no effects fire
44
- ```
45
-
46
- ## The Synchronous Contract
47
-
48
- Reactivity in Solid is **synchronous**. The system registers a subscriber, runs the function, and unregisters — all in one synchronous pass.
49
-
50
- This means **async operations break tracking**:
51
-
52
- ```typescript
53
- // BROKEN — fetch happens after tracking ends
54
- createEffect(async () => {
55
- const data = await fetch(url()); // url() is tracked
56
- setResult(data); // but this runs after tracking ends
57
- });
58
-
59
- // CORRECT — use createResource for async
60
- const [data] = createResource(url, fetchData);
61
- ```
62
-
63
- ## Dependency Tracking is Automatic
64
-
65
- You don't declare dependencies. Solid tracks them by observing which signals are read during execution:
66
-
67
- ```typescript
68
- createEffect(() => {
69
- // Solid automatically knows this depends on firstName() and lastName()
70
- console.log(`${firstName()} ${lastName()}`);
71
- });
72
- ```
73
-
74
- ### Conditional Tracking
75
-
76
- If a branch isn't taken, signals in that branch aren't tracked:
77
-
78
- ```typescript
79
- const display = createMemo(() => {
80
- if (!showDetails()) return "Hidden";
81
- // name() and age() are only tracked when showDetails() is true
82
- return `${name()}, age ${age()}`;
83
- });
84
- ```
85
-
86
- ## Derive, Don't Sync
87
-
88
- The golden rule of Solid reactivity:
89
-
90
- **If a value can be computed from other reactive values, derive it — don't synchronize it with an effect.**
91
-
92
- ```typescript
93
- // WRONG — synchronizing state
94
- createEffect(() => {
95
- setDoubled(count() * 2);
96
- });
97
-
98
- // RIGHT — deriving state
99
- const doubled = createMemo(() => count() * 2);
100
- // or simply:
101
- const doubled = () => count() * 2;
102
- ```
103
-
104
- Derived values integrate naturally with the reactive graph. Effects that synchronize state create ordering problems and glitchy intermediate states.
@@ -1,78 +0,0 @@
1
- # Signals & State
2
-
3
- > Sources: [Signals](https://docs.solidjs.com/concepts/signals), [Derived Signals](https://docs.solidjs.com/concepts/derived-values/derived-signals) — SolidJS Docs
4
-
5
- ## createSignal
6
-
7
- The primary state primitive. Returns a getter function and a setter function.
8
-
9
- ```typescript
10
- const [count, setCount] = createSignal(0);
11
-
12
- // Read: always call as a function
13
- console.log(count()); // 0
14
-
15
- // Write: direct value or updater function
16
- setCount(5);
17
- setCount(prev => prev + 1);
18
- ```
19
-
20
- **Key mental model:** The getter is a function, not a value. You must call `count()` to read it. Forgetting the parentheses is the most common Solid mistake.
21
-
22
- ## Derived Values
23
-
24
- ### Inline Derivations
25
-
26
- For simple computations, a plain function is sufficient:
27
-
28
- ```typescript
29
- const doubled = () => count() * 2;
30
- ```
31
-
32
- This re-evaluates every time it's called in a reactive context. No caching.
33
-
34
- ### createMemo
35
-
36
- For expensive computations or values read in multiple places, use `createMemo` to cache the result:
37
-
38
- ```typescript
39
- const sorted = createMemo(() => {
40
- console.log("Sorting..."); // Only runs when items() changes
41
- return items().slice().sort((a, b) => a.localeCompare(b));
42
- });
43
- ```
44
-
45
- **When to use `createMemo` vs inline derivation:**
46
-
47
- | Scenario | Use |
48
- |---|---|
49
- | Simple, cheap computation | Inline function `() => ...` |
50
- | Expensive computation (sort, filter, map) | `createMemo` |
51
- | Value read in multiple reactive contexts | `createMemo` |
52
- | Value used once in JSX | Inline function |
53
-
54
- ## Signals for Functions
55
-
56
- If you need to store a function as a signal value, use the callback form of the setter to avoid Solid treating it as an updater:
57
-
58
- ```typescript
59
- const [handler, setHandler] = createSignal<() => void>(() => {});
60
-
61
- // WRONG — Solid calls this as an updater
62
- setHandler(myFunction);
63
-
64
- // RIGHT — wrap in callback
65
- setHandler(() => myFunction);
66
- ```
67
-
68
- ## When to Use Signals vs Stores
69
-
70
- | Data shape | Use |
71
- |---|---|
72
- | Single primitive value | `createSignal` |
73
- | Simple object (few properties, flat) | `createSignal` |
74
- | Complex nested object | `createStore` |
75
- | Array of objects with updates | `createStore` |
76
- | Shared state across components | `createStore` + context |
77
-
78
- See [Stores & Nested State](stores-and-nested-state.md) for store patterns.
@@ -1,111 +0,0 @@
1
- # Stores & Nested State
2
-
3
- > Sources: [Stores](https://docs.solidjs.com/concepts/stores), [Complex State Management](https://docs.solidjs.com/guides/complex-state-management) — SolidJS Docs
4
-
5
- ## When to Use Stores
6
-
7
- Use `createStore` when your state is a **complex, nested object** or an **array of objects** that receives granular updates. Stores provide fine-grained reactivity at the **property level** — updating a nested property only re-renders the parts of UI that depend on that specific property.
8
-
9
- ```typescript
10
- import { createStore } from "solid-js/store";
11
-
12
- const [state, setState] = createStore({
13
- user: { name: "Alice", settings: { theme: "dark" } },
14
- tasks: [
15
- { id: 1, text: "Learn Solid", completed: false },
16
- ],
17
- });
18
- ```
19
-
20
- **Reading:** Access properties directly (no function call needed):
21
-
22
- ```typescript
23
- <span>{state.user.name}</span>
24
- <span>{state.user.settings.theme}</span>
25
- ```
26
-
27
- ## Path Syntax Updates
28
-
29
- Stores use a **path syntax** for targeted, efficient updates:
30
-
31
- ```typescript
32
- // Update a nested property
33
- setState("user", "settings", "theme", "light");
34
-
35
- // Update a specific array item
36
- setState("tasks", 0, "completed", true);
37
-
38
- // Update with a function
39
- setState("tasks", 0, "completed", prev => !prev);
40
- ```
41
-
42
- ### Array Operations
43
-
44
- ```typescript
45
- // Append to array (targeted — doesn't recreate the array)
46
- setState("tasks", state.tasks.length, {
47
- id: 2, text: "Ship it", completed: false,
48
- });
49
-
50
- // Update multiple indices
51
- setState("tasks", [0, 2], "completed", true);
52
-
53
- // Update by range
54
- setState("tasks", { from: 0, to: 3 }, "completed", false);
55
-
56
- // Update by predicate
57
- setState("tasks", task => task.completed, "archived", true);
58
- ```
59
-
60
- ## `produce` for Imperative Updates
61
-
62
- When you need to modify multiple properties at once, `produce` provides an imperative API:
63
-
64
- ```typescript
65
- import { produce } from "solid-js/store";
66
-
67
- setState("tasks", 0, produce(task => {
68
- task.completed = true;
69
- task.completedAt = Date.now();
70
- }));
71
-
72
- // Also works at the store root
73
- setState(produce(state => {
74
- state.tasks.push({ id: 3, text: "New task", completed: false });
75
- state.user.name = "Bob";
76
- }));
77
- ```
78
-
79
- `produce` creates a draft, applies your mutations, then produces an immutable update. Only use it with objects and arrays (not Sets or Maps).
80
-
81
- ## `reconcile` for External Data
82
-
83
- When you receive a whole new object (e.g., from an API response), `reconcile` diffs the old and new data, only triggering updates for properties that actually changed:
84
-
85
- ```typescript
86
- import { reconcile } from "solid-js/store";
87
-
88
- const newData = await fetchTasks();
89
- setState("tasks", reconcile(newData));
90
- ```
91
-
92
- This avoids re-rendering everything when most of the data hasn't changed.
93
-
94
- ## `unwrap` for Snapshots
95
-
96
- Extract a plain JavaScript object from a store (strips the reactive proxy):
97
-
98
- ```typescript
99
- import { unwrap } from "solid-js/store";
100
-
101
- const snapshot = unwrap(state);
102
- // Use snapshot for third-party libraries, logging, or serialization
103
- ```
104
-
105
- ## Best Practices
106
-
107
- - **Use path syntax** for targeted updates — it's more efficient than replacing objects
108
- - **Use `produce`** when modifying multiple properties in one operation
109
- - **Use `reconcile`** when integrating API responses to avoid unnecessary updates
110
- - **Don't mutate store properties directly** — always go through `setState`
111
- - **Stores are batched automatically** — multiple `setState` calls in the same synchronous block are batched into one update
@@ -1,186 +0,0 @@
1
- # TypeScript Integration
2
-
3
- > Source: [TypeScript](https://docs.solidjs.com/configuration/typescript) — SolidJS Docs
4
-
5
- ## TSConfig for SolidJS
6
-
7
- Required settings for Solid's JSX transformation:
8
-
9
- ```jsonc
10
- {
11
- "compilerOptions": {
12
- "jsx": "preserve",
13
- "jsxImportSource": "solid-js",
14
- "strict": true,
15
- "noEmit": true,
16
- "isolatedModules": true
17
- }
18
- }
19
- ```
20
-
21
- `"jsx": "preserve"` is required — Solid's JSX transformation is incompatible with TypeScript's built-in JSX handling.
22
-
23
- ## Typing Signals
24
-
25
- ```typescript
26
- // Type is inferred from the initial value
27
- const [count, setCount] = createSignal(0); // Signal<number>
28
-
29
- // Explicit type when initial value doesn't capture the full type
30
- const [user, setUser] = createSignal<User | null>(null);
31
-
32
- // Without a default, type includes undefined
33
- const [data, setData] = createSignal<string>(); // Accessor<string | undefined>
34
- ```
35
-
36
- ## Typing Stores
37
-
38
- ```typescript
39
- type AppState = {
40
- user: User;
41
- tasks: Task[];
42
- settings: { theme: "light" | "dark" };
43
- };
44
-
45
- const [state, setState] = createStore<AppState>({
46
- user: { id: "1", name: "Alice" },
47
- tasks: [],
48
- settings: { theme: "dark" },
49
- });
50
- ```
51
-
52
- ## Typing Context
53
-
54
- Provide a type parameter to `createContext`:
55
-
56
- ```typescript
57
- type AuthContext = {
58
- user: User | null;
59
- login: (credentials: Credentials) => Promise<void>;
60
- logout: () => void;
61
- };
62
-
63
- const AuthContext = createContext<AuthContext>();
64
- ```
65
-
66
- **Factory pattern** — derive the type from the factory function:
67
-
68
- ```typescript
69
- function createAuthStore() { /* ... */ }
70
-
71
- const AuthContext = createContext<ReturnType<typeof createAuthStore>>();
72
- ```
73
-
74
- ## Component Types
75
-
76
- ```typescript
77
- import type { Component, ParentComponent, VoidComponent, FlowComponent } from "solid-js";
78
-
79
- // Standard component
80
- const App: Component = () => <div>Hello</div>;
81
-
82
- // Accepts children
83
- const Card: ParentComponent<{ title: string }> = (props) => (
84
- <div>
85
- <h2>{props.title}</h2>
86
- {props.children}
87
- </div>
88
- );
89
-
90
- // No children allowed
91
- const Icon: VoidComponent<{ name: string }> = (props) => (
92
- <svg class={`icon-${props.name}`} />
93
- );
94
- ```
95
-
96
- ### Generic Components
97
-
98
- `Component` types don't support generics. Use function declarations:
99
-
100
- ```typescript
101
- function Select<T>(props: {
102
- options: T[];
103
- value: T;
104
- onChange: (value: T) => void;
105
- label: (item: T) => string;
106
- }): JSX.Element {
107
- return (
108
- <For each={props.options}>
109
- {(option) => (
110
- <button
111
- classList={{ active: option === props.value }}
112
- onClick={() => props.onChange(option)}
113
- >
114
- {props.label(option)}
115
- </button>
116
- )}
117
- </For>
118
- );
119
- }
120
- ```
121
-
122
- ## Event Handlers
123
-
124
- Inline handlers are auto-typed. For extracted handlers:
125
-
126
- ```typescript
127
- const handleInput: JSX.EventHandler<HTMLInputElement, InputEvent> = (e) => {
128
- // e.currentTarget is typed as HTMLInputElement
129
- setValue(e.currentTarget.value);
130
- };
131
- ```
132
-
133
- ## Control Flow Type Narrowing
134
-
135
- TypeScript can't narrow types through accessors. Use the callback form of `<Show>`:
136
-
137
- ```typescript
138
- // WRONG — TypeScript doesn't know user() is non-null inside Show
139
- <Show when={user()}>
140
- <span>{user()!.name}</span> {/* Forced to use ! */}
141
- </Show>
142
-
143
- // RIGHT — callback provides narrowed accessor
144
- <Show when={user()}>
145
- {(u) => <span>{u().name}</span>}
146
- </Show>
147
- ```
148
-
149
- ## Extending JSX Types
150
-
151
- ### Custom Events
152
-
153
- ```typescript
154
- declare module "solid-js" {
155
- namespace JSX {
156
- interface CustomEvents {
157
- myEvent: CustomEvent<{ detail: string }>;
158
- }
159
- }
160
- }
161
- ```
162
-
163
- ### Custom Directives
164
-
165
- ```typescript
166
- declare module "solid-js" {
167
- namespace JSX {
168
- interface Directives {
169
- tooltip: string;
170
- clickOutside: () => void;
171
- }
172
- }
173
- }
174
- ```
175
-
176
- ### Explicit Properties/Attributes
177
-
178
- ```typescript
179
- declare module "solid-js" {
180
- namespace JSX {
181
- interface ExplicitProperties { /* prop:___ */ }
182
- interface ExplicitAttributes { /* attr:___ */ }
183
- interface ExplicitBoolAttributes { /* bool:___ */ }
184
- }
185
- }
186
- ```
@@ -1,84 +0,0 @@
1
- # Testing Best Practices
2
-
3
- > A curated collection of testing best practices for the rendezvous.nz stack, sourced from official documentation, framework maintainers, and the testing community as of 2026.
4
-
5
- This project uses a **3-layer testing strategy** that maps to the classic testing pyramid. Each layer targets a different scope, runs in a different environment, and catches a different class of bugs.
6
-
7
- ```
8
- / E2E \ Playwright (real browsers)
9
- /----------\ Full user flows, cross-browser
10
- / Interaction \ Storybook (component sandbox)
11
- /----------------\ Component behavior, visual, a11y
12
- / Unit \ Vitest (Node/JSDOM)
13
- /--------------------\ Pure logic, signals, utilities
14
- ```
15
-
16
- ## The Three Layers
17
-
18
- | Layer | Tool | Scope | Environment | Speed |
19
- | --------------------------------------- | ---------------- | ------------------------------------ | -------------------------------------- | ----------------- |
20
- | [Unit](vitest-unit.md) | Vitest 4.x | Functions, signals, hooks, utilities | Node / JSDOM | ~ms per test |
21
- | [Interaction](storybook-interaction.md) | Storybook 10.x | Component rendering, behavior, a11y | Real browser (via Vitest addon) | ~100ms per story |
22
- | [E2E](playwright-e2e.md) | Playwright 1.58+ | Full user flows, API integration | Real browser (Chromium/Firefox/WebKit) | ~seconds per test |
23
-
24
- ## When to Use Which Layer
25
-
26
- | Question | Layer |
27
- | --------------------------------------------------------- | -------------------------------------- |
28
- | Does this logic need a DOM? | **Unit** if no, **Interaction** if yes |
29
- | Does it involve multiple pages or navigation? | **E2E** |
30
- | Am I testing a single component's visual states? | **Interaction** |
31
- | Am I testing a reactive primitive (signal, memo, effect)? | **Unit** |
32
- | Does it require authentication or real network? | **E2E** |
33
- | Am I testing component accessibility? | **Interaction** (axe-core addon) |
34
- | Am I testing cross-browser rendering? | **E2E** (multi-browser projects) |
35
- | Does it need the Cloudflare Worker running? | **E2E** |
36
-
37
- ## Distribution Guideline
38
-
39
- Aim for the pyramid shape -- many fast unit tests at the base, fewer interaction tests in the middle, and a focused set of E2E tests at the top.
40
-
41
- | Layer | Target Ratio | What to Cover |
42
- | ----------- | ------------ | ---------------------------------------------------------------------------- |
43
- | Unit | ~70% | Business logic, utilities, data transforms, reactive primitives, error paths |
44
- | Interaction | ~20% | Component variants, user interactions, accessibility, visual regression |
45
- | E2E | ~10% | Critical user journeys, authentication flows, cross-page workflows |
46
-
47
- ## Shared Principles Across All Layers
48
-
49
- 1. **Test behavior, not implementation.** Assert what the user sees or what the API returns, not internal state.
50
- 2. **Prefer role-based queries.** Use `getByRole`, `getByLabel`, `getByText` over test IDs or CSS selectors.
51
- 3. **Never sleep.** Use framework-provided waiting mechanisms (`waitFor`, `findBy*`, web-first assertions).
52
- 4. **One assertion focus per test.** A test should verify one behavior. Multiple assertions are fine if they verify facets of the same behavior.
53
- 5. **Isolate tests.** No test should depend on another test's side effects. Reset state between tests.
54
- 6. **Mock at boundaries.** Mock network calls and external services, not internal modules.
55
- 7. **SolidJS reactivity rules apply in tests.** Never destructure props. Use `renderHook` or `createRoot` for signal testing.
56
-
57
- ## SolidJS-Specific Considerations
58
-
59
- SolidJS components run once -- only reactive expressions re-execute. This affects testing:
60
-
61
- - **`render()` takes a callback**: `render(() => <Component />)`, not `render(<Component />)`.
62
- - **Effects are async**: Use `testEffect` from `@solidjs/testing-library` for effect assertions.
63
- - **Storybook decorators need `createJSXDecorator`**: Standard decorators cause duplicate DOM elements with SolidJS.
64
-
65
- ## Technology Versions (as of April 2026)
66
-
67
- | Tool | Version | Notes |
68
- | ------------------------ | ------- | ---------------------------------------------------- |
69
- | Vitest | ^4.1.x | V8 coverage, test tags, builder-pattern fixtures |
70
- | @solidjs/testing-library | ^0.8.x | SolidJS-specific render, renderHook, testEffect |
71
- | Storybook | ^10.3.x | ESM-only, `sb.mock`, testing widget |
72
- | storybook-solidjs-vite | ^10.0.x | Community-maintained SolidJS renderer |
73
- | @storybook/addon-vitest | ^10.x | Replaces old test-runner, uses Vitest browser mode |
74
- | Playwright | ^1.58.x | Timeline view, Chrome for Testing, WebSocket mocking |
75
- | @axe-core/playwright | ^4.x | WCAG 2.1 AA automated accessibility checks |
76
- | MSW | ^2.x | Network mocking for both Storybook and Vitest |
77
-
78
- ## Guides
79
-
80
- | Guide | Summary |
81
- | --------------------------------------------------------- | ---------------------------------------------------------------------------- |
82
- | [Vitest Unit Testing](vitest-unit.md) | AAA pattern, signal testing, mocking, coverage, performance, anti-patterns. |
83
- | [Storybook Interaction Testing](storybook-interaction.md) | CSF 3, play functions, a11y, visual regression, MSW, portable stories. |
84
- | [Playwright E2E Testing](playwright-e2e.md) | Page Objects, fixtures, auth, network mocking, CI/CD, flaky test prevention. |