CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-storybook--test

Utilities for testing your stories inside play functions

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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]> };
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@storybook/test@8.6.x
Publish Source
CLI
Badge
tessl/npm-storybook--test badge