npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zerothrow/jest

v1.1.1

Published

Jest matchers for ZeroThrow Result types

Readme

@zerothrow/jest

🧠 ZeroThrow Layers
ZT – primitives (try, tryAsync, ok, err)
Result – combinators (map, andThen, orElse)
ZeroThrow – utilities (collect, firstSuccess, pipe)
@zerothrow/* – ecosystem packages (resilience, jest, etc)

ZeroThrow Ecosystem · Packages ⇢

npm types size CI ecosystem

Jest matchers for ZeroThrow Result types - elegant error handling assertions for your tests.

Installation

npm install @zerothrow/jest @zerothrow/core @zerothrow/expect
# or: pnpm add @zerothrow/jest @zerothrow/core @zerothrow/expect

Note: @zerothrow/core and @zerothrow/expect are peer dependencies.

Quick Start

The matchers are automatically registered when you import the package. Simply import it in your test setup file or at the top of your test files:

import '@zerothrow/jest';
import { ZT } from '@zerothrow/core';

describe('My Service', () => {
  it('should handle success', () => {
    const result = ZT.ok(42);
    expect(result).toBeOk();
    expect(result).toBeOkWith(42);
  });

  it('should handle errors', () => {
    const result = ZT.err('VALIDATION_ERROR', 'Invalid input');
    expect(result).toBeErr();
    expect(result).toHaveErrorCode('VALIDATION_ERROR');
    expect(result).toHaveErrorMessage('Invalid input');
  });
});

Setup Options

Automatic Setup (Default)

The matchers are automatically registered when the module is imported:

// In your test file or setup file
import '@zerothrow/jest';

Manual Setup

If you need more control over when matchers are registered:

import { setup, jestMatchers } from '@zerothrow/jest';

// Option 1: Use the setup function
setup();

// Option 2: Register matchers manually
expect.extend(jestMatchers);

TypeScript Configuration

The TypeScript types are automatically included. If you're using a custom tsconfig.json for tests, ensure the types are included:

{
  "compilerOptions": {
    "types": ["jest", "@zerothrow/jest"]
  }
}

API

toBeOk()

Asserts that a Result is Ok (successful).

const result = ZT.ok('success');
expect(result).toBeOk(); // Passes

const error = ZT.err('FAILED');
expect(error).toBeOk(); // Fails

toBeOkWith(expected)

Asserts that a Result is Ok with a specific value.

const result = ZT.ok({ id: 1, name: 'test' });
expect(result).toBeOkWith({ id: 1, name: 'test' }); // Passes
expect(result).toBeOkWith({ id: 2, name: 'test' }); // Fails

toBeErr()

Asserts that a Result is Err (failure).

const result = ZT.err('ERROR_CODE');
expect(result).toBeErr(); // Passes

const success = ZT.ok('value');
expect(success).toBeErr(); // Fails

toBeErrWith(error)

Asserts that a Result is Err with specific error properties.

const result = ZT.err('VALIDATION_ERROR', 'Email is invalid');

// Match by error code and message
expect(result).toBeErrWith({ 
  code: 'VALIDATION_ERROR', 
  message: 'Email is invalid' 
});

// Match with Error instance
const error = new Error('Something went wrong');
const result2 = ZT.err(error);
expect(result2).toBeErrWith(error);

toHaveErrorCode(code)

Asserts that a Result has a specific error code.

const result = ZT.err('NOT_FOUND', 'User not found');
expect(result).toHaveErrorCode('NOT_FOUND'); // Passes
expect(result).toHaveErrorCode('SERVER_ERROR'); // Fails

toHaveErrorMessage(message)

Asserts that a Result has a specific error message. Supports both string and RegExp matching.

const result = ZT.err('ERROR', 'Connection timeout after 30 seconds');

// Exact string match
expect(result).toHaveErrorMessage('Connection timeout after 30 seconds');

// RegExp match
expect(result).toHaveErrorMessage(/timeout after \d+ seconds/);

Examples

Testing Async Operations with Combinators

import '@zerothrow/jest';
import { ZT } from '@zerothrow/core';

async function fetchUser(id: string) {
  return ZT.tryAsync(async () => {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      throw new Error('User not found');
    }
    return response.json();
  });
}

test('fetchUser transforms data correctly', async () => {
  const result = await fetchUser('123')
    .then(r => r
      .map(user => ({ ...user, fetched: true }))
      .tap(user => expect(user.fetched).toBe(true))
    );
    
  expect(result).toBeOk();
  expect(result).toBeOkWith(expect.objectContaining({ 
    id: '123',
    fetched: true 
  }));
});

test('fetchUser provides fallback for errors', async () => {
  const result = await fetchUser('invalid')
    .then(r => r
      .tapErr(err => console.error('Fetch failed:', err))
      .orElse(() => ZT.ok({ id: 'guest', name: 'Guest User' }))
    );
    
  expect(result).toBeOk();
  expect(result).toBeOkWith({ id: 'guest', name: 'Guest User' });
});

Testing Error Codes

import '@zerothrow/jest';
import { ZT } from '@zerothrow/core';

function validateEmail(email: string) {
  if (!email) {
    return ZT.err('REQUIRED', 'Email is required');
  }
  if (!email.includes('@')) {
    return ZT.err('INVALID_FORMAT', 'Email must contain @');
  }
  return ZT.ok(email);
}

describe('validateEmail', () => {
  test('validates required field', () => {
    const result = validateEmail('');
    expect(result).toBeErr();
    expect(result).toHaveErrorCode('REQUIRED');
    expect(result).toHaveErrorMessage('Email is required');
  });

  test('validates format', () => {
    const result = validateEmail('notanemail');
    expect(result).toBeErrWith({ 
      code: 'INVALID_FORMAT',
      message: 'Email must contain @'
    });
  });

  test('transforms valid emails', () => {
    const result = validateEmail('[email protected]')
      .map(email => email.toLowerCase())
      .map(email => ({ email, domain: email.split('@')[1] }))
      .tap(data => expect(data.domain).toBe('example.com'));
      
    expect(result).toBeOk();
    expect(result).toBeOkWith({ 
      email: '[email protected]',
      domain: 'example.com' 
    });
  });
  
  test('chains multiple validations', () => {
    const validateAndNormalize = (email: string) => 
      validateEmail(email)
        .andThen(email => {
          if (email.endsWith('.test')) {
            return ZT.err('TEST_EMAIL', 'Test emails not allowed');
          }
          return ZT.ok(email);
        })
        .map(email => email.replace(/\+.*@/, '@')); // Remove plus addressing
        
    const result = validateAndNormalize('[email protected]');
    expect(result).toBeOkWith('[email protected]');
    
    const testResult = validateAndNormalize('[email protected]');
    expect(testResult).toHaveErrorCode('TEST_EMAIL');
  });
});

Testing with Custom Error Types and Combinators

import '@zerothrow/jest';
import { ZT, ZeroThrow } from '@zerothrow/core';

class ValidationError extends Error {
  constructor(public code: string, message: string) {
    super(message);
    this.name = 'ValidationError';
  }
}

function processData(data: unknown) {
  if (!data) {
    return ZT.err(new ValidationError('EMPTY_DATA', 'No data provided'));
  }
  return ZT.ok(data);
}

test('transforms and validates data', () => {
  const pipeline = (input: unknown) => processData(input)
    .andThen(data => {
      if (typeof data !== 'object') {
        return ZT.err(new ValidationError('INVALID_TYPE', 'Expected object'));
      }
      return ZT.ok(data);
    })
    .map(obj => ({ ...obj, processed: true }))
    .tap(result => console.log('Processed:', result));
    
  const result = pipeline({ value: 42 });
  expect(result).toBeOk();
  expect(result).toBeOkWith({ value: 42, processed: true });
});

test('chains error handling', () => {
  const result = processData(null)
    .mapErr(err => new ValidationError('WRAPPED', `Wrapped: ${err.message}`))
    .tapErr(err => expect(err.code).toBe('WRAPPED'));
    
  expect(result).toBeErr();
  expect(result).toHaveErrorMessage(/Wrapped: No data provided/);
});

test('collects multiple validations', () => {
  const results = ZeroThrow.collect([
    processData({ id: 1 }),
    processData({ id: 2 }),
    processData(null)
  ]);
  
  expect(results).toBeErr();
  expect(results).toHaveErrorCode('EMPTY_DATA');
});

Contributing

See the main repository for contribution guidelines.

License

MIT