The TypeScript runner
ess verify conform synthesize --target typescript writes the suite a specification obliges as an
ESM test package that node --test runs. Run your first conformance suite
walks through one; this page lists what the package holds and what your implementation provides.
It needs Node.js 20 or later.
What ess writes
From the tasks project of the earlier pages:
$ cd ~/tasks
$ ess verify conform synthesize --path . --target typescript --out typescript
note: every declared actor may invoke `tasks.list.AddTask`, so no actor is refused it and no `tasks.list.AddTask/grant/denied` scenario is owed
note: every declared actor may invoke `tasks.list.CompleteTask`, so no actor is refused it and no `tasks.list.CompleteTask/grant/denied` scenario is owed
6 scenario(s) (0 authored), 0 refusal(s), 15 file(s) written to typescript
The package is essconform, one directory below --out:
README.md
ir.json
package.json
src/coordinate.ts
src/direct_response.ts
src/explore.ts
src/fixtures.ts
src/index.ts
src/one_time_response.ts
src/predicate.ts
src/reading.ts
src/response.ts
src/runtime.ts
suite.json
tsconfig.json
| File | What it is |
|---|---|
suite.json | the suite: one scenario per outcome, lifecycle move and refusal |
ir.json | the compiled specification, which the explorer interprets as its reference model |
src/runtime.ts | the runner: run, runWith, the Target interface and its request and result types |
src/predicate.ts, src/response.ts, src/reading.ts, src/coordinate.ts, src/fixtures.ts | what the runner asserts with: guards, returned values, clock readings and supplied values |
src/direct_response.ts, src/one_time_response.ts | typed direct responses and one-time response disclosure checks |
src/explore.ts | explore and exploreConcurrent, seeded random command sequences |
src/index.ts | the entry point your test imports from |
package.json, tsconfig.json | the package: npm test compiles src/ into dist/ and runs dist/*.test.js |
README.md | the wiring for this suite, and the report format it requires |
More columns: swipe horizontally, or focus the table and use the arrow keys.
Beside the package, typescript/.ess-output/state.json records which files ess wrote there. Nothing in essconform is edited by hand; a file
you add, such as src/conformance.test.ts, is kept when you regenerate.
The package has no runtime dependencies. Before you add anything to it, it installs and typechecks without a finding:
$ cd typescript/essconform
$ npm install
…
$ npm run --silent typecheck
What your implementation provides
One object satisfying Target, exactly as src/runtime.ts declares it. A method marked ? is
optional:
export interface Target {
fixtureValues?(
scenario: ScenarioContext,
contract: FixtureContract,
): Answer<Record<string, Node>>;
identity(): Answer<Identity>;
beginScenario(scenario: ScenarioContext): Answer<void>;
endScenario(scenario: ScenarioContext): Answer<void>;
executeCommand(request: CommandRequest): Answer<CommandResult>;
awaitOutcome?(request: AwaitRequest): Answer<AwaitAnswer>;
executeCommandWithoutInput?(request: AbsentInputRequest): Answer<CommandResult>;
queryView(request: ViewRequest): Answer<ViewResult>;
observeEvents(request: EventObservationRequest): Answer<ObservedEvent[]>;
configureExternalOutcome(control: ExternalOutcomeControl): Answer<void>;
configureExternalOutcomeRepeatedly?(
control: ExternalOutcomeControl & { times: number },
): Answer<void>;
redeliverEvent(request: RedeliveryRequest): Answer<void>;
deliverEvent?(request: EventDeliveryRequest): Answer<void>;
observeInvocations(request: InvocationObservationRequest): Answer<Invocation[]>;
}
| Method | What it answers |
|---|---|
identity | the implementation's name and version, for the report |
beginScenario, endScenario | bracket one scenario; state from one scenario must not satisfy another |
executeCommand | run one command; return the outcome taken, the declared error if it refused, and the events it emitted directly |
awaitOutcome | for a command every actor of which is declared system: true, which is never sent: the outcome the system reached on the requested instance, the input it issued the command with and what it published, or {reached: false}; hold the system's step until the runner awaits it, and report your clock's reading in elapsedMillis where the request carries within |
queryView | read one view; a read_your_writes view must already show the command that just returned |
observeEvents | the events seen for one activity, away from the command that caused them |
configureExternalOutcome | force the next answer of an outcome declared external: |
redeliverEvent | deliver an event a second time, for delivery: at_least_once |
observeInvocations | the commands one binding invoked and what it passed |
More columns: swipe horizontally, or focus the table and use the arrow keys.
A method the implementation cannot answer throws ErrUnsupported. Report/2 records that scenario
as unsupported, and the default strict run fails. Ordinary target errors are recorded separately
as error and make execution inconclusive. A specification that declares backend setup, periodic hosts or clock
readings asks for a further interface beside Target (EntitySetupTarget, PeriodicTarget,
ClockReadingTarget); the generated README.md names the ones its suite needs.
Run it
Hand a factory to run from one test file under src/; the runner builds one target per
scenario:
import { test } from "node:test";
import { run } from "./index.js";
import type { Target } from "./index.js";
await test("conformance", async (t) => {
await run(t, (): Target => newTarget());
});
Then run npm test with ESS_REPORT_FORMAT=2 set; ESS_REPORT_OUT names a file for the report:
$ ESS_REPORT_FORMAT=2 ESS_REPORT_OUT=report.json npm test
Verify conformance covers authored scenarios, coverage and the report in full.