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

test-assert-cli

v0.1.0

Published

Serve your node:test files to the browser, or run them in Node.js. test-assert-lite stands in for node:test.

Readme

test-assert-cli

npm version

Serve your node:test files to the browser, or run them in Node.js. test-assert-lite stands in for node:test.

  • Test files keep their node:test and node:assert imports. tacli maps them to test-assert-lite
  • --serve hosts your tests as a web page, with live reload
  • --alias and --import-map map any module name to any module, even a Node.js built-in. One suite can then test an implementation in both Node.js and the browser
  • TypeScript test files run without a build step
  • For automated browser runs, use webdriver-js or chromium-js, firefox-js and webkit-js. Both are built on tacli
npm install -D test-assert-cli

SYNOPSIS

BDD style with describe and it:

import {strict as assert} from "node:assert"
import {describe, it} from "node:test"

const parseQuery = (search) => Object.fromEntries(new URLSearchParams(search))
const buildQuery = (params) => new URLSearchParams(params).toString()

describe("buildQuery() from an object", () => {
    it("round-trips through parseQuery()", () => {
        const params = {q: "cat", page: "2"}
        assert.deepEqual(parseQuery("?" + buildQuery(params)), params)
    })
    it("appends to a URL", () => {
        const url = new URL("https://example.com/search")
        url.search = buildQuery({q: "cat"})
        assert.equal(url.href, "https://example.com/search?q=cat")
    })
    it("encodes a space as %20", () => {
        assert.equal(buildQuery({q: "black cat"}), "q=black%20cat")
    })
})

The same file runs with node --test and with tacli alike:

node --test test/query.test.mjs

tacli --test test/query.test.mjs

The spec result from tacli, version and user-agent lines omitted:

▶ buildQuery() from an object
  ✔ round-trips through parseQuery() (0.274ms)
  ✔ appends to a URL (0.047ms)
  ✖ encodes a space as %20 (0.093ms)
✖ buildQuery() from an object (1.285ms)
ℹ tests 3
ℹ suites 1
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 2.867

✖ failing tests:

✖ encodes a space as %20 (0.093ms)
  AssertionError: expected "q=black%20cat", got "q=black+cat"

CLI

tacli runs suites that use the supported node:test and node:assert APIs. The test files and their import lines stay unchanged. The browser runners, webdriver-js and chromium-js, firefox-js and webkit-js, accept the same options.

# Run suites in the local Node.js with the library instead of node:test
tacli --test test/*.test.mjs

# Serve suites at http://127.0.0.1:3000/ for your browser
tacli --serve --port 3000 test/browser.test.mjs
  • The first file is the test file. The rest is the script's argv, the same as node file args. --test takes every argument as a test file. Name files directly, as the shell expands globs. CommonJS test files are not supported. -e <script> runs a script in their place.
  • A script may import {argv, stdout, stderr} from "test-assert-lite/process", in a browser as well as in Node.js. The streams write to the CLI's own stdout and stderr, and argv holds the arguments as given. A file that imports them from node:process runs in a browser with --alias node:process=test-assert-lite/process.
  • A run exits 0 when all tests pass and 1 otherwise. Reports go to stdout. Server messages and access logs go to stderr.
  • Reports include a summary unless -q.

-v, --version

  • Prints the version of test-assert-cli and exits.

-e, --eval <script>

  • Runs the script in place of test files: tacli -e "console.log(process.version)".
  • The script is a module. It imports node:test as a test file does. A script that throws counts as one failed test.
  • Arguments after the script go to its argv, from argv[1], the same as node -e.

--test

  • Takes every argument as a test file, as node --test reads them: tacli --test test/*.test.mjs.
  • A run with no tests is reported too, as node --test reports one. Without --test such a run says nothing, as a script under node does.
  • Without it, the first file is the test file and the rest is its argv. A flag meant for the script goes after --: tacli test/cli.test.mjs -- --verbose.

--alias <specifier>=<file>

  • node:test and node:assert are aliased to test-assert-lite already. A test file written for Node.js needs no entry and no change.
  • Maps a specifier to an ES module, for example --alias lodash=node_modules/lodash-es/lodash.js. Repeatable, in every mode.
  • A node: builtin can be the specifier, for example --alias node:crypto=sha256.mjs. The same suite then runs on the same module in Node.js and in a browser.
  • The alias reaches every import of that name, a dependency's too, as an import map does.
  • In the browser modes, the target may also be a URL, for example --alias cdn=https://cdn.example/x.js. In every mode, it may be a test-assert-lite subpath, for example --alias my-test=test-assert-lite/test.

--import-map <file>

  • A JSON import map, for when there are too many specifiers for --alias. Its imports come first, each --alias after, so the command line has the last word.
  • Relative paths start from the map file. See Import Maps below for an example.

--reporter <name>

  • The format of the report: spec (default), tap or html.
  • Or a module to import, for example --reporter test-assert-lite/reporter/tap. Its default export is the reporter, in the shape node --test-reporter takes. When the module fails to import, the run reports with spec and counts one failed test.

-q, --quiet

  • Reduces output while keeping failures visible.
  • A named --reporter keeps its usual format.

--serve

  • With test files, serves the run page. Without them, serves htdocs/, or what --mount names. Prints the URL to open and keeps serving until Ctrl-C.
  • Without test files, htdocs/index.html imports index.js, so --alias index.js=test/browser.test.mjs runs that suite in it.
  • Auto reloads when a test file, a --script file or a locally mapped file changes.

--port <[host:]port>

  • Port the server listens on, with an optional address before it. Default: 127.0.0.1:0, a free port.
  • --port 3000 fixes the port, for an SSH tunnel or a firewall rule that has to name it.
  • For a browser on another machine, listen on an address that machine can reach: --port 192.168.0.2:3000.
  • --port 0.0.0.0:3000 listens on every address. Add --origin then, so the printed URL and the browser runners use an address the browser can reach. An IPv6 literal goes in brackets: --port [::]:3000.

--origin <url>

  • The URL the browser opens, http(s)://host[:port]. Default: the address the server listens on.
  • With --port 0.0.0.0:3000 the default is http://127.0.0.1:3000, which only this machine can open. Give the reachable one: --origin http://192.168.0.2:3000.
  • Through an SSH tunnel or a proxy, the browser opens a different URL than this server listens on. Pass that URL as --origin. The browser runners open it, and --serve prints it.

--script <file>

  • A classic script to run before the suite, for example an IIFE build that sets up a global. Repeatable, in order.
  • A mistyped file shows up as a 404 in the access log on stderr.

--mount <dir|url>

  • A directory, or an origin to proxy, that the root serves in place of htdocs/. For example http://127.0.0.1:8080, so the suite runs in a page of the app under test.
  • Its HTML pages get the import map and the scripts in their head, so a page served by the app can import the library and the suite by name.
  • A page with its own <script type="importmap"> is served unchanged, with no import map and no script or suite tags added. stderr says so.

Import Maps

Map package names to browser-ready ESM files installed by npm:

{
    "imports": {
        "sha256-uint8array": "../node_modules/sha256-uint8array/dist/sha256-uint8array.mjs",
        "int64-buffer": "../node_modules/int64-buffer/int64-buffer.mjs"
    }
}

Relative paths start from the import map file, not the current directory.

For test/import-map.json above:

# Run with the import map in Node.js
tacli --import-map test/import-map.json test/browser.test.mjs
  • node:test, node:assert and test-assert-lite are mapped by default. No entry needed for them.
  • The same map works in Node mode and in the browser modes.
  • A URL can be a key: "https://cdn.example/x.js": "./vendor/x.js" stands a local copy in for the CDN file.

Standing in for a builtin

A library that replaces a Node.js builtin can run one suite against both:

# Run the suite on Node's own crypto
tacli test/sha256.test.mjs

# Run the same suite on your implementation
tacli --alias node:crypto=dist/sha256-uint8array.mjs test/sha256.test.mjs
  • The suite is written for the builtin: import {createHash} from "node:crypto", and no line names the library.
  • --alias puts sha256-uint8array under that name, so the same suite runs on it, in Node.js and in a browser.
  • Any package can stand in this way, so the same suite compares implementations too.

Bundling with Rollup

The library stays out of the bundle. node:test and node:assert are left external, and tacli maps them to the library when the bundle runs, in Node.js and in a browser alike. Good for CI.

// rollup.config.mjs
import multiEntry from "@rollup/plugin-multi-entry"

export default {
    input: "test/*.test.mjs",
    external: [
        "node:assert",
        "node:test",
    ],
    output: {
        file: "htdocs/scripts/bundled-tests.mjs",
        format: "es",
    },
    plugins: [multiEntry()],
    treeshake: false,
}
# Run the bundle in Node.js
tacli --test htdocs/scripts/bundled-tests.mjs

SEE ALSO

  • https://www.npmjs.com/package/test-assert-lite
  • https://www.npmjs.com/package/test-assert-cli
  • https://www.npmjs.com/package/webdriver-js-cli
  • https://www.npmjs.com/package/playwright-js-cli
  • https://github.com/kawanet/test-assert-lite