Rust native tests and browser builder
Run the tests natively
Cargo is the canonical native diagnostic host. Run the package's unit tests and README doctest directly:
cargo test
cargo test --docBuild the browser harness
The Cargo subcommand compiles the selected package's existing library tests to wasm32-unknown-unknown and emits one static test directory.
cargo vanilla-test --browser [options]
cargo vanilla-test --browser
cargo vanilla-test --browser --bench lifecycle
cargo vanilla-test --browser --out-dir dist/browser-tests
cargo vanilla-test --browser --manifest-path crates/app/Cargo.toml
cargo vanilla-test --browser --page tests/browser.html| Option | Behavior |
|---|---|
--browser | Required explicit browser-build mode. |
--bench <name> | Compile one named benchmark beside the tests and emit vanilla-test-benchmark.wasm. |
--out-dir <path> | Output directory. Default: dist/vanilla-test. |
--manifest-path <path> | Select a package or workspace member by its Cargo.toml. |
--page <path> | Read a page relative to the selected package directory, or from an absolute path, and copy it byte-for-byte as index.html. No HTML parsing, build-time validation, or asset copying. |
--help / --version | Print Cargo subcommand help or the installed crate version. |
A custom page keeps ./browser.js and the documented result hooks; deploy any referenced assets separately. The adapter takes one snapshot when it starts. Each data-rust-browser-site-check must be connected, visible, and contain nonempty rendered text; optional data-rust-browser-expected requires an exact text match. These host checks run beside—not instead of—the same Rust harness.
The zero-import Rust module cannot call JavaScript functions, the DOM, or browser APIs. The host does not click, type, navigate, wait, retry, or make custom network assertions. Test JavaScript functions in the JavaScript lane; see the complete Rust browser contract.
C# native tests and browser builder
Run the tests natively
The standard .NET test host remains the complete native path:
dotnet test
dotnet test tests/App.Tests/App.Tests.csproj --configuration ReleaseBuild a browser test or benchmark
The .NET tool publishes either the selected MSTest project with its focused parameterless-test host or a benchmark executable with a managed browser export.
dotnet vanilla-test --browser [options]
dotnet vanilla-test --browser
dotnet vanilla-test --browser --project tests/App.Tests/App.Tests.csproj
dotnet vanilla-test --browser --out-dir dist/browser-tests
dotnet vanilla-test --browser --page tests/browser.html
dotnet vanilla-test --browser --bench \
--project benchmarks/App.Benchmarks/App.Benchmarks.csproj| Option | Behavior |
|---|---|
--browser | Required explicit browser-build mode. |
--bench | Build the selected executable as a browser benchmark. Its --browser-sample mode must execute one validated workload and return status 0. |
--project <path> | Select an MSTest or benchmark .csproj; otherwise the current directory must contain exactly one project. |
--out-dir <path> | Project-relative output directory. Defaults to dist/vanilla-test, or dist/vanilla-test-benchmark with --bench. |
--page <path> | Use a compatible site-test page instead of the built-in runner; unavailable with --bench. |
--help / --version | Print tool help or the exact installed tool version. |
The output contains index.html, browser.js, runtests.mjs, and the published _framework/ tree. Keep the directory intact: .NET browser WebAssembly is a coordinated multi-file AppBundle, not one standalone .wasm artifact.
Benchmark mode instead emits index.html, benchmark.js, runbenchmark.mjs, and its own complete _framework/ tree. Runtime initialization is outside each managed sample; the benchmark executable owns the workload and validation.
npx --yes node-http-server@10.0.0 --root dist/vanilla-test --port 4173Serve the entire generated directory together. The pinned node-http-server command preserves the AppBundle paths; no repository dependency is required.
A custom page keeps the documented C# result hooks and ./browser.js. Mark rendered text with data-csharp-browser-site-check and optional data-csharp-browser-expected; page checks run beside the shared C# test assembly.
The browser host runs public parameterless [TestMethod] methods, including documented initialize, cleanup, ignore, async, and disposal behavior. Use native dotnet test for the full MSTest platform and detailed diagnostics.
JavaScript coverage commands
vanilla-test coverage [all|node|chrome] [options]
vanilla-test coverage
vanilla-test coverage all
vanilla-test coverage node
vanilla-test coverage chrome
# equivalent short aliases
vanilla-test all
vanilla-test node
vanilla-test chromeThe suite report and any collector errors appear there. Use the process exit status for automation and the generated report directory for coverage details.
coverage, coverage all, and the all alias run Node first, then Chrome. The node and chrome aliases select one collector. Running vanilla-test with no arguments prints help and exits with status 0.
An ordinary assertion or threshold failure in Node does not discard its report or prevent the Chrome collector from producing its own report.
Repository contributors can use the npm scripts below. Run a script exactly as shown; append CLI options after --.
| npm command | Underlying command | Purpose |
|---|---|---|
npm test | npm run test:core && npm run test:tooling | Run every core and tooling test. |
npm run test:core | node ./test/node.js | Run the shared core suite directly in Node.js. |
npm run test:tooling | node --test ./test/tooling.js ./test/output.js ./test/server-security.js ./test/status-builder.js ./test/benchmark.js | Run CLI, report, output, server-security, benchmark-harness, and site-status tests. |
npm run benchmark | node ./benchmark/run.js | Run the one-million-case native Node and Chrome end-to-end benchmark after npm ci --prefix benchmark. |
npm run benchmark:smoke | node ./benchmark/run.js --cases 101 ... | Verify all benchmark adapters, native collectors, result validation, and report writes with a small workload. |
npm run coverage | node ./bin/vanilla-test.js coverage | Run the Node and Chrome collectors. |
npm run coverage:node | node ./bin/vanilla-test.js coverage node | Run only Node coverage. |
npm run coverage:chrome | node ./bin/vanilla-test.js coverage chrome | Run only Chrome coverage. |
npm run site:status | node ./scripts/build-site-status.js --run-tooling | Refresh site status data and Shields badges from current artifacts. |
npm run screenshots | node ./scripts/screenshots.js | Smoke-test the playground in Chrome, then regenerate the browser and native-report screenshots after coverage. |
npm start | node ./scripts/serve.js | Serve the repository and documentation locally. |
npm run coverage -- --timeout-ms 60000
npm run coverage:chrome -- --headed
npm run coverage:chrome -- --chrome-path "/opt/google/chrome/google-chrome"Test output and failures appear there. Coverage metrics are written to the configured reportsDirectory.
Options
| Option | Behavior |
|---|---|
--config <path> | Use a JSON configuration file. Default: vanilla-test.config.json in the current directory. |
--chrome-path <path> | Launch an explicit Google Chrome executable. The resolved target must be an executable file. |
--headed | Show Chrome during the browser run, overriding chrome.headless. |
--timeout-ms <ms> | Override timeoutMs with a positive integer up to 3,600,000. |
--help | Print command usage. |
--version | Print the installed package version. |
Options may appear once. Unknown arguments, duplicate options, or options missing a value are usage errors.
Configuration
vanilla-test works with bundlers and without a bundler. The Chrome collector runs native browser ESM with a generated import map and no build or transpilation step. Paths are resolved from the directory containing the configuration file, not from whichever directory contains the installed package.
{
"entry": "./test/CI.js",
"reportsDirectory": "./coverage",
"thresholds": {
"statements": 100,
"branches": 100,
"functions": 100,
"lines": 100
},
"timeoutMs": 30000,
"node": {
"include": ["index.js"]
},
"chrome": {
"include": ["index.js"],
"imports": {},
"scopes": {},
"headless": true,
"executablePath": null
}
}| Key | Required/default | Contract |
|---|---|---|
entry | Required | Existing project-local module exporting a default function or named run(). |
reportsDirectory | ./coverage | Project-local parent for atomically staged, runtime-owned reports. |
thresholds | Every metric defaults to 100 | If present, contains all four numeric percentages—statements, branches, functions, and lines—from 0 through 100. |
timeoutMs | 30000 | Integer from 1 through 3,600,000. |
node | Required for all/node | Node collector object. It may be omitted for a Chrome-only run. |
node.include | Required with node | Nonempty array of positive project-relative globs. At least one file must match. |
chrome | Required for all/chrome | Chrome collector object. It may be omitted for a Node-only run. |
chrome.include | Required with chrome | Independent nonempty include scope for Chrome. At least one file must match. |
chrome.imports | {} | Additional or overriding global browser specifiers mapped to files inside the project. Defaults map vanilla-test plus root ansi-colors-es6 and strong-type entries when present. |
chrome.scopes | {} | Additional or overriding importer scopes. Generated defaults scope ./node_modules/vanilla-test/ to nested-first dependency files, preserving Node and bundler resolution during version conflicts. |
chrome.headless | true | Boolean selecting visible or headless Chrome. CLI --headed forces false. |
chrome.executablePath | null | String path or null. A config path is resolved from the configuration directory. CLI --chrome-path takes precedence. |
When you run only one collector, its sibling configuration object is optional: a Node-only configuration may omit chrome, and a Chrome-only configuration may omit node. The all target requires both objects. Import and scope targets must stay inside the project; configured values override matching defaults.
Choose Chrome with executablePath
null is the portable default, not a placeholder. It tells vanilla-test to check CHROME_PATH, standard Google Chrome Stable locations on Windows, macOS, and Linux, then Chrome executable names available on PATH.
{
"chrome": {
"include": ["index.js"],
"imports": {},
"scopes": {},
"headless": true,
"executablePath": null
}
}Use a committed string when every machine shares one nonstandard installation. JSON requires Windows backslashes to be escaped:
{
"chrome": {
"include": ["index.js"],
"imports": {},
"scopes": {},
"headless": true,
"executablePath": "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
}
}For machine-specific and CI installations, leave the config at null and use an environment or command-line override:
# PowerShell
$env:CHROME_PATH = 'C:\Program Files\Google\Chrome\Application\chrome.exe'
npm run coverage:chrome
npm run coverage:chrome -- --chrome-path 'D:\Browsers\Chrome\chrome.exe'
# macOS or Linux shell
CHROME_PATH=/usr/bin/google-chrome-stable npm run coverage:chrome
npm run coverage:chrome -- --chrome-path "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"A missing, non-file, or non-executable Chrome path is reported as a harness error with exit status 2.
Keep Chrome's sandbox enabled by default. On an isolated Linux CI runner that cannot provide a usable Chrome sandbox, opt out for that job only:
VANILLA_TEST_CHROME_NO_SANDBOX=1 npm run coverageThe variable accepts only 0, 1, an empty value, or an unset value; empty and unset both preserve the sandbox. 1 adds Chrome's --no-sandbox flag and reduces browser isolation; do not use it on a general-purpose workstation or an untrusted shared runner.
| Precedence | Source | Path base |
|---|---|---|
| 1 (highest) | --chrome-path <path> | Current shell directory |
| 2 | String chrome.executablePath | Configuration-file directory |
| 3 | CHROME_PATH | Environment value |
| 4 | Stable-location and PATH discovery | Operating system |
Entry result contract
The imported entry may return synchronously or asynchronously. Its result must expose consistent ok and failureCount values:
{ ok: true, failureCount: 0 }
{ ok: false, failureCount: 2 }Missing, negative, non-integer, or contradictory values are harness failures. This prevents an incomplete or malformed suite from being treated as a successful run.
Path and import safety
- Entry, reports, included source, and browser-import targets must stay inside the configuration root after real-path resolution.
- Include scopes accept positive globs only; absolute paths, parent traversal, and
!negation are rejected. - Browser import targets must be local files. Remote URLs and protocol-relative URLs are rejected.
- The temporary browser server permits only
GETandHEAD, requires its exact loopbackHost, denies dotfiles and common secret/key paths, and rejects traversal and link escapes. - The Chrome page blocks requests outside the bound coverage origin; local responses add CSP, CORP, no-referrer, no-sniff, frame-denial, and no-store headers.
- Unknown configuration keys are errors rather than silently ignored settings.
Keep source scope and browser imports narrow enough that a coverage run cannot accidentally serve or measure unrelated files.
Exit statuses
| Status | Meaning | CI interpretation |
|---|---|---|
0 | Tests and coverage thresholds passed. | Success. |
1 | An assertion failed or a coverage threshold was missed. | Product/test failure. |
2 | Usage, configuration, missing runtime prerequisite, launch, timeout, or malformed-result failure. | Harness/infrastructure failure. |
130 | The process was interrupted by SIGINT or SIGTERM. | Cancelled/interrupted run. |
Output layout
coverage/
node/
.vanilla-test-coverage.json
index.html
lcov.info
coverage-summary.json
test-results.json
chrome/
.vanilla-test-coverage.json
index.html
lcov.info
coverage-summary.json
test-results.json
vanilla-test-chrome.png| Artifact | Meaning |
|---|---|
index.html | Standalone project-owned native V8 coverage report. |
lcov.info | Native metrics in conventional LCOV transport form. |
coverage-summary.json | Aggregate and per-file native metric totals. |
test-results.json | Plain, ANSI-free suite status, counts, and descriptions. |
.vanilla-test-coverage.json | Runtime ownership marker used to protect unrelated directories. |
vanilla-test-chrome.png | Successful Chrome harness screenshot; Chrome output only. |
A collector builds a complete report in a temporary sibling directory. After a valid completed run—even a test or threshold failure—it atomically replaces only a report carrying the matching ownership marker. An unowned directory is refused. Configuration, harness, timeout, collector, or interruption failures clean up staging and preserve the previous known-good report. Running both collectors retains both final reports. The documentation site's coverage page normalizes those summaries and links the complete published reports.
CI recipe
- run: npm ci
- run: npm test
- run: npm run coverage -- --chrome-path "$CHROME_PATH"Keep the exit status and uploaded runtime reports as separate evidence for each gate.
Install Google Chrome Stable explicitly in CI and log the exact Node and Chrome versions beside the artifacts. Treat the Node test matrix, coverage collectors, and packed-artifact smoke as separate required gates before publishing quality data.