or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

assertions.mddom-testing.mdindex.mdmocking.md
tile.json

tessl/npm-storybook--test

Utilities for testing your stories inside play functions

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@storybook/test@8.6.x

To install, run

npx @tessl/cli install tessl/npm-storybook--test@8.6.0

index.mddocs/

Storybook Test

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.

Package Information

  • Package Name: @storybook/test
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install -D @storybook/test

Core Imports

import { expect, fn, userEvent, within } from '@storybook/test';

For CommonJS:

const { expect, fn, userEvent, within } = require('@storybook/test');

Basic Usage

import { expect, fn, userEvent, within } from '@storybook/test';
import { Button } from './Button';

export default {
  component: Button,
  args: {
    onClick: fn(),
  },
};

export const Demo = {
  play: async ({ args, canvasElement }) => {
    const canvas = within(canvasElement);
    await userEvent.click(canvas.getByRole('button'));
    await expect(args.onClick).toHaveBeenCalled();
  },
};

Architecture

Storybook Test is built around several key components:

  • Instrumentation Layer: All functions are wrapped with Storybook's instrumenter for addon-interactions debugging
  • Assertion Engine: Enhanced expect function based on @vitest/expect and chai with testing-library matchers
  • Mock System: Reactive spy and mock functions that integrate with Storybook's interaction tracking
  • DOM Testing: Complete testing-library integration for DOM querying and user interaction simulation
  • Auto-Enhancement: Automatic story argument processing to wrap actions in spies and enhance contexts

Capabilities

Assertions

Powerful assertion library based on @vitest/expect with testing-library DOM matchers and full chai compatibility.

interface Expect extends AsymmetricMatchersContaining {
  <T>(actual: T, message?: string): Assertion<T>;
  unreachable(message?: string): Promise<never>;
  soft<T>(actual: T, message?: string): Assertion<T>;
  extend(expects: MatchersObject): void;
  assertions(expected: number): Promise<void>;
  hasAssertions(): Promise<void>;
  anything(): any;
  any(constructor: unknown): any;
  getState(): MatcherState;
  setState(state: Partial<MatcherState>): void;
  not: AsymmetricMatchersContaining;
}

Assertions

Mocking and Spying

Mock functions and spies with reactive instrumentation for Storybook integration. Includes automatic cleanup and enhanced debugging.

function fn<T extends Procedure = Procedure>(implementation?: T): Mock<T>;
function spyOn<T, K extends keyof T>(
  object: T,
  method: K
): MockInstance;

function clearAllMocks(): void;
function resetAllMocks(): void;
function restoreAllMocks(): void;
function onMockCall(callback: Listener): () => void;

Mocking and Spying

DOM Testing

Complete DOM testing utilities including queries, user interactions, and async utilities. All functions are instrumented for Storybook debugging.

// Query functions (get, find, query variants)
function getByRole(container: HTMLElement, role: string, options?: ByRoleOptions): HTMLElement;
function findByText(container: HTMLElement, text: string, options?: SelectorMatcherOptions): Promise<HTMLElement>;
function queryByTestId(container: HTMLElement, testId: string, options?: MatcherOptions): HTMLElement | null;

// Scoping utility
function within(element: HTMLElement): BoundFunctions<typeof queries>;

// User interactions
interface UserEvent {
  click(element: Element, options?: ClickOptions): Promise<void>;
  type(element: Element, text: string, options?: TypeOptions): Promise<void>;
  clear(element: Element): Promise<void>;
  selectOptions(element: Element, values: string | string[]): Promise<void>;
  upload(element: Element, file: File | File[]): Promise<void>;
}

// Async utilities
function waitFor<T>(callback: () => T | Promise<T>, options?: WaitForOptions): Promise<T>;
function waitForElementToBeRemoved(callback: () => Element | Element[]): Promise<void>;

DOM Testing

Types

Core Types

type Procedure = (...args: any[]) => any;

type MockV2<T extends Procedure> = MockInstance<Parameters<T>, ReturnType<T>> & T;

type Mock<T extends Procedure | any[] = any[], R = any> = T extends Procedure
  ? MockV2<T>
  : T extends any[]
    ? MockV2<(...args: T) => R>
    : never;

interface MockInstance<TArgs extends any[] = any[], TReturns = any> {
  getMockName(): string;
  mockName(name: string): this;
  mockClear(): this;
  mockReset(): this;
  mockRestore(): void;
  mockImplementation(fn?: (...args: TArgs) => TReturns): this;
  mockImplementationOnce(fn: (...args: TArgs) => TReturns): this;
  mockReturnValue(value: TReturns): this;
  mockReturnValueOnce(value: TReturns): this;
  mockResolvedValue(value: Awaited<TReturns>): this;
  mockResolvedValueOnce(value: Awaited<TReturns>): this;
  mockRejectedValue(value: any): this;
  mockRejectedValueOnce(value: any): this;
}

// Jest-compatible assertion types
interface JestAssertion<T = any> {
  toBe(expected: T): void;
  toEqual(expected: T): void;
  toStrictEqual(expected: T): void;
  toContain(expected: any): void;
  toHaveLength(expected: number): void;
  // ... other Jest matchers
}

// Testing Library DOM matchers
interface TestingLibraryMatchers<R, T> {
  toBeInTheDocument(): R;
  toBeVisible(): R;
  toBeEmpty(): R;
  toBeDisabled(): R;
  toBeEnabled(): R;
  toBeRequired(): R;
  toBeValid(): R;
  toBeInvalid(): R;
  toHaveAttribute(attr: string, value?: string): R;
  toHaveClass(...classNames: string[]): R;
  toHaveStyle(css: string | Record<string, any>): R;
  toHaveTextContent(text: string | RegExp): R;
  toHaveValue(value: string | string[] | number): R;
  toHaveDisplayValue(value: string | RegExp | Array<string | RegExp>): R;
  toBeChecked(): R;
  toHaveFormValues(expectedValues: Record<string, any>): R;
  toHaveFocus(): R;
  toHaveAccessibleName(expectedAccessibleName?: string | RegExp): R;
  toHaveAccessibleDescription(expectedAccessibleDescription?: string | RegExp): R;
  toHaveErrorMessage(expectedErrorMessage?: string | RegExp): R;
}

// Expect static type
interface ExpectStatic {
  <T>(actual: T): JestAssertion<T>;
  stringContaining(expected: string): any;
  arrayContaining<T = any>(expected: readonly T[]): any;
  objectContaining(expected: Record<string, any>): any;
  any(constructor: any): any;
  anything(): any;
}

// Utility types
type Promisify<Fn> = Fn extends (...args: infer A) => infer R
  ? (...args: A) => R extends Promise<any> ? R : Promise<R>
  : Fn;

type PromisifyObject<O> = { [K in keyof O]: Promisify<O[K]> };

interface Assertion<T> extends PromisifyObject<JestAssertion<T>>, TestingLibraryMatchers<ReturnType<ExpectStatic['stringContaining']>, Promise<void>> {
  toHaveBeenCalledOnce(): Promise<void>;
  toSatisfy<E>(matcher: (value: E) => boolean, message?: string): Promise<void>;
  resolves: Assertion<T>;
  rejects: Assertion<T>;
  not: Assertion<T>;
}

type BoundFunctions<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any
    ? (...args: Parameters<T[K]>) => ReturnType<T[K]>
    : T[K];
};

// DOM Testing option types
interface MatcherOptions {
  exact?: boolean;
  normalizer?: (text: string) => string;
}

interface SelectorMatcherOptions extends MatcherOptions {
  selector?: string;
}

interface ByRoleOptions extends MatcherOptions {
  checked?: boolean;
  selected?: boolean;
  expanded?: boolean;
  pressed?: boolean;
  level?: number;
  name?: string | RegExp;
  description?: string | RegExp;
}

interface WaitForOptions {
  timeout?: number;
  interval?: number;
  onTimeout?: (error: Error) => Error;
  mutationObserverOptions?: MutationObserverInit;
}

interface ClickOptions {
  altKey?: boolean;
  button?: number;
  buttons?: number;
  clientX?: number;
  clientY?: number;
  ctrlKey?: boolean;
  detail?: number;
  metaKey?: boolean;
  relatedTarget?: Element | null;
  screenX?: number;
  screenY?: number;
  shiftKey?: boolean;
}

interface TypeOptions {
  delay?: number;
  skipClick?: boolean;
  skipAutoClose?: boolean;
  initialSelectionStart?: number;
  initialSelectionEnd?: number;
}

Utility Types

type MaybeMocked<T> = T & {
  [K in keyof T]: T[K] extends (...args: any[]) => any
    ? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>
    : T[K];
};

type MaybeMockedDeep<T> = T & {
  [K in keyof T]: T[K] extends (...args: any[]) => any
    ? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>
    : MaybeMockedDeep<T[K]>;
};

type MaybePartiallyMocked<T> = {
  [K in keyof T]?: T[K] extends (...args: any[]) => any
    ? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>
    : T[K];
};

type MaybePartiallyMockedDeep<T> = {
  [K in keyof T]?: T[K] extends (...args: any[]) => any
    ? MockInstance<Parameters<T[K]>, ReturnType<T[K]>>
    : MaybePartiallyMockedDeep<T[K]>;
};

type Promisify<Fn> = Fn extends (...args: infer A) => infer R
  ? (...args: A) => R extends Promise<any> ? R : Promise<R>
  : Fn;

type PromisifyObject<O> = { [K in keyof O]: Promisify<O[K]> };