This documentation is available as Markdown. For the complete index, see llms.txt. Skip to content

Upgrade to scenarios on V8

For the complete documentation index, see llms.txt

Since 0.52.0 a scenario runs on V8, the engine of Node and Chrome, as the JavaScript it is. Most suites move with one command and one run. This page is the whole list of what to check.

1. Update and fix

Terminal window
cd unotest
npm i -D @unotest/web@latest
npx @unotest/web lint --fix

Run these from inside unotest/, the suite’s own package. Its package.json pins the version the suite runs on, here and on a box. From the project root, npm i writes the new version into the root package.json (and creates one if there is none), the suite stays on the old version, and lint warns that it runs a version the suite does not pin. Every command on this page runs from unotest/.

lint --fix writes the one edit the upgrade needs everywhere: a let for every variable assigned without a declaration. In JavaScript such a name is a global, shared by every test of the run, so it is now a lint error (validator:implicit-global). The let goes at the start of the test_*, flow_* or helper, where every step sees the name:

function test_count() {
let n; // written by lint --fix
step("Count the rows", () => {
n = textContent(getByTestId("rows"));
});
step("Check the count", () => {
assertEqual(n, "3");
});
}

Commit what it wrote, then run cd unotest && npx @unotest/web lint again: what it still reports is what --fix cannot decide for you. The next sections cover each case.

2. Run the suite

Terminal window
cd unotest && npx @unotest/web collection regress

Use the collection that holds all your scenarios, or cd unotest && npx @unotest/web e2e <scenario> for one file. A test that passed before and fails now names its line; the cases below are the ones to look for.

What to edit by hand

Map and Set keys are iterators. keys(), values() and entries() of a Map or a Set, and matchAll, now give iterators, as in JavaScript. for…of and a spread read them as before; an array method needs the spread first:

const names = [...seen.keys()].map((k) => k.toUpperCase());
assertEqual([...seen.keys()], ["a", "b"]);

With typescript installed, lint warns on an array method called on an iterator, and a DSL function given one fails naming the line and the spread to write.

No async / await. DSL calls return their result; the runner does the waiting. An async function returns at its first await, and what follows runs after the test is over — its checks included. Remove both words.

No count of statements. A test is cut only by its time budget — setTestTimeout(ms) in the test, else testTimeoutMs in the config or UNOTEST_MAX_DURATION_MS. A long loop that ends is never cut; an endless one fails on the time budget, naming its line.

Continue on a failure finishes the test. In a debug run, Continue on a failure pause ends the test as failed: no statement after the failure runs, the teardown(...) blocks play. To see what follows a failure, fix the scenario and run it again.

sortBy is gone. It came with the old engine; JavaScript’s arrays have no such method, so rows.sortBy('order') fails with TypeError: rows.sortBy is not a function. Use sort:

rows.sort((a, b) => a.order - b.order); // numbers
rows.sort((a, b) => a.name.localeCompare(b.name)); // strings

For {desc: true}, swap a and b.

Errors read as JavaScript’s. A name nothing declares fails as ReferenceError: <name> is not defined (with typescript installed, lint names it first), and a member read on null or undefined fails with JavaScript’s TypeError.

Inherited keys fail the step. An object handed to a DSL function with a key it only inherits — written under __proto__: or made by Object.create(…) — fails the step instead of being sent without that key. Write the keys on the object itself.

UNOTEST_ENGINE is gone. There is one engine. A value left set is ignored; drop it from your environment and CI.

AstExecutor is no longer exported. A test rig that built it runs scenarios through npx @unotest/web e2e or the MCP run_test tool instead.

Lint rule names in the config. If linter.rules in unotest.config.mjs names one of these, update it:

WasNow
validator:implicit-declarationvalidator:implicit-global
validator:unsupported-statementvalidator:top-level-statement
validator:out-of-language, validator:library-member, validator:unsupported-string-memberdrop the entry

A config that still names an old rule loads, and lint warns about the entry; for a renamed rule it names the new id.

What you can now write

Classes, getters, generators, Date, Symbol, instanceof and the built-in objects work as in JavaScript. What a scenario does not have is the APIs of Node and of the browser — see The language is JavaScript.

A scenario runs with the rights of the user who started it, as a Playwright test does: it is not a sandbox.