or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions.mdcli-commands.mdframework-support.mdhighlighting.mdindex.mdmanager-api.mdstory-composition.mdtesting.mdtheming.mdviewport.md

testing.mddocs/

0

# Testing and Mocking

1

2

Comprehensive testing utilities built on top of popular testing libraries with Storybook-specific instrumentation and integrations. Provides enhanced expect functionality, user interaction utilities, and module mocking capabilities.

3

4

## Capabilities

5

6

### Enhanced Expect

7

8

Storybook's expect is based on Chai with additional instrumentation for better integration with Storybook's testing ecosystem.

9

10

```typescript { .api }

11

/**

12

* Enhanced expect function with Storybook instrumentation

13

* Provides assertion capabilities with detailed error reporting

14

*/

15

const expect: Expect;

16

17

interface Expect {

18

<T>(actual: T): ExpectStatic<T>;

19

/** Check if value is null */

20

(actual: null): ExpectStatic<null>;

21

/** Check if value is undefined */

22

(actual: undefined): ExpectStatic<undefined>;

23

/** Use with spy assertions */

24

<T extends (...args: any[]) => any>(actual: T): ExpectStatic<T>;

25

}

26

27

interface ExpectStatic<T> {

28

/** Negates the assertion */

29

not: ExpectStatic<T>;

30

/** Basic equality assertion */

31

toBe(expected: T): void;

32

/** Deep equality assertion */

33

toEqual(expected: T): void;

34

/** Truthiness assertion */

35

toBeTruthy(): void;

36

/** Falsiness assertion */

37

toBeFalsy(): void;

38

/** Null assertion */

39

toBeNull(): void;

40

/** Undefined assertion */

41

toBeUndefined(): void;

42

/** Array/string contains assertion */

43

toContain(expected: any): void;

44

/** Function call assertion */

45

toHaveBeenCalled(): void;

46

/** Function call count assertion */

47

toHaveBeenCalledTimes(times: number): void;

48

/** Function call arguments assertion */

49

toHaveBeenCalledWith(...args: any[]): void;

50

// ... additional matchers

51

}

52

```

53

54

**Usage Example:**

55

56

```typescript

57

import { expect, userEvent, within } from "storybook/test";

58

import { composeStory } from "storybook/preview-api";

59

import { Primary } from "./Button.stories";

60

61

const ComposedButton = composeStory(Primary, ButtonMeta);

62

63

test("button interaction", async () => {

64

const onClickSpy = vi.fn();

65

render(<ComposedButton onClick={onClickSpy} />);

66

67

const button = screen.getByRole("button");

68

await userEvent.click(button);

69

70

expect(onClickSpy).toHaveBeenCalledOnce();

71

expect(button).toBeInTheDocument();

72

});

73

```

74

75

### User Event Utilities

76

77

User interaction testing utilities for simulating user actions with proper async handling and event propagation.

78

79

```typescript { .api }

80

/**

81

* User event utilities for simulating user interactions

82

* Provides type-safe, async user interaction methods

83

*/

84

const userEvent: UserEvent;

85

86

interface UserEvent {

87

/** Setup user event with custom configuration */

88

setup(options?: UserEventOptions): UserEventObject;

89

/** Click an element */

90

click(element: Element, options?: ClickOptions): Promise<void>;

91

/** Double click an element */

92

dblClick(element: Element, options?: ClickOptions): Promise<void>;

93

/** Type text into an element */

94

type(element: Element, text: string, options?: TypeOptions): Promise<void>;

95

/** Clear and type text */

96

clear(element: Element): Promise<void>;

97

/** Select text in an input */

98

selectAll(element: Element): Promise<void>;

99

/** Tab to next/previous element */

100

tab(options?: TabOptions): Promise<void>;

101

/** Hover over an element */

102

hover(element: Element): Promise<void>;

103

/** Stop hovering over an element */

104

unhover(element: Element): Promise<void>;

105

/** Upload files to file input */

106

upload(element: Element, files: File | File[]): Promise<void>;

107

}

108

109

interface UserEventOptions {

110

/** Delay between events in milliseconds */

111

delay?: number;

112

/** Skip pointer events */

113

skipPointerEventsCheck?: boolean;

114

/** Skip hover events */

115

skipHover?: boolean;

116

}

117

118

interface ClickOptions {

119

button?: 'left' | 'right' | 'middle';

120

ctrlKey?: boolean;

121

shiftKey?: boolean;

122

altKey?: boolean;

123

metaKey?: boolean;

124

}

125

126

interface TypeOptions {

127

delay?: number;

128

skipClick?: boolean;

129

initialSelectionStart?: number;

130

initialSelectionEnd?: number;

131

}

132

```

133

134

**Usage Example:**

135

136

```typescript

137

import { userEvent, within, expect } from "storybook/test";

138

139

export const InteractiveForm: Story = {

140

play: async ({ canvasElement }) => {

141

const canvas = within(canvasElement);

142

143

// Type in form fields

144

await userEvent.type(canvas.getByLabelText(/name/i), "John Doe");

145

await userEvent.type(canvas.getByLabelText(/email/i), "john@example.com");

146

147

// Select dropdown option

148

await userEvent.selectOptions(

149

canvas.getByLabelText(/country/i),

150

["usa"]

151

);

152

153

// Upload file

154

const file = new File(["avatar"], "avatar.png", { type: "image/png" });

155

await userEvent.upload(canvas.getByLabelText(/avatar/i), file);

156

157

// Submit form

158

await userEvent.click(canvas.getByRole("button", { name: /submit/i }));

159

160

// Verify results

161

expect(canvas.getByText(/success/i)).toBeInTheDocument();

162

},

163

};

164

```

165

166

### Module Mocking

167

168

Module mocking utilities for replacing imports and dependencies in tests and stories.

169

170

```typescript { .api }

171

/**

172

* Storybook's module mocking utilities

173

*/

174

interface MockUtilities {

175

/**

176

* Mock a module with a factory function or mock implementation

177

* @param path - Module path to mock (string or Promise for dynamic imports)

178

* @param factory - Optional factory function to create mock implementation

179

*/

180

mock(path: string | Promise<unknown>, factory?: ModuleMockOptions): void;

181

}

182

183

const sb: MockUtilities;

184

185

type ModuleMockOptions =

186

| (() => any)

187

| { [key: string]: any }

188

| any;

189

```

190

191

**Usage Example:**

192

193

```typescript

194

import { sb } from "storybook/test";

195

196

// Mock an API module

197

sb.mock("./api/userService", () => ({

198

fetchUser: vi.fn().mockResolvedValue({

199

id: 1,

200

name: "John Doe",

201

email: "john@example.com"

202

}),

203

updateUser: vi.fn().mockResolvedValue({ success: true }),

204

}));

205

206

// Mock with partial implementation

207

sb.mock("./utils/analytics", () => ({

208

track: vi.fn(),

209

identify: vi.fn(),

210

// Keep other exports as default

211

...vi.importActual("./utils/analytics"),

212

}));

213

214

// Use in story

215

export const ComponentWithMockedAPI: Story = {

216

play: async ({ canvasElement }) => {

217

const canvas = within(canvasElement);

218

219

// Component will use mocked userService

220

await userEvent.click(canvas.getByText("Load User"));

221

222

// Verify mock was called

223

const { fetchUser } = await import("./api/userService");

224

expect(fetchUser).toHaveBeenCalledWith(1);

225

},

226

};

227

```

228

229

## Testing Library Integration

230

231

Complete instrumented re-export of `@testing-library/dom` with Storybook-specific enhancements.

232

233

### Query Functions

234

235

```typescript { .api }

236

// Get* queries - throw error if not found

237

function getByRole(container: Element, role: string, options?: ByRoleOptions): HTMLElement;

238

function getByLabelText(container: Element, text: string, options?: SelectorMatcherOptions): HTMLElement;

239

function getByText(container: Element, text: string, options?: SelectorMatcherOptions): HTMLElement;

240

function getByDisplayValue(container: Element, value: string, options?: SelectorMatcherOptions): HTMLElement;

241

function getByAltText(container: Element, text: string, options?: SelectorMatcherOptions): HTMLElement;

242

function getByTitle(container: Element, text: string, options?: SelectorMatcherOptions): HTMLElement;

243

function getByTestId(container: Element, testId: string, options?: SelectorMatcherOptions): HTMLElement;

244

245

// GetAll* queries - return array, throw if none found

246

function getAllByRole(container: Element, role: string, options?: ByRoleOptions): HTMLElement[];

247

// ... similar pattern for other queries

248

249

// Query* queries - return null if not found

250

function queryByRole(container: Element, role: string, options?: ByRoleOptions): HTMLElement | null;

251

// ... similar pattern for other queries

252

253

// QueryAll* queries - return empty array if none found

254

function queryAllByRole(container: Element, role: string, options?: ByRoleOptions): HTMLElement[];

255

// ... similar pattern for other queries

256

257

// Find* queries - return promise, reject if not found after timeout

258

function findByRole(container: Element, role: string, options?: ByRoleOptions): Promise<HTMLElement>;

259

// ... similar pattern for other queries

260

261

// FindAll* queries - return promise of array

262

function findAllByRole(container: Element, role: string, options?: ByRoleOptions): Promise<HTMLElement[]>;

263

// ... similar pattern for other queries

264

```

265

266

### Event Utilities

267

268

```typescript { .api }

269

/**

270

* Fire DOM events on elements

271

*/

272

const fireEvent: {

273

[K in keyof HTMLElementEventMap]: (

274

element: Element,

275

eventProperties?: Partial<HTMLElementEventMap[K]>

276

) => boolean;

277

} & {

278

/** Create a DOM event */

279

createEvent: (eventName: string, node?: Element, init?: object) => Event;

280

};

281

```

282

283

### Async Utilities

284

285

```typescript { .api }

286

/**

287

* Wait for element to appear or condition to be met

288

* @param callback - Function to execute and wait for

289

* @param options - Configuration options

290

*/

291

function waitFor<T>(

292

callback: () => T | Promise<T>,

293

options?: WaitForOptions

294

): Promise<T>;

295

296

/**

297

* Wait for element to be removed from DOM

298

* @param element - Element to wait for removal

299

* @param options - Configuration options

300

*/

301

function waitForElementToBeRemoved<T>(

302

element: T,

303

options?: WaitForOptions

304

): Promise<void>;

305

306

interface WaitForOptions {

307

timeout?: number;

308

interval?: number;

309

onTimeout?: (error: Error) => Error;

310

}

311

```

312

313

### Screen and Within

314

315

```typescript { .api }

316

/**

317

* Global screen object for querying document

318

*/

319

const screen: Screen;

320

321

interface Screen {

322

getByRole: (role: string, options?: ByRoleOptions) => HTMLElement;

323

getAllByRole: (role: string, options?: ByRoleOptions) => HTMLElement[];

324

queryByRole: (role: string, options?: ByRoleOptions) => HTMLElement | null;

325

// ... all query methods available on screen

326

}

327

328

/**

329

* Scope queries to a specific container element

330

* @param element - Container element to scope queries to

331

* @returns Object with all query methods scoped to the container

332

*/

333

function within(element: Element): Screen;

334

```

335

336

**Usage Example:**

337

338

```typescript

339

import { screen, within, waitFor, fireEvent } from "storybook/test";

340

341

export const AsyncComponent: Story = {

342

play: async ({ canvasElement }) => {

343

const canvas = within(canvasElement);

344

345

// Click button to trigger async action

346

fireEvent.click(canvas.getByRole("button", { name: /load data/i }));

347

348

// Wait for loading to complete

349

await waitFor(() => {

350

expect(canvas.queryByText(/loading/i)).not.toBeInTheDocument();

351

});

352

353

// Verify data loaded

354

expect(canvas.getByText(/data loaded successfully/i)).toBeInTheDocument();

355

},

356

};

357

```

358

359

## Configuration and Setup

360

361

### Test Parameters

362

363

Configure testing behavior at story or project level.

364

365

```typescript { .api }

366

interface TestParameters {

367

/** Disable testing for this story */

368

disable?: boolean;

369

/** Custom test timeout */

370

timeout?: number;

371

/** Skip certain test assertions */

372

skip?: string[];

373

}

374

```

375

376

**Usage Example:**

377

378

```typescript

379

export const LongRunningTest: Story = {

380

parameters: {

381

test: {

382

timeout: 10000, // 10 second timeout

383

},

384

},

385

play: async ({ canvasElement }) => {

386

// Long-running test logic

387

},

388

};

389

```