@exodus/test v1.0.0-rc.98
@exodus/test
A runner for node:test, jest, and tape test suites on top of node:test (and any runtime).
It can run your existing tests on all runtimes and also browsers, with snapshots and module mocks.
Features
- Native ESM, including in Jest tests
- Esbuild on the fly for babelified ESM interop (enable via
--esbuild) - TypeScript support in both transform (through tsx, enable via
--esbuild) and typestrip (via--typescript) modes - Runs on Node.js node:test, Bun, Deno, Electron, v8 CLI, JSC, Hermes, SpiderMonkey, Chrome, Firefox, WebKit, Brave, Microsoft Edge, QuickJS, XS, GraalJS, Escargot, and even engine262.
- Testsuite-agnostic — can run any file as long as it sets exit code based on test results
- Built-in Jest compatibility (with
--jest), includingjest.*global- Up to ~10x faster depending on the original setup
- Actual
expectmodule, alsojest-extendedandjest-whenjust work on top - Snapshots, including snapshot matchers
- Function and timer mocks
- test.concurrent
- Module mocks, including for ESM modules (already loaded ESM modules can be mocked only on
node:test) - Loads Jest configuration
- Built-in network record/replay for offline tests, mocking
fetchandWebSocketsessions --drop-networksupport for guaranteed offline testing- Native code coverage via v8 (Node.js or c8), with istanbul reporters
- GitHub reporter (auto-enabled by default)
- JSDOM env support
- Hanging tests error by default (unlike
jest) - Babel support, picks up your Babel config (enable via
--babel) - Unlike
bun:test, it runs test files in isolated contexts \ Bun leaks globals / side effects between test files (ref), and has incompatibletest()lifecycle / order - Also features a tape API for drop-in replacement
Engines
Use --engine (or EXODUS_TEST_ENGINE=) to specify one of:
node:test— the default one, runs on top of modern Node.js test runner APInode:pure— implementation in pure JS, runs on Node.jsnode:bundle— same asnode:pure, but bundles everything into a single file before launching- Other runtimes:
bun:pure/bun:bundle— Bun, expectsbunto be availabledeno:bundle— Deno (v1 or v2, whicheverdenois)electron-as-node:test/electron-as-node:pure/electron-as-node:bundle\ Same asnode:*, but useselectronbinary.\ The usecase is mostly to test on BoringSSL instead of OpenSSL.electron:bundle— run tests in Electron BrowserWindow without Node.js integration.
- Browsers:
- Playwright builds (install Playwright-built engines with
exodus-test --playwright install)chromium:playwright— Playwright-built Chromiumfirefox:playwright— Playwright-built Firefoxwebkit:playwright— Playwright-built WebKit, close to Safarichrome:playwright— Chrome (system-installed)msedge:playwright— Microsoft Edge (system-installed)
- Puppeteer (system-provided or upstream builds)
chrome:puppeteer— Chromefirefox:puppeteer— Firefoxbrave:puppeteer— Bravemsedge:puppeteer— Microsoft Edge
- Playwright builds (install Playwright-built engines with
- Barebone engines (system-provided or installed with
npx jsvu/npx esvu):d8:bundle— v8 CLI (Chrome/Blink/Node.js JavaScript engine)jsc:bundle— JavaScriptCore (Safari/WebKit JavaScript engine)hermes:bundle— Hermes (React Native JavaScript engine)spidermonkey:bundle— SpiderMonkey (Firefox/Gecko JavaScript engine)quickjs:bundle— QuickJSxs:bundle— XSgraaljs:bundle— GraalJSescargot:bundle— Escargotengine262:bundle- engine262, the per-spec implementation of ECMA-262 (install with esvu)
Reporter samples
CLI (but uses colors when output supports them, e.g. in terminal):
# tests/jest/expect.mock.test.js
✔ PASS drinkAll > drinks something lemon-flavoured (1.300417ms)
✔ PASS drinkAll > does not drink something octopus-flavoured (0.191791ms)
✔ PASS drinkAll (1.842959ms)
✔ PASS drinkEach > drinkEach drinks each drink (0.360625ms)
✔ PASS drinkEach (0.463416ms)
✔ PASS toHaveBeenCalledWith > registration applies correctly to orange La Croix (0.53325ms)
✔ PASS toHaveBeenCalledWith (0.564166ms)
✔ PASS toHaveBeenLastCalledWith > applying to all flavors does mango last (0.380375ms)
✔ PASS toHaveBeenLastCalledWith (0.473417ms)
# tests/jest/fn.invocationCallOrder.test.js
✔ PASS mock.invocationCallOrder (4.221042ms)GitHub Actions collapses test results per-file, like this:
See live output in CI
Library
List of exports
@exodus/test/node—node:testAPI, working under non-Node.js platforms@exodus/test/jest—jestimplementation@exodus/test/tape—tapemock (can also be helpful when moving fromtap)
Binary
Just use "test": "exodus-test"
Options
--jest— register jest test helpers as global variables, also loadjest.config.*configuration options--esbuild— use esbuild loader, also enables Typescript support--babel— use babel loader (slower than--esbuild, makes sense if you have a special config)--coverage— enable coverage, prints coverage output (varies by coverage engine)--coverage-engine c8— use c8 coverage engine (default), also generates./coverage/dirs--coverage-engine node— use Node.js builtint coverage engine--watch— operate in watch mode and re-run tests on file changes--only— only run the tests marked withtest.only--passWithNoTests— do not error when no test files were found--write-snapshots— write snapshots instead of verifying them (has--test-update-snapshotsalias)--test-force-exit— force exit after tests are done
Jest compatibility
The --jest mode is mostly compatible with Jest. There are some noteworthy differences though.
This tool does not hoist mocks, so it is important that a mock is defined before the module that uses it is imported.
In ESM, this can be achieved with dynamic imports:
jest.doMock('./hogwarts.js', () => ({
__esModule: true,
default: jest.fn(),
}))
const { default: getEntryQualification } = await import('./hogwarts.js')
const { qualifiesForHogwarts } = await import('./wizard.js') // module importing ./hogwarts.js
test('qualifies for Hogwarts', () => {
// doSomething is a mock function
getEntryQualification.mockReturnValue(['lumos'])
expect(qualifiesForHogwarts('potter')).toBe(false)
getEntryQualification.mockReturnValue([])
expect(qualifiesForHogwarts('potter')).toBe(true)
})Note that all modules that transitively import hogwarts.js will have to be imported after the mock is defined.
License
12 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
7 months ago
7 months ago
7 months ago
12 months ago
12 months ago
12 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago