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

sxy-test-runner

v2.3.19

Published

Readme

sxy-test-runner

Mocha-like test runner for es modules (only), with watch mode, and re-running only tests related to changes (by dependency analysis), full async code support

import { describe } from 'sxy-test-runner'
import { expect } from 'chai'

function add(a, b) {
    return a + b
}

describe('The add() function', ({it}) => {
    it('should add 1 and 2, making 3', () => {
        const result = add(1, 2)
        expect(result).to.equal(3)
    })
})

Usage

1. Install

npm i -D sxy-test-runner

or

yarn add -D sxy-test-runner

or

pnpm add -D sxy-test-runner

2. Create a config (required)

sxy-test-runner init

sxy-test is an alias for sxy-test-runner and can be used instead

This will create a config in your project folder. It can be moved to a folder named "config" if you prefer, or specify a location using --config

3. Modify the config, specifying the base path for tests and code to watch, and glob patterns for tests and code to watch

{
    // base folder for tests matching and ignore patterns
    testsBase: 'dist',

    // glob pattern or array of glob patterns of test files
    tests: [
        '**/*.test.{js,jsx}'
    ],

    // watch mode configurations
    watch: {

        //// the base directory to watch files in
        watchFilesBase: 'dist',

        //// glob filter or array of global filters of files to watch
        watchFiles: '**/*.js',

    },
}

The other config options and fairly well explained in the config. More info on them later.

4. Run

npx sxy-test-runner

sxy-test is an alias for sxy-test-runner and can be used instead

("npx" can be omitted in package.json scripts)

sxy-test-runner runs watch mode, re-running only relevant tests, by default.

While watch mode is idle, enter r or l to re-run the last set of tests, a to run all tests again, or f to re-run the currently failing tests.

Set watch.ttyActionsOnKeypress: true to trigger these commands as soon as their key is pressed, without waiting for Enter. It defaults to false.

To run tests only once and not watch (e.g. for ci/cd), use

npx sxy-test-runner once

To override the configured test pattern for either watch or once mode, use -t or --test-files. The pattern remains relative to testsBase:

npx sxy-test-runner once --test-files "**/*.focused.test.js"

To further constrain the configured test pattern without replacing it, use -f or --filter. This pattern is also relative to testsBase and works in both modes:

npx sxy-test-runner once --filter "**/users/**"

--test-files and --filter can be combined: the former replaces the configured test pattern, then the latter filters the discovered files.

--filter-describe and --filter-it filter by name, but only after the selected test files have loaded. sxy-test-runner gives each test file a fresh ESM instance and propagates it through that file's static dependency graph, bypassing the ordinary ESM cache. Loading a large set of files and filtering their describes or tests afterward can therefore be much slower than filtering files first. Prefer --filter when a file glob can select the work you need, and use the name filters for further narrowing within those files.

To specific an alternate config, use -c or --config

npx sxy-test-runner --config sxy-test-runner.alternate.config.js

How it works

sxy-test-runner runs all tests inside one node process, to improve performance (this framework far exceeds mocha in parallel mode, which is needed to test ESM files, due to this).

Because of this, be careful with globals, which will be shared.

While tests are running, NODE_ENV defaults to test when it was not already set. SXYT and SXY_TEST_RUNNER are both set to true, allowing configuration and application code to detect sxy-test-runner specifically.

Sharing a module between test files

Ordinary test dependencies receive a fresh module instance for each test file. To deliberately share a module and its static dependency graph, give its filename a ._cached_. marker:

import { database } from './fixtures/database._cached_.js'

The module then uses Node's normal process-wide module cache, so every test file receives the same exported values. JavaScript and TypeScript module extensions are supported; in TypeScript NodeNext source, import the emitted ._cached_.js name as usual.

Cached modules remain loaded for the full one-off run or watch process. Avoid unsynchronised mutable state when tests run in parallel, and explicitly clean up shared resources such as servers or database connections.

Execution modes

execution settings let you choose whether to run test files, describe blocks, and individual tests sequentially or in parallel.

If working with globals you may want to use sequential only, to avoid conflicts on those globals.

Sequential mode also allows console log output to be displayed correctly with each test. Running in parallel mode does not allow log output to be matched up to each test, due to the use of a global override on console.log (other console output methods such as console.trace() are not covered currently)

If your code is isolated, and asyncronous, you can use parallel exeecution modes to radically speed up your tests, as the expense of the points mentioned above.

Dependencies

Module dependencies are identified using dependency-tree. Dynamic imports will not be detected and will not trigger test re-runs.

No option to re-run all tests on changes is currently available - I may add this on request, or submit a PR if you want this.

Failing test re-running

Default behaviour is to re-run any failing test any time a change is made, even if that change isn't relevant to the failing test. This is to ensure you can see where attention is needed. Turn this off in the config if it is not convenient for you: config.watch.reRunFailingTests

Advanced usage

1. Run tasks at startup - setup

Edit config.setup. This config value can take a function, a file to import relative to the project folder, or an array or one or both of those.

2. Run tasks before each test file / describe / test globally

Edit config.beforeEachFile / config.beforeEachDescribe / config.beforeEachTest with a function, file or array of either.

If a file exports named values, or a function returns an object with properties, these keys will be added to a storage object (beware of conflicting names). This object is then passed to the afterEachFile / afterEachDescribe / afterEachTest tasks for cleanup.

    // tasks to run before each test file is run
    beforeEachFile: () => {
        const thing = prepSomething()
        return { thing }
    },

    // tasks to run after each test file is run
    afterEachFile: ({thing}) => {
        destroyingSomething(thing)
    },

3. Run code before each describe block, or at the end of a test file - per test file

Not yet implemented.

4. Run code before each test, after each test, or after the describe block - per describe block

import { describe } from 'sxy-test-runner'
import { expect } from 'chai'

describe('something', ({it, beforeEachTest, afterEachTest, afterDescribe}) => {

    // setup can take place here without any special measures
    // but you can only safely set globals for your test here if your
    // execution mode for files and describes is 'sequential' (default)
    const something = createSomething()

    // run this just before each test
    beforeEachTest(() => {
        prepSomething(something)
    })

    // run this just after each test
    afterEachTest(() => {
        clearupSomething(something)
    })

    it('should do something', () => {
        expect(something.didSomething()).to.equal(true)
    })
    it('should do another thing', () => {
        expect(something.didAnotherThing()).to.equal(true)
    })

    // teardown should go in afterDescribe.
    // tests are not run syncronously within the describe block (even sync tests)
    // so cleanup code here would run too early
    afterDescribe(() => {
        destroySomething(something)
    })

})

Exit codes for once

0: success 1: non sxyt error 2: sxyt error 3: test errors It may also crash due to exceptions without giving any of these errors codes