CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-storybook--test

Utilities for testing your stories inside play functions

Pending
Overview
Eval results
Files

assertions.mddocs/

Assertions

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.

Capabilities

Expect Function

The main assertion function that supports jest-compatible API with additional testing-library matchers.

/**
 * Create an assertion for the given value
 * @param actual - The value to assert against
 * @param message - Optional error message
 * @returns Assertion object with chainable matchers
 */
function expect<T>(actual: T, message?: string): Assertion<T>;

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;
}

Usage Examples:

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

// Basic assertions
expect(42).toBe(42);
expect('hello').toEqual('hello');
expect([1, 2, 3]).toContain(2);

// Mock function assertions
const mockFn = fn();
mockFn('arg1', 'arg2');
expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2');
expect(mockFn).toHaveBeenCalledOnce();

// Async assertions
await expect(Promise.resolve('value')).resolves.toBe('value');
await expect(Promise.reject('error')).rejects.toBe('error');

// DOM assertions (with testing-library matchers)
const button = document.createElement('button');
button.textContent = 'Click me';
button.disabled = false;
expect(button).toBeInTheDocument();
expect(button).toHaveTextContent('Click me');
expect(button).toBeEnabled();

Assertion Interface

The assertion interface provides all jest-compatible matchers plus testing-library DOM matchers.

interface Assertion<T> extends PromisifyObject<JestAssertion<T>>, TestingLibraryMatchers<ReturnType<ExpectStatic['stringContaining']>, Promise<void>> {
  // Standard jest matchers
  toBe(expected: T): Promise<void>;
  toEqual(expected: T): Promise<void>;
  toStrictEqual(expected: T): Promise<void>;
  toContain(expected: any): Promise<void>;
  toContainEqual(expected: any): Promise<void>;
  toHaveLength(expected: number): Promise<void>;
  toMatch(expected: string | RegExp): Promise<void>;
  toMatchObject(expected: Record<string, any>): Promise<void>;
  toThrow(expected?: string | RegExp | Error): Promise<void>;
  toThrowError(expected?: string | RegExp | Error): Promise<void>;
  
  // Mock-specific matchers
  toHaveBeenCalled(): Promise<void>;
  toHaveBeenCalledOnce(): Promise<void>;
  toHaveBeenCalledTimes(expected: number): Promise<void>;
  toHaveBeenCalledWith(...expected: any[]): Promise<void>;
  toHaveBeenLastCalledWith(...expected: any[]): Promise<void>;
  toHaveBeenNthCalledWith(nth: number, ...expected: any[]): Promise<void>;
  toHaveReturned(): Promise<void>;
  toHaveReturnedTimes(expected: number): Promise<void>;
  toHaveReturnedWith(expected: any): Promise<void>;
  toHaveLastReturnedWith(expected: any): Promise<void>;
  toHaveNthReturnedWith(nth: number, expected: any): Promise<void>;
  
  // Testing Library DOM matchers
  toBeInTheDocument(): Promise<void>;
  toBeVisible(): Promise<void>;
  toBeEmptyDOMElement(): Promise<void>;
  toBeDisabled(): Promise<void>;
  toBeEnabled(): Promise<void>;
  toBeInvalid(): Promise<void>;
  toBeRequired(): Promise<void>;
  toBeValid(): Promise<void>;
  toBeChecked(): Promise<void>;
  toBePartiallyChecked(): Promise<void>;
  toHaveAccessibleDescription(expected?: string | RegExp): Promise<void>;
  toHaveAccessibleName(expected?: string | RegExp): Promise<void>;
  toHaveAttribute(attribute: string, expected?: string | RegExp): Promise<void>;
  toHaveClass(...expected: string[]): Promise<void>;
  toHaveFocus(): Promise<void>;
  toHaveFormValues(expected: Record<string, any>): Promise<void>;
  toHaveStyle(expected: string | Record<string, any>): Promise<void>;
  toHaveTextContent(expected?: string | RegExp): Promise<void>;
  toHaveValue(expected?: string | string[] | number): Promise<void>;
  toHaveDisplayValue(expected: string | RegExp | string[] | RegExp[]): Promise<void>;

  // Additional matchers
  toSatisfy<E>(matcher: (value: E) => boolean, message?: string): Promise<void>;
  
  // Async assertion helpers
  resolves: Assertion<T>;
  rejects: Assertion<T>;
  
  // Negation
  not: Assertion<T>;
}

Soft Assertions

Create soft assertions that don't immediately fail the test but collect errors.

/**
 * Create a soft assertion that collects errors instead of immediately failing
 * @param actual - The value to assert against
 * @param message - Optional error message
 * @returns Assertion object for soft testing
 */
function soft<T>(actual: T, message?: string): Assertion<T>;

Usage Example:

import { expect } from '@storybook/test';

// Soft assertions collect errors without stopping execution
expect.soft(1).toBe(2); // This won't throw immediately
expect.soft(2).toBe(3); // This won't throw immediately
expect.soft(3).toBe(3); // This passes

// All collected soft assertion errors will be reported at the end

Extending Expect

Add custom matchers to the expect function.

/**
 * Extend expect with custom matchers
 * @param expects - Object containing custom matcher functions
 */
function extend(expects: MatchersObject): void;

interface MatchersObject {
  [key: string]: (this: MatcherState, actual: any, ...expected: any[]) => MatcherResult;
}

interface MatcherResult {
  message: () => string;
  pass: boolean;
}

interface MatcherState {
  isNot: boolean;
  promise: string;
  assertEquals: (actual: any, expected: any, message?: string) => void;
  assertType: (value: any, type: string, message?: string) => void;
}

Usage Example:

import { expect } from '@storybook/test';

expect.extend({
  toBeWithinRange(received, floor, ceiling) {
    const pass = received >= floor && received <= ceiling;
    if (pass) {
      return {
        message: () => `expected ${received} not to be within range ${floor} - ${ceiling}`,
        pass: true,
      };
    } else {
      return {
        message: () => `expected ${received} to be within range ${floor} - ${ceiling}`,
        pass: false,
      };
    }
  },
});

// Usage
expect(100).toBeWithinRange(90, 110);

Asymmetric Matchers

Create asymmetric matchers for flexible assertions.

/**
 * Match any value of the given constructor type
 * @param constructor - Constructor function to match against
 * @returns Asymmetric matcher
 */
function any(constructor: unknown): any;

/**
 * Match any truthy value
 * @returns Asymmetric matcher
 */
function anything(): any;

Usage Examples:

import { expect } from '@storybook/test';

// Asymmetric matchers for flexible matching
expect({ name: 'John', age: 30 }).toEqual({
  name: expect.any(String),
  age: expect.any(Number),
});

expect(['apple', 'banana']).toEqual([
  expect.anything(),
  expect.anything(),
]);

Assertion Count Validation

Validate the number of assertions executed during a test.

/**
 * Expect exactly N assertions to be called during the test
 * @param expected - Expected number of assertions
 */
function assertions(expected: number): Promise<void>;

/**
 * Expect at least one assertion to be called during the test
 */
function hasAssertions(): Promise<void>;

Usage Examples:

import { expect } from '@storybook/test';

export const TestStory = {
  play: async () => {
    expect.assertions(2); // Expect exactly 2 assertions
    
    expect(1).toBe(1);
    expect(2).toBe(2);
    // Test will fail if not exactly 2 assertions are made
  },
};

export const AnotherStory = {
  play: async () => {
    expect.hasAssertions(); // Expect at least one assertion
    
    const condition = Math.random() > 0.5;
    if (condition) {
      expect(true).toBe(true);
    }
    // Test will fail if no assertions are made
  },
};

Unreachable Code

Mark code paths that should never be reached.

/**
 * Mark a code path as unreachable - will always fail if executed
 * @param message - Optional error message
 * @throws Always throws an error
 */
function unreachable(message?: string): Promise<never>;

Usage Example:

import { expect } from '@storybook/test';

export const TestStory = {
  play: async () => {
    const value = 'valid';
    
    switch (value) {
      case 'valid':
        expect(true).toBe(true);
        break;
      default:
        expect.unreachable('Should never reach default case');
    }
  },
};

Install with Tessl CLI

npx tessl i tessl/npm-storybook--test

docs

assertions.md

dom-testing.md

index.md

mocking.md

tile.json