@bemedev/app-vitest
v2.0.0
Published
Test helpers for @bemedev/app
Maintainers
Readme
@bemedev/app-vitest
Declarative test sequence generator for @bemedev/app state machines inside
Vitest.
@bemedev/app-vitest allows you to write state machine integration tests as a list
of sequentially run, declarative test assertions, removing the boilerplate of manual
await-tick-assert cycles.
Installation
npm install @bemedev/app-vitest --save-dev
# or
pnpm add @bemedev/app-vitest -DRequirements: Node.js ≥ 24 ·
@bemedev/app≥ 2.0.0 · Vitest ≥ 4.0.0
Quick Start
import { interpret } from '@bemedev/app';
import { constructTests } from '@bemedev/app-vitest';
import { describe, test, vi } from 'vitest';
import { myMachine } from './my.machine';
describe('My Machine Integration', () => {
const service = interpret(myMachine, { context: { count: 0 } });
// 1. Initialize the declarative helpers
const { start, useStateValue, send, stop } = constructTests(service);
// 2. Define sequence tests by spreading the generated tuples
test(...start());
test(...useStateValue('idle'));
test(...send('INCREMENT'));
test(...useStateValue('active'));
test(...stop());
});API Reference
constructTests( service, helper?, startIndex?)
| Parameter | Type | Description |
| ------------ | ------------- | --------------------------------------------------------------------------------------------- |
| vi | VitestUtils | The Vitest vi utility object (required for fake timer management). |
| service | Interpreter | The interpreter service instance under test (Sync or Async). |
| helper | Function | Optional callback to define custom helpers (e.g. context assertions or custom event senders). |
| startIndex | number | Optional starting sequence index (defaults to 0). |
Returns an object containing built-in assertions and any custom helpers returned by
the helper callback.
Built-in Assertion Helpers
Every function returns a TestArr (tuple of [inviteString, testCallback]) designed
to be spread directly into Vitest's test(...) function:
start(index?): Starts the service and awaits initial task settlement.stop(index?): Stops the service cleanly.dispose(index?): Alias forstop(index?).pause(index?): Pauses the interpreter service activities and timers.resume(index?): Resumes the interpreter service.send(event, index?): Sends an event to the service and awaits transition settlement.useStateValue(value, index?): Asserts that the current active state value matchesvalue.useTags(...tags): Asserts that the current state carries the specified active tags.useWarnings(...warnings): Asserts that the service has logged the specified warning messages in its warning collector.useErrors(...errors): Asserts that the service has logged the specified error messages in its error collector.changeIndex(fn): Modifies the running test sequence index dynamically.unhandledRejection(testFn, error, timeout?): Asserts that runningtestFnrejects with the expectederrormessage (setup forunhandledRejectionanduncaughtException).
Custom Option Helpers
The third argument helper receives a configuration object exposing helper factories
to create customized, type-safe assertions:
const { wait, sendFetch, checkCount } = constructTests(
vi,
service,
({ waiter, sender, contexts, service }) => ({
// 1. A custom delay helper (automatically advances fake timers if active)
wait: waiter(500),
// 2. A strongly-typed event sender
sendFetch: sender('FETCH'),
// 3. A custom context selector assertion
checkCount: contexts(({ context }) => context.count, 'count'),
}),
);Helper Factories:
service: The underlying interpreter service instance (Sync or Async) under test.waiter(defaultDelay?): Returns a function to wait for a delay in milliseconds. If Vitest fake timers are active, it automatically advances them usingvi.advanceTimersByTimeAsync().sender(eventType): Returns a function to send a specific event type with its payload arguments.contexts(selector?, name?): Returns a function asserting that the resolved value from the selector matches the expected value.
Advanced Example (with Fake Timers)
import { interpret } from '@bemedev/app';
import { constructTests } from '@bemedev/app-vitest';
import { describe, test, vi, afterAll } from 'vitest';
import { timerMachine } from './timer.machine';
vi.useFakeTimers();
describe('Timer Machine tests', () => {
const service = interpret(timerMachine, { context: { duration: 1000 } });
const { start, useStateValue, wait, send } = constructTests(
service,
({ waiter }) => ({ waitSecond: waiter(1000) }),
);
test(...start());
test(...useStateValue('idle'));
test(...send('START'));
test(...useStateValue('running'));
// Automatically advances Vitest fake timers by 1000ms
test(...waitSecond());
test(...useStateValue('completed'));
});
afterAll(() => vi.useRealTimers());License
MIT
