or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-jest-each

Parameterised tests for Jest that enable running the same test multiple times with different data sets using arrays or tagged template literals

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/jest-each@30.1.x

To install, run

npx @tessl/cli install tessl/npm-jest-each@30.1.0

index.mddocs/

jest-each

jest-each is a parameterized testing library that enables running the same test multiple times with different data sets. It provides two main approaches: array-based parameterization and tagged template literal syntax with readable table formatting. The library integrates seamlessly with all Jest test functions and modifiers.

Package Information

  • Package Name: jest-each
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install jest-each

Core Imports

import each from "jest-each";

For CommonJS:

const each = require("jest-each").default;

Named imports:

import each, { bind } from "jest-each";

Basic Usage

Array Format

import each from "jest-each";

// Array of arrays
each([
  [1, 2, 3],
  [4, 5, 9],
  [7, 8, 15]
]).test('adds %i + %i to equal %i', (a, b, expected) => {
  expect(a + b).toBe(expected);
});

// Array of objects  
each([
  { name: 'John', age: 25 },
  { name: 'Jane', age: 30 }
]).test('user $name is $age years old', ({ name, age }) => {
  expect(typeof name).toBe('string');
  expect(typeof age).toBe('number');
});

Template Literal Format

each`
  a    | b    | expected
  ${1} | ${2} | ${3}
  ${4} | ${5} | ${9}
  ${7} | ${8} | ${15}
`.test('adds $a + $b to equal $expected', ({ a, b, expected }) => {
  expect(a + b).toBe(expected);
});

Capabilities

Main Each Function

Creates parameterized test functions that work with all Jest test methods and modifiers.

/**
 * Main function to create parameterized tests using arrays or template literals
 * @param table - Test data as array of arrays, array of objects, or template literal
 * @param data - Additional template data when using template literals
 * @returns Object with Jest test methods (test, it, describe, etc.)
 */
function each(table: EachTable, ...data: TemplateData): {
  test: EachTestFunction;
  it: EachTestFunction;
  describe: EachDescribeFunction;
  xit: EachTestFunction;
  fit: EachTestFunction;
  xtest: EachTestFunction;
  fdescribe: EachDescribeFunction;
  xdescribe: EachDescribeFunction;
};

type EachTable = ArrayTable | TemplateTable;
type ArrayTable = Table | Row;
type Table = ReadonlyArray<Row>;
type Row = ReadonlyArray<unknown>;
type TemplateTable = TemplateStringsArray;
type TemplateData = ReadonlyArray<unknown>;

With Global Context

Creates each function bound to a specific global context for custom Jest environments.

/**
 * Creates each function bound to specific global context
 * @param g - Global Jest context object
 * @returns Function that accepts table and returns test methods
 */
each.withGlobal: (g: Global) => (table: EachTable, ...data: TemplateData) => ReturnType<typeof each>;

interface Global {
  test: TestFunction;
  it: TestFunction;
  describe: DescribeFunction;
  xit: TestFunction;
  fit: TestFunction;
  xtest: TestFunction;
  fdescribe: DescribeFunction;
  xdescribe: DescribeFunction;
}

Test Function Interface

All returned test functions support Jest's standard interface with modifiers.

interface EachTestFunction {
  /**
   * Creates a parameterized test
   * @param title - Test title with optional placeholders
   * @param testFn - Test function receiving parameters from data table
   * @param timeout - Optional timeout in milliseconds
   */
  (title: string, testFn: EachTestFn<TestFn>, timeout?: number): void;
  
  /** Run only this test */
  only: EachTestFunction;
  
  /** Skip this test */
  skip: EachTestFunction;
  
  /** Concurrent test execution */
  concurrent: EachConcurrentTestFunction;
}

interface EachConcurrentTestFunction {
  (title: string, testFn: EachTestFn<ConcurrentTestFn>, timeout?: number): void;
  only: EachConcurrentTestFunction;
  skip: EachConcurrentTestFunction;
}

interface EachDescribeFunction {
  /**
   * Creates a parameterized describe block
   * @param title - Describe title with optional placeholders
   * @param suiteFn - Suite function receiving parameters from data table
   * @param timeout - Optional timeout in milliseconds
   */
  (title: string, suiteFn: EachTestFn<BlockFn>, timeout?: number): void;
  
  /** Run only this describe block */
  only: EachDescribeFunction;
  
  /** Skip this describe block */
  skip: EachDescribeFunction;
}

type EachTestFn<T extends TestCallback> = (...args: ReadonlyArray<any>) => ReturnType<T>;

Bind Function (Advanced)

Core binding function for creating custom parameterized test functions.

/**
 * Creates parameterized test functions by binding to Jest global methods
 * @param cb - Global Jest callback function to bind to
 * @param supportsDone - Whether the callback supports done callback (default: true)
 * @param needsEachError - Whether to pass error context (default: false)
 * @returns Function that accepts table data and returns bound test function
 */
function bind<EachCallback extends TestCallback>(
  cb: GlobalCallback,
  supportsDone?: boolean,
  needsEachError?: boolean
): EachTestFn<any>;

type GlobalCallback = (
  testName: string,
  fn: ConcurrentTestFn,
  timeout?: number,
  eachError?: Error
) => void;

type TestCallback = BlockFn | TestFn | ConcurrentTestFn;

Title Formatting and Interpolation

Printf-Style Placeholders

jest-each supports printf-style placeholders for automatic value formatting in test titles:

  • %s - String formatting
  • %d, %i - Integer formatting
  • %f - Float formatting
  • %j - JSON formatting
  • %o, %O - Object formatting
  • %p - Pretty-print formatting (with depth control)
  • %# - Zero-based index placeholder
  • %$ - One-based index placeholder
each([
  [1, 2, 3],
  [4, 5, 9]
]).test('test %# adds %i + %i to equal %i', (a, b, expected) => {
  expect(a + b).toBe(expected);
});
// Results in: "test 0 adds 1 + 2 to equal 3", "test 1 adds 4 + 5 to equal 9"

Template Variable Interpolation

When using template literals or object arrays, variables can be interpolated using $variable syntax:

each`
  name     | age  | status
  ${'John'} | ${25} | ${'active'}
  ${'Jane'} | ${30} | ${'inactive'}
`.test('user $name (age $age) is $status', ({ name, age, status }) => {
  expect(typeof name).toBe('string');
});

Object Property Access

Template variables support deep property access using dot notation:

each([
  { user: { profile: { name: 'John' }, age: 25 } },
  { user: { profile: { name: 'Jane' }, age: 30 } }
]).test('user $user.profile.name is $user.age years old', ({ user }) => {
  expect(user).toBeDefined();
});

Data Table Formats

Array of Arrays

Most basic format for simple parameter lists:

each([
  [1, 2, 3],    // First test case
  [4, 5, 9],    // Second test case  
  [7, 8, 15]    // Third test case
]).test('adds %i + %i to equal %i', (a, b, expected) => {
  expect(a + b).toBe(expected);
});

Array of Objects

Provides named parameters for better readability:

each([
  { input: 'hello', expected: 5 },
  { input: 'world', expected: 5 },
  { input: '', expected: 0 }
]).test('$input has length $expected', ({ input, expected }) => {
  expect(input.length).toBe(expected);
});

Single Column Arrays

For tests with single parameters:

each([1, 2, 3, 4, 5]).test('number %i is defined', (num) => {
  expect(num).toBeDefined();
  expect(typeof num).toBe('number');
});

Template Literals

Tabular format with column headers for maximum readability:

each`
  operation | a    | b    | expected
  ${'add'}  | ${1} | ${2} | ${3}
  ${'sub'}  | ${5} | ${3} | ${2}
  ${'mul'}  | ${3} | ${4} | ${12}
`.test('$operation: $a and $b equals $expected', ({ operation, a, b, expected }) => {
  switch (operation) {
    case 'add':
      expect(a + b).toBe(expected);
      break;
    case 'sub':
      expect(a - b).toBe(expected);
      break;
    case 'mul':
      expect(a * b).toBe(expected);
      break;
  }
});

Jest Integration

Test Methods

All Jest test methods are supported:

each(testData).test('test title', testFn);        // Standard test
each(testData).test.only('test title', testFn);   // Run only this test
each(testData).test.skip('test title', testFn);   // Skip this test
each(testData).test.concurrent('test title', testFn); // Concurrent execution

each(testData).it('test title', testFn);          // Alias for test
each(testData).xit('test title', testFn);         // Skip test (alias)
each(testData).fit('test title', testFn);         // Focus test (alias)
each(testData).xtest('test title', testFn);       // Skip test (alias)

Describe Blocks

Parameterized describe blocks for grouping related tests:

each([
  { type: 'user', endpoint: '/users' },
  { type: 'post', endpoint: '/posts' }
]).describe('$type API', ({ type, endpoint }) => {
  test('should fetch all items', async () => {
    const response = await api.get(endpoint);
    expect(response.status).toBe(200);
  });
  
  test('should create new item', async () => {
    const response = await api.post(endpoint, { name: 'Test' });
    expect(response.status).toBe(201);
  });
});

Concurrent Test Execution

Support for concurrent test execution with proper typing:

each([
  { url: '/api/users/1' },
  { url: '/api/users/2' },
  { url: '/api/users/3' }
]).test.concurrent('fetches data from $url', async ({ url }) => {
  const response = await fetch(url);
  expect(response.status).toBe(200);
});

Done Callback Support

Automatic detection and support for Jest's done callback:

each([
  { delay: 100 },
  { delay: 200 }
]).test('async test with $delay ms delay', ({ delay }, done) => {
  setTimeout(() => {
    expect(delay).toBeGreaterThan(0);
    done();
  }, delay);
});

Error Handling and Validation

Table Validation

Comprehensive validation with descriptive error messages:

// Invalid: empty array
each([]).test('title', () => {}); // Throws: "Error: .each called with an empty Array of table data."

// Invalid: tagged template with no data
each``.test('title', () => {}); // Throws: "Error: .each called with an empty Tagged Template Literal of table data."

// Invalid: not an array or template literal
each("invalid").test('title', () => {}); // Throws: ".each must be called with an Array or Tagged Template Literal."

Template Table Validation

Validation for template literal format and argument count:

// Invalid: mismatched arguments
each`
  a | b
  ${1} | ${2}
  ${3} // Missing second argument
`.test('title', () => {}); // Throws error with missing argument details

Types

// Core types for data tables
type Col = unknown;
type Row = ReadonlyArray<Col>;
type Table = ReadonlyArray<Row>;
type ArrayTable = Table | Row;
type TemplateTable = TemplateStringsArray;
type TemplateData = ReadonlyArray<unknown>;
type EachTable = ArrayTable | TemplateTable;

// Test function types
type ValidTestReturnValues = void | undefined;
type TestReturnValue = ValidTestReturnValues | Promise<unknown>;
type DoneFn = (reason?: string | Error) => void;

type TestFn = PromiseReturningTestFn | GeneratorReturningTestFn | DoneTakingTestFn;
type PromiseReturningTestFn = (this: TestContext) => TestReturnValue;
type DoneTakingTestFn = (this: TestContext, done: DoneFn) => ValidTestReturnValues;
type ConcurrentTestFn = () => Promise<unknown>;
type BlockFn = () => void;

// Generic test function type for parameterized tests
type EachTestFn<EachCallback extends TestCallback> = (
  ...args: ReadonlyArray<any>
) => ReturnType<EachCallback>;

// Template and interpolation types
type Template = Record<string, unknown>;
type Templates = Array<Template>;
type Headings = Array<string>;

// Internal test case representation
type EachTests = ReadonlyArray<{
  title: string;
  arguments: ReadonlyArray<unknown>;
}>;