Types and imports
Let TypeScript check your app names, set up the tests tsconfig, and write imports the way Retest loads them.
Retest runs .ts files as they are, with no build step. It removes the types with the transform built into Node.js. It does not check types, so run tsc for that.
Typed names Link to Typed names
The declare module block in the config registers its type once, for every file in the same TypeScript program.
declare module '@rehearsal-labs/retest' { interface Register { config: typeof config }}The type check then knows your names:
appstakes only the config's app names, and a test can use only the apps it declares.tags,state,secret()andgetByTestId()take only the names the config lists. A config that lists notags,statesortestIdsaccepts any string for them.lockstakes only the config's locks.- Without a registered config, a test cannot declare
appsorstate, and the error says how to register.
A registration applies to the whole program, so keep a registered project in a tsconfig of its own. Retest checks the same names again when it loads the tests, for code with no types.
Typed capabilities Link to Typed capabilities
What a page can do depends on what it is, and the types say so:
tap()exists only on an app whose every target has a touch screen.- An iOS simulator or macOS app has no
goto,select,checkoruncheck. Its elements taketap()on iOS andclick()on macOS. - An Electron app has no
goto: its page is the first window the app opens. - A judge name with a typo, or a screenshot for a judge that reads only text, fails to compile.
// A type error: "A macOS app takes no swipe. Use scroll()."await desk.swipe('up')The tests tsconfig Link to The tests tsconfig
retest init writes tests/tsconfig.json. It extends your project's own tsconfig.json when there is one, and its options accept what Retest loads and refuse what it does not.
{ "compilerOptions": { "target": "es2024", "module": "esnext", "moduleResolution": "bundler", "types": ["node"], "strict": true, "noEmit": true, "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, "erasableSyntaxOnly": false, "noUncheckedIndexedAccess": true, "skipLibCheck": true }, "include": ["../retest.config.ts", "**/*"]}The typecheck:e2e script init adds runs tsc -p tests/tsconfig.json.
Imports Link to Imports
- Test files, helpers and the config are ES modules, in
.ts,.mts,.jsor.mjsfiles. Set"type": "module"inpackage.json. - A relative import works with or without its extension.
./helperfinds./helper.ts, then./helper.js,./helper.mtsand./helper.mjs, then the folder'sindex.tsorindex.js. ./helper.jsloads./helper.tswhen that file exists, astscreads it.- Enums, namespaces with values and parameter properties load too.
Path aliases Link to Path aliases
Retest reads the paths of the tsconfig.json nearest the importing file. It follows extends, allows comments and trailing commas, and reads paths from baseUrl when one is set.
{ "compilerOptions": { "paths": { "@/*": ["./src/*"] } }}An alias tries its targets in order, each with the extensions above. TypeScript 7 removed baseUrl, and paths work the same without it. An import that cannot be found names the tsconfig.json that governed it.
What does not load Link to What does not load
These fail as their file loads, with a message that names the construct and the file:
- CommonJS test files and configs:
.ctsfiles,require(),module.exportsand__dirname. Writeimportandexport. - JSX, in
.tsxand.jsxfiles or inside a.tsfile. - Decorators, which Node.js cannot run.
- A type imported without
type. Writeimport type { Title } from './titles.ts'.
TypeScript 6 and 7 Link to TypeScript 6 and 7
Retest's types are checked with both TypeScript 6 and TypeScript 7, and so is the tsconfig init writes. Use whichever your project has.
Source maps Link to Source maps
Types are removed in place, so every line and column stays where it was. A file with an enum, a namespace with values or a parameter property is transformed instead, with a source map. Either way, a failure names its TypeScript line and column.
Retest has run Node's TypeScript transform on Node.js 24.12, which is why it asks for 24.12 or later.