Scenarios are plain .js with an implicit global vocabulary (click,
getByRole, step, your LOGIN / PASSWORD variables). An untouched
editor treats them as broken JavaScript: red squiggles, no completion.
The fix ships with the package and wires itself.
What you get
- Autocomplete and signature help for every DSL function.
- Completion offers exactly what a scenario has:
Math.round,s.trim(),new Map()complete;Date,setTimeout,document, which lint refuses, do not. - Hover docs — the same reference text as this site.
- Locator chains resolve:
getByRole('button').filter(...).first(). UPPER_SNAKEvariables fromunotest/.env/.secretsstop being “undefined” (names only — values never leave those files).- Helpers (
flow_*inunotest/e2e/_helpers/) resolve from source, with go-to-definition into the real file.
How it wires
npx @unotest/web init sets everything up in a new project. In an
existing project, or a checkout without unotest/jsconfig.json,
cd unotest && npx @unotest/web types writes the missing pieces. Runs and lint
never write the jsconfig themselves: a test run adds no files to your
tree, so in a fresh checkout types is the step to take.
Four files are involved:
| File | What it is |
|---|---|
unotest/jsconfig.json | Points the IDE at the declaration files. Sets noLib: the scenario library below is the editor’s whole standard library. Written by init, or by npx @unotest/web types when missing; yours after that. |
unotest/node_modules/@unotest/web/types/unotest-lib.d.ts | The scenario library — Math, JSON, Object, Map, Set, string, number and array methods — cut from TypeScript’s own declarations to what a scenario has. keys(), values() and entries() of a Map or a Set read as arrays, as they run. |
unotest/node_modules/@unotest/web/types/unotest-dsl.d.ts | DSL declarations, shipped with the package — always matches the installed version. The suite is its own npm package, so they live under unotest/, not at the project root. |
.unotest/types/env.d.ts | Your variable names, regenerated from .env / .secrets keys on every lint/run. Gitignored cache. |
Refresh on demand:
cd unotest && npx @unotest/web typesDeliberately NOT type checking
checkJs stays off. The DSL has its own validator with rules an editor
cannot know (npx @unotest/web lint, also run before every scenario
run) — a second, diverging diagnostics engine would only disagree with
it. The editor gains navigation and documentation; correctness stays with
the linter.
ESLint (optional)
If your project runs ESLint, @unotest/eslint-plugin reports the exact
same diagnostics as unotest-web lint — its rules are backed by the same
validator, severities from your unotest/unotest.config.mjs:
npm install --save-dev eslint @unotest/eslint-pluginThat one goes wherever your ESLint already lives — it lints your repository, not the suite package.
import unotest from "@unotest/eslint-plugin";
export default [...(await unotest.configs.recommended())];recommended() wires two rules: @unotest/dsl reports the validator’s
errors, @unotest/dsl-warning its warnings. ESLint gives a severity to a
rule, not to a report, hence two rules over one check — the file is
validated once for both. Which diagnostic is an error and which a warning
comes from linter.rules in unotest/unotest.config.mjs, as for the CLI.
To make warnings fail the build, raise the whole warning rule:
import unotest from "@unotest/eslint-plugin";
export default [ ...(await unotest.configs.recommended()), { files: ["unotest/e2e/**/*.js"], rules: { "@unotest/dsl-warning": "error" } },];Wiring the plugin by hand instead of recommended(), name both rules, or
the warnings are not reported at all:
rules: { "@unotest/dsl": "error", "@unotest/dsl-warning": "warn",}The plugin’s README has the rest: what recommended() sets besides the
rules (files, globals, no-undef).
Remaining squiggles
Two sources that typings cannot fix:
- Spellchecker on domain words inside strings (
'listitem','/dashboard/checkout') — add them to your project dictionary. - WebStorm’s “Implicitly declared global variable” on an assignment
without a declaration (
ts = shell(...)) — declare it,const ts = shell(...).lintreports it too, with the declaration to write.