0
# Assertions
1
2
Powerful assertion library based on @vitest/expect with testing-library DOM matchers and full chai compatibility. The expect function is instrumented for Storybook's addon-interactions debugging.
3
4
## Capabilities
5
6
### Expect Function
7
8
The main assertion function that supports jest-compatible API with additional testing-library matchers.
9
10
```typescript { .api }
11
/**
12
* Create an assertion for the given value
13
* @param actual - The value to assert against
14
* @param message - Optional error message
15
* @returns Assertion object with chainable matchers
16
*/
17
function expect<T>(actual: T, message?: string): Assertion<T>;
18
19
interface Expect extends AsymmetricMatchersContaining {
20
<T>(actual: T, message?: string): Assertion<T>;
21
unreachable(message?: string): Promise<never>;
22
soft<T>(actual: T, message?: string): Assertion<T>;
23
extend(expects: MatchersObject): void;
24
assertions(expected: number): Promise<void>;
25
hasAssertions(): Promise<void>;
26
anything(): any;
27
any(constructor: unknown): any;
28
getState(): MatcherState;
29
setState(state: Partial<MatcherState>): void;
30
not: AsymmetricMatchersContaining;
31
}
32
```
33
34
**Usage Examples:**
35
36
```typescript
37
import { expect, fn } from '@storybook/test';
38
39
// Basic assertions
40
expect(42).toBe(42);
41
expect('hello').toEqual('hello');
42
expect([1, 2, 3]).toContain(2);
43
44
// Mock function assertions
45
const mockFn = fn();
46
mockFn('arg1', 'arg2');
47
expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2');
48
expect(mockFn).toHaveBeenCalledOnce();
49
50
// Async assertions
51
await expect(Promise.resolve('value')).resolves.toBe('value');
52
await expect(Promise.reject('error')).rejects.toBe('error');
53
54
// DOM assertions (with testing-library matchers)
55
const button = document.createElement('button');
56
button.textContent = 'Click me';
57
button.disabled = false;
58
expect(button).toBeInTheDocument();
59
expect(button).toHaveTextContent('Click me');
60
expect(button).toBeEnabled();
61
```
62
63
### Assertion Interface
64
65
The assertion interface provides all jest-compatible matchers plus testing-library DOM matchers.
66
67
```typescript { .api }
68
interface Assertion<T> extends PromisifyObject<JestAssertion<T>>, TestingLibraryMatchers<ReturnType<ExpectStatic['stringContaining']>, Promise<void>> {
69
// Standard jest matchers
70
toBe(expected: T): Promise<void>;
71
toEqual(expected: T): Promise<void>;
72
toStrictEqual(expected: T): Promise<void>;
73
toContain(expected: any): Promise<void>;
74
toContainEqual(expected: any): Promise<void>;
75
toHaveLength(expected: number): Promise<void>;
76
toMatch(expected: string | RegExp): Promise<void>;
77
toMatchObject(expected: Record<string, any>): Promise<void>;
78
toThrow(expected?: string | RegExp | Error): Promise<void>;
79
toThrowError(expected?: string | RegExp | Error): Promise<void>;
80
81
// Mock-specific matchers
82
toHaveBeenCalled(): Promise<void>;
83
toHaveBeenCalledOnce(): Promise<void>;
84
toHaveBeenCalledTimes(expected: number): Promise<void>;
85
toHaveBeenCalledWith(...expected: any[]): Promise<void>;
86
toHaveBeenLastCalledWith(...expected: any[]): Promise<void>;
87
toHaveBeenNthCalledWith(nth: number, ...expected: any[]): Promise<void>;
88
toHaveReturned(): Promise<void>;
89
toHaveReturnedTimes(expected: number): Promise<void>;
90
toHaveReturnedWith(expected: any): Promise<void>;
91
toHaveLastReturnedWith(expected: any): Promise<void>;
92
toHaveNthReturnedWith(nth: number, expected: any): Promise<void>;
93
94
// Testing Library DOM matchers
95
toBeInTheDocument(): Promise<void>;
96
toBeVisible(): Promise<void>;
97
toBeEmptyDOMElement(): Promise<void>;
98
toBeDisabled(): Promise<void>;
99
toBeEnabled(): Promise<void>;
100
toBeInvalid(): Promise<void>;
101
toBeRequired(): Promise<void>;
102
toBeValid(): Promise<void>;
103
toBeChecked(): Promise<void>;
104
toBePartiallyChecked(): Promise<void>;
105
toHaveAccessibleDescription(expected?: string | RegExp): Promise<void>;
106
toHaveAccessibleName(expected?: string | RegExp): Promise<void>;
107
toHaveAttribute(attribute: string, expected?: string | RegExp): Promise<void>;
108
toHaveClass(...expected: string[]): Promise<void>;
109
toHaveFocus(): Promise<void>;
110
toHaveFormValues(expected: Record<string, any>): Promise<void>;
111
toHaveStyle(expected: string | Record<string, any>): Promise<void>;
112
toHaveTextContent(expected?: string | RegExp): Promise<void>;
113
toHaveValue(expected?: string | string[] | number): Promise<void>;
114
toHaveDisplayValue(expected: string | RegExp | string[] | RegExp[]): Promise<void>;
115
116
// Additional matchers
117
toSatisfy<E>(matcher: (value: E) => boolean, message?: string): Promise<void>;
118
119
// Async assertion helpers
120
resolves: Assertion<T>;
121
rejects: Assertion<T>;
122
123
// Negation
124
not: Assertion<T>;
125
}
126
```
127
128
### Soft Assertions
129
130
Create soft assertions that don't immediately fail the test but collect errors.
131
132
```typescript { .api }
133
/**
134
* Create a soft assertion that collects errors instead of immediately failing
135
* @param actual - The value to assert against
136
* @param message - Optional error message
137
* @returns Assertion object for soft testing
138
*/
139
function soft<T>(actual: T, message?: string): Assertion<T>;
140
```
141
142
**Usage Example:**
143
144
```typescript
145
import { expect } from '@storybook/test';
146
147
// Soft assertions collect errors without stopping execution
148
expect.soft(1).toBe(2); // This won't throw immediately
149
expect.soft(2).toBe(3); // This won't throw immediately
150
expect.soft(3).toBe(3); // This passes
151
152
// All collected soft assertion errors will be reported at the end
153
```
154
155
### Extending Expect
156
157
Add custom matchers to the expect function.
158
159
```typescript { .api }
160
/**
161
* Extend expect with custom matchers
162
* @param expects - Object containing custom matcher functions
163
*/
164
function extend(expects: MatchersObject): void;
165
166
interface MatchersObject {
167
[key: string]: (this: MatcherState, actual: any, ...expected: any[]) => MatcherResult;
168
}
169
170
interface MatcherResult {
171
message: () => string;
172
pass: boolean;
173
}
174
175
interface MatcherState {
176
isNot: boolean;
177
promise: string;
178
assertEquals: (actual: any, expected: any, message?: string) => void;
179
assertType: (value: any, type: string, message?: string) => void;
180
}
181
```
182
183
**Usage Example:**
184
185
```typescript
186
import { expect } from '@storybook/test';
187
188
expect.extend({
189
toBeWithinRange(received, floor, ceiling) {
190
const pass = received >= floor && received <= ceiling;
191
if (pass) {
192
return {
193
message: () => `expected ${received} not to be within range ${floor} - ${ceiling}`,
194
pass: true,
195
};
196
} else {
197
return {
198
message: () => `expected ${received} to be within range ${floor} - ${ceiling}`,
199
pass: false,
200
};
201
}
202
},
203
});
204
205
// Usage
206
expect(100).toBeWithinRange(90, 110);
207
```
208
209
### Asymmetric Matchers
210
211
Create asymmetric matchers for flexible assertions.
212
213
```typescript { .api }
214
/**
215
* Match any value of the given constructor type
216
* @param constructor - Constructor function to match against
217
* @returns Asymmetric matcher
218
*/
219
function any(constructor: unknown): any;
220
221
/**
222
* Match any truthy value
223
* @returns Asymmetric matcher
224
*/
225
function anything(): any;
226
```
227
228
**Usage Examples:**
229
230
```typescript
231
import { expect } from '@storybook/test';
232
233
// Asymmetric matchers for flexible matching
234
expect({ name: 'John', age: 30 }).toEqual({
235
name: expect.any(String),
236
age: expect.any(Number),
237
});
238
239
expect(['apple', 'banana']).toEqual([
240
expect.anything(),
241
expect.anything(),
242
]);
243
```
244
245
### Assertion Count Validation
246
247
Validate the number of assertions executed during a test.
248
249
```typescript { .api }
250
/**
251
* Expect exactly N assertions to be called during the test
252
* @param expected - Expected number of assertions
253
*/
254
function assertions(expected: number): Promise<void>;
255
256
/**
257
* Expect at least one assertion to be called during the test
258
*/
259
function hasAssertions(): Promise<void>;
260
```
261
262
**Usage Examples:**
263
264
```typescript
265
import { expect } from '@storybook/test';
266
267
export const TestStory = {
268
play: async () => {
269
expect.assertions(2); // Expect exactly 2 assertions
270
271
expect(1).toBe(1);
272
expect(2).toBe(2);
273
// Test will fail if not exactly 2 assertions are made
274
},
275
};
276
277
export const AnotherStory = {
278
play: async () => {
279
expect.hasAssertions(); // Expect at least one assertion
280
281
const condition = Math.random() > 0.5;
282
if (condition) {
283
expect(true).toBe(true);
284
}
285
// Test will fail if no assertions are made
286
},
287
};
288
```
289
290
### Unreachable Code
291
292
Mark code paths that should never be reached.
293
294
```typescript { .api }
295
/**
296
* Mark a code path as unreachable - will always fail if executed
297
* @param message - Optional error message
298
* @throws Always throws an error
299
*/
300
function unreachable(message?: string): Promise<never>;
301
```
302
303
**Usage Example:**
304
305
```typescript
306
import { expect } from '@storybook/test';
307
308
export const TestStory = {
309
play: async () => {
310
const value = 'valid';
311
312
switch (value) {
313
case 'valid':
314
expect(true).toBe(true);
315
break;
316
default:
317
expect.unreachable('Should never reach default case');
318
}
319
},
320
};
321
```