claude-toolkit 0.1.12 → 0.1.20

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.
@@ -0,0 +1,455 @@
1
+ # Storybook Interaction Testing
2
+
3
+ > Sources: [Storybook 10 Blog](https://storybook.js.org/blog/storybook-10/), [Storybook 9.0 Release](https://storybook.js.org/releases/9.0), [Interaction Testing Docs](https://storybook.js.org/docs/writing-tests/interaction-testing), [storybook-solidjs-vite](https://www.npmjs.com/package/storybook-solidjs-vite), [Vitest Addon Docs](https://storybook.js.org/docs/writing-tests/integrations/vitest-addon)
4
+
5
+ Storybook serves as the middle layer of the testing pyramid: component-level interaction testing, visual regression, and accessibility auditing -- all in a real browser environment.
6
+
7
+ ## SolidJS Integration
8
+
9
+ The integration is community-maintained via two packages:
10
+
11
+ - **`storybook-solidjs`** -- the renderer
12
+ - **`storybook-solidjs-vite`** -- Vite builder integration (v10.0.x)
13
+
14
+ ### Critical: Decorator Reactivity
15
+
16
+ SolidJS components run once; Storybook re-executes decorators on every args/globals change. Standard decorators cause **duplicate DOM elements** with SolidJS.
17
+
18
+ Always use `createJSXDecorator` for JSX-returning decorators:
19
+
20
+ ```typescript
21
+ import { createJSXDecorator } from "storybook-solidjs";
22
+
23
+ // CORRECT -- prevents double-rendering
24
+ const ThemeDecorator = createJSXDecorator((Story, context) => (
25
+ <ThemeProvider>
26
+ <Story />
27
+ </ThemeProvider>
28
+ ));
29
+
30
+ export default {
31
+ decorators: [ThemeDecorator],
32
+ };
33
+ ```
34
+
35
+ ### CSF Format
36
+
37
+ Use **CSF 3** for SolidJS. CSF Factories (Storybook 10's new format) are React-only as of 10.3.
38
+
39
+ ## Writing Stories (CSF 3)
40
+
41
+ ```typescript
42
+ // Button.stories.tsx
43
+ import type { Meta, StoryObj } from "storybook-solidjs";
44
+ import { Button } from "./Button";
45
+
46
+ const meta = {
47
+ title: "Components/Button",
48
+ component: Button,
49
+ tags: ["autodocs"],
50
+ argTypes: {
51
+ variant: {
52
+ control: "select",
53
+ options: ["primary", "secondary", "ghost"],
54
+ },
55
+ size: {
56
+ control: "radio",
57
+ options: ["sm", "md", "lg"],
58
+ },
59
+ },
60
+ } satisfies Meta<typeof Button>;
61
+
62
+ export default meta;
63
+ type Story = StoryObj<typeof meta>;
64
+
65
+ export const Primary: Story = {
66
+ args: {
67
+ variant: "primary",
68
+ children: "Click me",
69
+ },
70
+ };
71
+
72
+ export const Loading: Story = {
73
+ args: {
74
+ variant: "primary",
75
+ loading: true,
76
+ children: "Submitting...",
77
+ },
78
+ };
79
+
80
+ export const Disabled: Story = {
81
+ args: {
82
+ variant: "primary",
83
+ disabled: true,
84
+ children: "Unavailable",
85
+ },
86
+ };
87
+ ```
88
+
89
+ ### Story Coverage Checklist
90
+
91
+ Every component should have stories covering:
92
+
93
+ - All visual states (default, hover, focus, active, disabled, loading, error, empty, success)
94
+ - Viewport sizes (mobile, tablet, desktop) where layout differs
95
+ - Theme variants (light/dark) if applicable
96
+ - Edge cases (long text, missing data, boundary values)
97
+
98
+ ## Interaction Testing with Play Functions
99
+
100
+ Play functions turn stories into executable tests. Import everything from `@storybook/test` (instrumented versions of Testing Library + Vitest utilities):
101
+
102
+ ```typescript
103
+ import { expect, fn, userEvent, within, waitFor } from "@storybook/test";
104
+
105
+ export const SubmitForm: Story = {
106
+ args: {
107
+ onSubmit: fn(), // Storybook-instrumented spy
108
+ },
109
+ play: async ({ canvasElement, args, step }) => {
110
+ const canvas = within(canvasElement);
111
+
112
+ await step("Fill in the form", async () => {
113
+ await userEvent.type(canvas.getByLabelText("Email"), "user@example.com");
114
+ await userEvent.type(canvas.getByLabelText("Password"), "secret123");
115
+ });
116
+
117
+ await step("Submit the form", async () => {
118
+ await userEvent.click(canvas.getByRole("button", { name: "Sign in" }));
119
+ });
120
+
121
+ await step("Verify submission", async () => {
122
+ await expect(args.onSubmit).toHaveBeenCalledOnce();
123
+ await expect(args.onSubmit).toHaveBeenCalledWith({
124
+ email: "user@example.com",
125
+ password: "secret123",
126
+ });
127
+ });
128
+ },
129
+ };
130
+ ```
131
+
132
+ ### Key Rules
133
+
134
+ 1. **Always `await` expect calls.** This enables proper logging in the Interactions panel.
135
+ 2. **Use `step()` to organize** complex interactions into labeled groups.
136
+ 3. **Use `fn()` for spying.** Storybook auto-restores mocks between stories.
137
+ 4. **Never import from `@testing-library/dom` directly.** Use `@storybook/test` for instrumented versions.
138
+
139
+ ## Running Stories as Tests in CI
140
+
141
+ The **Vitest addon** (`@storybook/addon-vitest`) replaces the old `@storybook/test-runner`. It transforms stories into real Vitest tests via a plugin, running in browser mode with Playwright.
142
+
143
+ ### Advantages Over the Old Test Runner
144
+
145
+ - Does not need a running Storybook instance
146
+ - Uses Vitest browser mode (real Chromium) for accurate rendering
147
+ - Results appear alongside your Vitest unit tests
148
+ - Faster execution, simpler architecture
149
+
150
+ ### Configuration (Vitest 4.x)
151
+
152
+ Required dependencies: `@storybook/addon-vitest`, `@vitest/browser`, `@vitest/browser-playwright`, `@vitest/coverage-v8`.
153
+
154
+ Add as an inline project in your main Vite config using `test.projects`:
155
+
156
+ ```typescript
157
+ // vite.config.ts
158
+ import { storybookTest } from "@storybook/addon-vitest/vitest-plugin";
159
+ import { playwright } from "@vitest/browser-playwright";
160
+
161
+ export default defineConfig({
162
+ plugins: [/* ...app plugins */],
163
+ test: {
164
+ projects: [
165
+ {
166
+ extends: true,
167
+ test: { name: "unit", environment: "jsdom", setupFiles: ["./tests/setup.ts"] },
168
+ },
169
+ {
170
+ extends: true,
171
+ plugins: [storybookTest({ configDir: ".storybook" })],
172
+ test: {
173
+ name: "storybook",
174
+ browser: { enabled: true, headless: true, provider: playwright(), instances: [{ browser: "chromium" }] },
175
+ setupFiles: [".storybook/vitest.setup.ts"],
176
+ },
177
+ },
178
+ ],
179
+ },
180
+ });
181
+ ```
182
+
183
+ ```json
184
+ {
185
+ "scripts": {
186
+ "test:storybook": "vitest --project storybook"
187
+ }
188
+ }
189
+ ```
190
+
191
+ ## Accessibility Testing
192
+
193
+ Storybook's a11y integration (built on axe-core) catches ~57% of WCAG issues automatically.
194
+
195
+ ### Global Configuration
196
+
197
+ ```typescript
198
+ // .storybook/preview.ts
199
+ export default {
200
+ parameters: {
201
+ a11y: {
202
+ test: "error", // "off" | "todo" | "error"
203
+ },
204
+ },
205
+ tags: ["a11y-test"],
206
+ };
207
+ ```
208
+
209
+ - **`"error"`**: Fail in CI on any violation.
210
+ - **`"todo"`**: Log violations without failing (for gradual adoption).
211
+ - **`"off"`**: Disable for specific stories.
212
+
213
+ ### Per-Story Overrides
214
+
215
+ ```typescript
216
+ export const CustomA11y: Story = {
217
+ parameters: {
218
+ a11y: {
219
+ config: {
220
+ rules: [
221
+ { id: "color-contrast", enabled: true },
222
+ ],
223
+ },
224
+ },
225
+ },
226
+ };
227
+ ```
228
+
229
+ ## Visual Regression Testing
230
+
231
+ ### Chromatic (by Storybook Team)
232
+
233
+ - Cloud service that captures screenshots of every story
234
+ - Pixel-perfect diffing across Chrome, Firefox, Safari, Edge
235
+ - Manages baselines across branches and team members
236
+ - Free tier available; paid for scale
237
+
238
+ ### Integration
239
+
240
+ ```bash
241
+ bunx chromatic --project-token=<token>
242
+ ```
243
+
244
+ Add to CI as a quality gate after `test:storybook` passes.
245
+
246
+ ## MSW for API-Dependent Components
247
+
248
+ ### Global Setup
249
+
250
+ ```typescript
251
+ // .storybook/preview.ts
252
+ import { initialize, mswLoader } from "msw-storybook-addon";
253
+
254
+ initialize();
255
+
256
+ export default {
257
+ loaders: [mswLoader],
258
+ };
259
+ ```
260
+
261
+ ### Per-Story Handlers
262
+
263
+ ```typescript
264
+ import { http, HttpResponse } from "msw";
265
+
266
+ export const WithUserData: Story = {
267
+ parameters: {
268
+ msw: {
269
+ handlers: [
270
+ http.get("/api/user", () =>
271
+ HttpResponse.json({ name: "Alice", email: "alice@example.com" })
272
+ ),
273
+ ],
274
+ },
275
+ },
276
+ };
277
+
278
+ export const WithError: Story = {
279
+ parameters: {
280
+ msw: {
281
+ handlers: [
282
+ http.get("/api/user", () =>
283
+ new HttpResponse(null, { status: 500 })
284
+ ),
285
+ ],
286
+ },
287
+ },
288
+ };
289
+ ```
290
+
291
+ ### Best Practices
292
+
293
+ - Keep "success" handlers in a shared `mocks/handlers.ts` module
294
+ - Override with error/edge-case handlers at the story level
295
+ - Configure MSW to error on unhandled requests
296
+
297
+ ### Module Mocking (Storybook 10)
298
+
299
+ Storybook 10 introduced `sb.mock` for module-level mocking (services, utilities). Register in `.storybook/preview.ts` only:
300
+
301
+ ```typescript
302
+ // .storybook/preview.ts
303
+ import { sb } from "@storybook/test";
304
+
305
+ sb.mock("../src/api/client", () => ({
306
+ fetchUser: async () => ({ name: "Mock User" }),
307
+ }));
308
+ ```
309
+
310
+ MSW remains the right tool for network-level mocking; `sb.mock` is for internal module replacement.
311
+
312
+ ## Portable Stories
313
+
314
+ Reuse Storybook stories in your existing Vitest tests with `composeStories`:
315
+
316
+ ```typescript
317
+ // Button.test.tsx
318
+ import { composeStories } from "storybook-solidjs";
319
+ import * as stories from "./Button.stories";
320
+ import { render } from "@solidjs/testing-library";
321
+
322
+ const { Primary, Loading, Disabled } = composeStories(stories);
323
+
324
+ test("renders primary button", () => {
325
+ const { getByRole } = render(() => <Primary />);
326
+ expect(getByRole("button")).toBeInTheDocument();
327
+ });
328
+
329
+ test("can override args in test", () => {
330
+ const { getByRole } = render(() => <Primary children="Override" />);
331
+ expect(getByRole("button")).toHaveTextContent("Override");
332
+ });
333
+ ```
334
+
335
+ This preserves all story annotations (args, decorators, loaders, play functions) in unit tests.
336
+
337
+ ## vanilla-extract Integration
338
+
339
+ **Zero additional configuration needed.** Storybook uses your `vite.config.ts`, which already includes `@vanilla-extract/vite-plugin`. Styles compile automatically.
340
+
341
+ ### Theme Decorator
342
+
343
+ ```typescript
344
+ // .storybook/preview.ts
345
+ import { createJSXDecorator } from "storybook-solidjs";
346
+ import "../src/styles/global.css";
347
+
348
+ const withTheme = createJSXDecorator((Story) => (
349
+ <div class={themeClass}>
350
+ <Story />
351
+ </div>
352
+ ));
353
+
354
+ export default {
355
+ decorators: [withTheme],
356
+ };
357
+ ```
358
+
359
+ ## Organization at Scale
360
+
361
+ ### File Structure
362
+
363
+ Organize by feature/domain, not by component type:
364
+
365
+ ```
366
+ src/
367
+ features/
368
+ auth/
369
+ LoginForm.tsx
370
+ LoginForm.css.ts
371
+ LoginForm.stories.tsx # Co-located
372
+ LoginForm.test.tsx # Unit tests alongside
373
+ components/
374
+ Button/
375
+ Button.tsx
376
+ Button.css.ts
377
+ Button.stories.tsx
378
+ ```
379
+
380
+ ### Tags for Filtering
381
+
382
+ Use tags to control which stories run in different contexts:
383
+
384
+ ```typescript
385
+ const meta = {
386
+ tags: ["autodocs", "a11y-test", "interaction-test"],
387
+ // ...
388
+ } satisfies Meta<typeof Component>;
389
+ ```
390
+
391
+ ### Storybook Main Config
392
+
393
+ ```typescript
394
+ // .storybook/main.ts
395
+ import type { StorybookConfig } from "storybook-solidjs-vite";
396
+
397
+ const config: StorybookConfig = {
398
+ stories: ["../src/**/*.stories.@(ts|tsx)"],
399
+ framework: "storybook-solidjs-vite",
400
+ addons: [
401
+ "@storybook/addon-essentials",
402
+ "@storybook/addon-a11y",
403
+ "@storybook/addon-vitest",
404
+ ],
405
+ };
406
+
407
+ export default config;
408
+ ```
409
+
410
+ ## Component Documentation
411
+
412
+ ### Autodocs
413
+
414
+ Enable globally or per-component:
415
+
416
+ ```typescript
417
+ const meta = {
418
+ tags: ["autodocs"],
419
+ // ...
420
+ } satisfies Meta<typeof Button>;
421
+ ```
422
+
423
+ Generates prop tables from TypeScript interfaces. Add JSDoc comments on props interfaces for richer docs.
424
+
425
+ ### MDX for Custom Docs
426
+
427
+ ```mdx
428
+ {/* Button.mdx */}
429
+ import { Meta, Canvas, Controls } from "@storybook/blocks";
430
+ import * as ButtonStories from "./Button.stories";
431
+
432
+ <Meta of={ButtonStories} />
433
+
434
+ # Button
435
+
436
+ Our primary interaction element.
437
+
438
+ <Canvas of={ButtonStories.Primary} />
439
+ <Controls of={ButtonStories.Primary} />
440
+ ```
441
+
442
+ ## Anti-Patterns
443
+
444
+ | Anti-Pattern | Fix |
445
+ |---|---|
446
+ | Destructuring SolidJS props in stories | Pass props via `args`; use `createJSXDecorator` for decorators |
447
+ | Importing from `@testing-library/dom` directly | Import from `@storybook/test` for instrumented versions |
448
+ | Not awaiting `expect()` in play functions | Always `await expect(...)` |
449
+ | Hardcoding test data in stories | Use `args` and story composition |
450
+ | Giant monolithic story files | One story file per component |
451
+ | Skipping error/edge-case stories | Always include loading, error, empty, boundary states |
452
+ | Using `@storybook/test-runner` with Vite | Use `@storybook/addon-vitest` instead |
453
+ | Not resolving unhandled MSW requests | Configure MSW to error on unhandled requests |
454
+ | Using inline styles in stories | Use theme tokens and `.css.ts` files |
455
+ | Registering `sb.mock` in story files | Register in `.storybook/preview.ts` only |