0
# Storybook Test
1
2
The `@storybook/test` package provides instrumented testing utilities specifically designed for Storybook stories' play functions. It exports enhanced versions of popular testing libraries including @vitest/spy, @vitest/expect, @testing-library/dom, and @testing-library/user-event, with instrumentation that enables debugging capabilities in the addon-interactions panel.
3
4
## Package Information
5
6
- **Package Name**: @storybook/test
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install -D @storybook/test`
10
11
## Core Imports
12
13
```typescript
14
import { expect, fn, userEvent, within } from '@storybook/test';
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { expect, fn, userEvent, within } = require('@storybook/test');
21
```
22
23
## Basic Usage
24
25
```typescript
26
import { expect, fn, userEvent, within } from '@storybook/test';
27
import { Button } from './Button';
28
29
export default {
30
component: Button,
31
args: {
32
onClick: fn(),
33
},
34
};
35
36
export const Demo = {
37
play: async ({ args, canvasElement }) => {
38
const canvas = within(canvasElement);
39
await userEvent.click(canvas.getByRole('button'));
40
await expect(args.onClick).toHaveBeenCalled();
41
},
42
};
43
```
44
45
## Architecture
46
47
Storybook Test is built around several key components:
48
49
- **Instrumentation Layer**: All functions are wrapped with Storybook's instrumenter for addon-interactions debugging
50
- **Assertion Engine**: Enhanced expect function based on @vitest/expect and chai with testing-library matchers
51
- **Mock System**: Reactive spy and mock functions that integrate with Storybook's interaction tracking
52
- **DOM Testing**: Complete testing-library integration for DOM querying and user interaction simulation
53
- **Auto-Enhancement**: Automatic story argument processing to wrap actions in spies and enhance contexts
54
55
## Capabilities
56
57
### Assertions
58
59
Powerful assertion library based on @vitest/expect with testing-library DOM matchers and full chai compatibility.
60
61
```typescript { .api }
62
interface Expect extends AsymmetricMatchersContaining {
63
<T>(actual: T, message?: string): Assertion<T>;
64
unreachable(message?: string): Promise<never>;
65
soft<T>(actual: T, message?: string): Assertion<T>;
66
extend(expects: MatchersObject): void;
67
assertions(expected: number): Promise<void>;
68
hasAssertions(): Promise<void>;
69
anything(): any;
70
any(constructor: unknown): any;
71
getState(): MatcherState;
72
setState(state: Partial<MatcherState>): void;
73
not: AsymmetricMatchersContaining;
74
}
75
```
76
77
[Assertions](./assertions.md)
78
79
### Mocking and Spying
80
81
Mock functions and spies with reactive instrumentation for Storybook integration. Includes automatic cleanup and enhanced debugging.
82
83
```typescript { .api }
84
function fn<T extends Procedure = Procedure>(implementation?: T): Mock<T>;
85
function spyOn<T, K extends keyof T>(
86
object: T,
87
method: K
88
): MockInstance;
89
90
function clearAllMocks(): void;
91
function resetAllMocks(): void;
92
function restoreAllMocks(): void;
93
function onMockCall(callback: Listener): () => void;
94
```
95
96
[Mocking and Spying](./mocking.md)
97
98
### DOM Testing
99
100
Complete DOM testing utilities including queries, user interactions, and async utilities. All functions are instrumented for Storybook debugging.
101
102
```typescript { .api }
103
// Query functions (get, find, query variants)
104
function getByRole(container: HTMLElement, role: string, options?: ByRoleOptions): HTMLElement;
105
function findByText(container: HTMLElement, text: string, options?: SelectorMatcherOptions): Promise<HTMLElement>;
106
function queryByTestId(container: HTMLElement, testId: string, options?: MatcherOptions): HTMLElement | null;
107
108
// Scoping utility
109
function within(element: HTMLElement): BoundFunctions<typeof queries>;
110
111
// User interactions
112
interface UserEvent {
113
click(element: Element, options?: ClickOptions): Promise<void>;
114
type(element: Element, text: string, options?: TypeOptions): Promise<void>;
115
clear(element: Element): Promise<void>;
116
selectOptions(element: Element, values: string | string[]): Promise<void>;
117
upload(element: Element, file: File | File[]): Promise<void>;
118
}
119
120
// Async utilities
121
function waitFor<T>(callback: () => T | Promise<T>, options?: WaitForOptions): Promise<T>;
122
function waitForElementToBeRemoved(callback: () => Element | Element[]): Promise<void>;
123
```
124
125
[DOM Testing](./dom-testing.md)
126
127
## Types
128
129
### Core Types
130
131
```typescript { .api }
132
type Procedure = (...args: any[]) => any;
133
134
type MockV2<T extends Procedure> = MockInstance<Parameters<T>, ReturnType<T>> & T;
135
136
type Mock<T extends Procedure | any[] = any[], R = any> = T extends Procedure
137
? MockV2<T>
138
: T extends any[]
139
? MockV2<(...args: T) => R>
140
: never;
141
142
interface MockInstance<TArgs extends any[] = any[], TReturns = any> {
143
getMockName(): string;
144
mockName(name: string): this;
145
mockClear(): this;
146
mockReset(): this;
147
mockRestore(): void;
148
mockImplementation(fn?: (...args: TArgs) => TReturns): this;
149
mockImplementationOnce(fn: (...args: TArgs) => TReturns): this;
150
mockReturnValue(value: TReturns): this;
151
mockReturnValueOnce(value: TReturns): this;
152
mockResolvedValue(value: Awaited<TReturns>): this;
153
mockResolvedValueOnce(value: Awaited<TReturns>): this;
154
mockRejectedValue(value: any): this;
155
mockRejectedValueOnce(value: any): this;
156
}
157
158
// Jest-compatible assertion types
159
interface JestAssertion<T = any> {
160
toBe(expected: T): void;
161
toEqual(expected: T): void;
162
toStrictEqual(expected: T): void;
163
toContain(expected: any): void;
164
toHaveLength(expected: number): void;
165
// ... other Jest matchers
166
}
167
168
// Testing Library DOM matchers
169
interface TestingLibraryMatchers<R, T> {
170
toBeInTheDocument(): R;
171
toBeVisible(): R;
172
toBeEmpty(): R;
173
toBeDisabled(): R;
174
toBeEnabled(): R;
175
toBeRequired(): R;
176
toBeValid(): R;
177
toBeInvalid(): R;
178
toHaveAttribute(attr: string, value?: string): R;
179
toHaveClass(...classNames: string[]): R;
180
toHaveStyle(css: string | Record<string, any>): R;
181
toHaveTextContent(text: string | RegExp): R;
182
toHaveValue(value: string | string[] | number): R;
183
toHaveDisplayValue(value: string | RegExp | Array<string | RegExp>): R;
184
toBeChecked(): R;
185
toHaveFormValues(expectedValues: Record<string, any>): R;
186
toHaveFocus(): R;
187
toHaveAccessibleName(expectedAccessibleName?: string | RegExp): R;
188
toHaveAccessibleDescription(expectedAccessibleDescription?: string | RegExp): R;
189
toHaveErrorMessage(expectedErrorMessage?: string | RegExp): R;
190
}
191
192
// Expect static type
193
interface ExpectStatic {
194
<T>(actual: T): JestAssertion<T>;
195
stringContaining(expected: string): any;
196
arrayContaining<T = any>(expected: readonly T[]): any;
197
objectContaining(expected: Record<string, any>): any;
198
any(constructor: any): any;
199
anything(): any;
200
}
201
202
// Utility types
203
type Promisify<Fn> = Fn extends (...args: infer A) => infer R
204
? (...args: A) => R extends Promise<any> ? R : Promise<R>
205
: Fn;
206
207
type PromisifyObject<O> = { [K in keyof O]: Promisify<O[K]> };
208
209
interface Assertion<T> extends PromisifyObject<JestAssertion<T>>, TestingLibraryMatchers<ReturnType<ExpectStatic['stringContaining']>, Promise<void>> {
210
toHaveBeenCalledOnce(): Promise<void>;
211
toSatisfy<E>(matcher: (value: E) => boolean, message?: string): Promise<void>;
212
resolves: Assertion<T>;
213
rejects: Assertion<T>;
214
not: Assertion<T>;
215
}
216
217
type BoundFunctions<T> = {
218
[K in keyof T]: T[K] extends (...args: any[]) => any
219
? (...args: Parameters<T[K]>) => ReturnType<T[K]>
220
: T[K];
221
};
222
223
// DOM Testing option types
224
interface MatcherOptions {
225
exact?: boolean;
226
normalizer?: (text: string) => string;
227
}
228
229
interface SelectorMatcherOptions extends MatcherOptions {
230
selector?: string;
231
}
232
233
interface ByRoleOptions extends MatcherOptions {
234
checked?: boolean;
235
selected?: boolean;
236
expanded?: boolean;
237
pressed?: boolean;
238
level?: number;
239
name?: string | RegExp;
240
description?: string | RegExp;
241
}
242
243
interface WaitForOptions {
244
timeout?: number;
245
interval?: number;
246
onTimeout?: (error: Error) => Error;
247
mutationObserverOptions?: MutationObserverInit;
248
}
249
250
interface ClickOptions {
251
altKey?: boolean;
252
button?: number;
253
buttons?: number;
254
clientX?: number;
255
clientY?: number;
256
ctrlKey?: boolean;
257
detail?: number;
258
metaKey?: boolean;
259
relatedTarget?: Element | null;
260
screenX?: number;
261
screenY?: number;
262
shiftKey?: boolean;
263
}
264
265
interface TypeOptions {
266
delay?: number;
267
skipClick?: boolean;
268
skipAutoClose?: boolean;
269
initialSelectionStart?: number;
270
initialSelectionEnd?: number;
271
}
272
```
273
274
### Utility Types
275
276
```typescript { .api }
277
type MaybeMocked<T> = T & {
278
[K in keyof T]: T[K] extends (...args: any[]) => any
279
? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>
280
: T[K];
281
};
282
283
type MaybeMockedDeep<T> = T & {
284
[K in keyof T]: T[K] extends (...args: any[]) => any
285
? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>
286
: MaybeMockedDeep<T[K]>;
287
};
288
289
type MaybePartiallyMocked<T> = {
290
[K in keyof T]?: T[K] extends (...args: any[]) => any
291
? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>
292
: T[K];
293
};
294
295
type MaybePartiallyMockedDeep<T> = {
296
[K in keyof T]?: T[K] extends (...args: any[]) => any
297
? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>
298
: MaybePartiallyMockedDeep<T[K]>;
299
};
300
301
type Promisify<Fn> = Fn extends (...args: infer A) => infer R
302
? (...args: A) => R extends Promise<any> ? R : Promise<R>
303
: Fn;
304
305
type PromisifyObject<O> = { [K in keyof O]: Promisify<O[K]> };
306
```