Skip to content

API reference ​

ts
import { afterEach, beforeEach, describe, expect, play, sleep, test } from "keiko";

Tests ​

test(name, fn, { timeout? })A test. fn is usually async. Default timeout 30 000 ms of real time
test.only(name, fn)Run only focused tests
test.skip(name, fn?)Registered, not run
test.todo(name)A reminder in the report
test.sweep(name, grid, fn, options?)One test per combination of param values; fn(game, values)
test.deterministic(name, script, options?)Play script(game) twice and compare every step. { sameSeed: true } keeps Math.random the same
describe(name, fn)Group tests; describe.only and describe.skip apply to everything inside
beforeEach(fn) / afterEach(fn)Run around each test in the file or describe. Outer beforeEach first; inner afterEach first, even after a failure
sleep(ms)Wait in real time, even while a game runs

play(options?) ​

Returns Promise<Playtest<S>>. S types game.state.

OptionDefault
page"index.html"Page to boot
params{ "group.key": value }, set before the first step
readyfirst loads done (debuff: a scene is active)(game) => boolean; keiko plays frames while waiting
timeout10000Real-time limit for ready, in ms
seed1Math.random seed
fps60Virtual clock frame rate
webgltrue (debuff: false)Offer WebGL contexts
viewport{ width: 960, height: 540 }Window size

Playtest<S> ​

Reading: state, scene, tick, time, get(path), param(path), params(), window, stepSeconds, driver.

Playing (each returns the playtest):

wait(duration = 1)Play
press(code), release(code)Hold or let go of a key
hold(code, duration)Press, wait, release
tap(code), tap(x, y)One-step key or pointer press
pointerDown(x, y), pointerMove(x, y), pointerUp()Pointer
set(path, value), set(values)Params
command(name, ...args)Returns the command's result
replay(fileOrScenario)Play scenario steps
await settle(timeout?)Wait in real time for the game's requests to finish
await run(duration, { timeout? })Play, letting async code and loads finish between frames

Asking:

Returns
until(condition, { within?, message? })Steps taken. TimeoutError if not within within (default "10s")
await eventually(condition, { within?, message?, timeout? })Steps taken. Like until, letting async code and loads finish between frames
during(duration, check, message?)The playtest. InvariantError at the step where check returned false or threw
trace(pathOrFn, duration)A Trace

A duration is a step count (30) or a time ("0.5s", "250ms").

Trace<T> ​

values, start, first, last, length, at(i), min(), max(), range(), stepOf(valueOrPredicate), every(fn), some(fn), changes(), timeAt(step), iterable.

expect(value) ​

toBe, toEqual, toBeAbove, toBeBelow, toBeAtLeast, toBeAtMost, toBeBetween, toBeCloseTo(target, tolerance = 0.001), toBeTruthy, toBeFalsy, toBeDefined, toBeUndefined, toContain, toHaveLength, toMatch, toThrow(message?), and .not.

Errors ​

ExpectationErroractual, expected
GameErrorThe game threw: tick, scene, cause
TimeoutErroruntil ran out of time
InvariantErrorduring caught a broken invariant
MissingHookErrorA test used a hook the game doesn't expose

window.__KEIKO__ ​

state(), scene(), step(n) + stepSeconds, press(code), release(code), pointerDown(x, y), pointerMove(x, y), pointerUp(), params(), setParam(group, key, value), command(name, ...args). All optional. See Exposing your game.

CLI ​

text
keiko [files or folders...] [options]

  -t, --filter <text>   Only tests whose name contains this text
      --root <dir>      Project root (default: current folder)
      --json            Machine-readable report
      --debuff          Drive through debuff's dev hooks (default: detected)
      --no-debuff       Drive through __KEIKO__ and the virtual clock
  -h, --help

keiko/runner ​

ts
import { findTests, formatResult, formatSummary, runTests } from "keiko/runner";

const report = await runTests({ root, files?, filter?, vite?, debuff?, onResult? });
// { ok, passed, failed, skipped, todo, durationMs, results, outside }