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

@bupkis/from-jest

v0.2.0

Published

Migrate Jest and Vitest assertions to bupkis

Readme

@bupkis/from-jest

Migrate Jest and Vitest assertions to bupkis with a single command.

Installation

npx @bupkis/from-jest

Or install globally:

npm install -g @bupkis/from-jest
bupkis-from-jest

Usage

# Transform all test files in current directory
npx @bupkis/from-jest

# Transform specific patterns
npx @bupkis/from-jest "src/**/*.test.ts" "tests/**/*.spec.ts"

# Dry run (see what would change)
npx @bupkis/from-jest --dry-run

# Strict mode (fail on any unsupported matcher)
npx @bupkis/from-jest --strict

# Exclude patterns
npx @bupkis/from-jest -e "**/fixtures/**" -e "**/snapshots/**"

# Transform mock/spy matchers using @bupkis/sinon
npx @bupkis/from-jest --sinon

Modes

  • --best-effort (default): Transform what we can, add // TODO: Manual migration needed comments for complex cases
  • --strict: Fail immediately on any unsupported transformation
  • --interactive: Prompt for ambiguous cases (coming soon)

Supported Test Frameworks

Supported versions:

  • Jest 29+ (including Jest 30)
  • Vitest 1+

The codemod handles imports from:

  • Jest: @jest/globals
  • Vitest: vitest
  • Global expect: Adds import { expect } from 'bupkis'

When transforming, the codemod:

  1. Removes expect from your test framework import
  2. Adds import { expect } from 'bupkis'
  3. Keeps other imports (describe, it, test, etc.) from your original framework

Note: Jest 30 removed several matcher aliases (e.g., toThrowErrortoThrow, toBeCalledtoHaveBeenCalled). This codemod handles both the old aliases and the new canonical names.

Supported Matchers

Jest/Vitest Core

| Jest | bupkis | | ------------------------------ | -------------------------------------- | | expect(x).toBe(y) | expect(x, 'to be', y) | | expect(x).toEqual(y) | expect(x, 'to deep equal', y) | | expect(x).toBeTruthy() | expect(x, 'to be truthy') | | expect(x).toBeFalsy() | expect(x, 'to be falsy') | | expect(x).toBeNull() | expect(x, 'to be null') | | expect(x).toBeUndefined() | expect(x, 'to be undefined') | | expect(x).toBeDefined() | expect(x, 'to be defined') | | expect(x).toBeInstanceOf(Y) | expect(x, 'to be an instance of', Y) | | expect(x).toBeGreaterThan(y) | expect(x, 'to be greater than', y) | | expect(x).toBeLessThan(y) | expect(x, 'to be less than', y) | | expect(x).toMatch(pattern) | expect(x, 'to match', pattern) | | expect(x).toContain(y) | expect(x, 'to contain', y) | | expect(x).toHaveLength(n) | expect(x, 'to have length', n) | | expect(x).toHaveProperty(k) | expect(x, 'to have property', k) | | expect(x).toMatchObject(y) | expect(x, 'to satisfy', y) | | expect(fn).toThrow() | expect(fn, 'to throw') |

jest-extended

| jest-extended | bupkis | | -------------------------- | -------------------------------- | | expect(x).toBeTrue() | expect(x, 'to be true') | | expect(x).toBeFalse() | expect(x, 'to be false') | | expect(x).toBeArray() | expect(x, 'to be an array') | | expect(x).toBeEmpty() | expect(x, 'to be empty') | | expect(x).toStartWith(s) | expect(x, 'to start with', s) | | expect(x).toEndWith(s) | expect(x, 'to end with', s) | | expect(x).toBeOneOf(arr) | expect(x, 'to be one of', arr) |

Negation

All matchers support negation:

// Jest/Vitest
expect(x).not.toBe(y);

// bupkis
expect(x, 'not to be', y);

Mock/Spy Matchers (with --sinon)

When you use the --sinon flag, the codemod transforms Jest mock matchers to @bupkis/sinon assertions:

| Jest (canonical) | Jest 29 alias | @bupkis/sinon | | ---------------------------- | -------------------- | ----------------------------------------- | | toHaveBeenCalled() | toBeCalled() | 'was called' | | toHaveBeenCalledTimes(n) | toBeCalledTimes() | 'was called times', n | | toHaveBeenCalledWith(...) | toBeCalledWith() | 'was called with', [...] | | toHaveBeenLastCalledWith() | lastCalledWith() | spy.lastCall, 'to have args', [...] | | toHaveBeenNthCalledWith(n) | nthCalledWith() | spy.getCall(n-1), 'to have args', [...] | | toHaveReturned() | toReturn() | 'to have returned' | | toHaveReturnedTimes(n) | toReturnTimes() | 'to have returned times', n | | toHaveReturnedWith(v) | toReturnWith() | 'to have returned with', v | | toHaveLastReturnedWith(v) | lastReturnedWith() | spy.lastCall, 'to have returned', v | | toHaveNthReturnedWith(n,v) | nthReturnedWith() | spy.getCall(n-1), 'to have returned', v |

Note: The Jest 29 aliases were deprecated in Jest 26 and removed in Jest 30. This codemod handles both.

Example transformation:

// Before (Jest)
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2');
expect(mockFn).toHaveBeenLastCalledWith('final');

// After (bupkis + @bupkis/sinon)
expect(mockFn, 'was called');
expect(mockFn, 'was called with', ['arg1', 'arg2']);
expect(mockFn.lastCall, 'to have args', ['final']);

Important: The --sinon flag only transforms assertions. You must manually migrate your mock/spy creation from jest.fn() to Sinon:

// Before
const mockFn = jest.fn();

// After (manual migration required)
import sinon from 'sinon';
const mockFn = sinon.spy();

When mock matchers are detected without --sinon, the codemod will notify you and suggest using the flag.

Programmatic API

import { transform, transformCode } from '@bupkis/from-jest';

// Transform a code string
const result = await transformCode(`expect(42).toBe(42);`);
console.log(result.code); // expect(42, 'to be', 42);

// Transform with sinon support
const result = await transformCode(`expect(spy).toHaveBeenCalled();`, {
  sinon: true,
});
console.log(result.code); // expect(spy, 'was called');

// Transform files
const results = await transform({
  include: ['src/**/*.test.ts'],
  exclude: ['**/node_modules/**'],
  mode: 'best-effort',
  sinon: true, // Enable mock matcher transformation
  write: true,
});

What Requires Manual Migration

Some Jest/Vitest patterns cannot be automatically transformed:

  • Mock/spy creation: jest.fn(), jest.spyOn(), vi.fn() must be manually migrated to Sinon (sinon.spy(), sinon.stub())
  • DOM matchers: @testing-library/jest-dom matchers like toBeInTheDocument
  • Promise matchers: resolves/rejects need restructuring to expectAsync
  • Complex toHaveProperty: When checking property value, not just existence

These cases will be marked with // TODO: Manual migration needed comments when using --best-effort mode.

Tip: Mock/spy assertions can be transformed automatically with --sinon. See the Mock/Spy Matchers section.

License

Copyright © 2025 Christopher Hiller. Licensed under BlueOak-1.0.0.