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.
Maintainers
Readme
test-assert-cli
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:testandnode:assertimports.taclimaps them to test-assert-lite --servehosts your tests as a web page, with live reload--aliasand--import-mapmap 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-cliSYNOPSIS
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.mjsThe 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.--testtakes 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, andargvholds the arguments as given. A file that imports them fromnode:processruns 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:testas a test file does. A script that throws counts as one failed test. - Arguments after the script go to its
argv, fromargv[1], the same asnode -e.
--test
- Takes every argument as a test file, as
node --testreads them:tacli --test test/*.test.mjs. - A run with no tests is reported too, as
node --testreports one. Without--testsuch a run says nothing, as a script undernodedoes. - 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:testandnode:assertare 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. Itsimportscome first, each--aliasafter, 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),taporhtml. - Or a module to import, for example
--reporter test-assert-lite/reporter/tap. Its default export is the reporter, in the shapenode --test-reportertakes. When the module fails to import, the run reports withspecand counts one failed test.
-q, --quiet
- Reduces output while keeping failures visible.
- A named
--reporterkeeps its usual format.
--serve
- With test files, serves the run page. Without them, serves
htdocs/, or what--mountnames. Prints the URL to open and keeps serving until Ctrl-C. - Without test files,
htdocs/index.htmlimportsindex.js, so--alias index.js=test/browser.test.mjsruns that suite in it. - Auto reloads when a test file, a
--scriptfile 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 3000fixes 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:3000listens on every address. Add--originthen, 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:3000the default ishttp://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--serveprints 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 examplehttp://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.mjsnode:test,node:assertandtest-assert-liteare 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. --aliasputs 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.mjsSEE 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
