--- title: Test utilities with @jeeboard/testkit description: Covers deterministic test helpers and the spatial benchmark from the @jeeboard/testkit package. --- # @jeeboard/testkit The `@jeeboard/testkit` package gives you deterministic helpers for tests that use jeeboard. Deterministic means that each run gives the same result. The package also contains a benchmark that measures spatial index query speed. Use this package in your test code. Do not use it in production code. ## Key concepts - **Manual scheduler**: a scheduler that queues tasks and runs them only when you flush a phase. - **Phase**: a named stage of work. The phases are `presentation`, `compilation`, `raster`, and `background`. - **Manual clock**: a clock that returns a fixed time until you advance it by hand. - **Sequential ID generator**: an identifier generator that returns predictable strings such as `test-1` and `test-2`. - **Fixture scene**: a generated array of elements with a regular grid layout. A fixture is fixed test data. - **Test editor**: a bundle that contains an `EditorCore` and a `ManualScheduler`. The types `Clock`, `IdGenerator`, `EditorCore`, and `EditorElement` come from [core](../core/index.md). The spatial index comes from [scene](../scene/index.md). ## Manual scheduler `createManualScheduler` creates a `ManualScheduler`. The scheduler stores tasks per phase. No task runs until you flush it. ```ts import { createManualScheduler } from "@jeeboard/testkit"; const scheduler = createManualScheduler(); const events: string[] = []; scheduler.request(() => events.push("background"), "background"); scheduler.request(() => events.push("presentation"), "presentation"); scheduler.request(() => events.push("raster"), "raster"); scheduler.flush("presentation"); // events is ["presentation"] scheduler.pending("background"); // 1 scheduler.flushAll(); // events is ["presentation", "raster", "background"] ``` The `ManualScheduler` methods are: - `request(task, phase?)` queues a task. The default phase is `background`. - `flush(phase)` runs all queued tasks for one phase. - `flushAll()` runs all phases in the order `presentation`, `compilation`, `raster`, `background`. - `pending(phase)` returns the number of queued tasks for one phase. ## Manual clock `createManualClock` creates a `Clock` with an extra `advance` method. The clock time does not change until you advance it. ```ts import { createManualClock } from "@jeeboard/testkit"; const clock = createManualClock(100); clock.advance(25); clock.now(); // 125 ``` The parameter of `createManualClock` is the start time. The default start time is `0`. The parameter of `advance` is the number of milliseconds to add. NOTE: `advance` throws an error if the value is negative or not finite. ## Sequential ID generator `createSequentialIdGenerator` creates an `IdGenerator` that counts up from one. ```ts import { createSequentialIdGenerator } from "@jeeboard/testkit"; const ids = createSequentialIdGenerator(); ids.next(); // "test-1" ids.next(); // "test-2" ``` The optional parameter is the prefix. The default prefix is `"test"`. ## Test editor `createTestEditor` creates an `EditorCore` and a `ManualScheduler` together. Pass an array of initial elements to populate the core. ```ts import { createTestEditor } from "@jeeboard/testkit"; const { core, scheduler } = createTestEditor(); ``` The returned object has two read-only fields: `core` and `scheduler`. Use `core` for document state. Use `scheduler` to control when deferred work runs. ## Fixture scenes `createShapeBenchmarkScene` creates `count` shape elements in a grid layout. Each element is 100 units wide and 80 units high. The columns have a spacing of 120 units. The rows have a spacing of 100 units. Each element has the identifier `benchmark-shape-` and the properties `{ shape: "rectangle" }`. ```ts import { createShapeBenchmarkScene } from "@jeeboard/testkit"; const elements = createShapeBenchmarkScene(100); ``` The function is deterministic. Two calls with the same count return the same identifiers. NOTE: `createShapeBenchmarkScene` throws an error if the count is not a non-negative integer. ## Spatial benchmark `benchmarkSpatialIndex` builds a fixture scene, inserts it into a uniform grid index, and runs rectangle queries. It returns a `SpatialBenchmarkResult`: - `elementCount`: the requested number of elements. - `queryCount`: the requested number of queries. - `indexedElements`: the number of elements inserted into the index. - `queriedElements`: the total number of elements returned by all queries. - `elapsedMs`: the total query time in milliseconds. - `averageQueryMs`: the query time divided by the query count. ```ts import { benchmarkSpatialIndex } from "@jeeboard/testkit"; const result = benchmarkSpatialIndex(100, 3); // result.elementCount is 100 // result.indexedElements is 100 ``` The defaults are 100,000 elements and 100 queries. The index cell size is 3,000 units. Each query rectangle is 1,920 by 1,080 units. NOTE: `benchmarkSpatialIndex` throws an error if the element count is not a non-negative integer. It also throws if the query count is not a positive integer. ## Benchmark script The package contains a benchmark script at `test/benchmark.mjs`. The script prints the result as JSON. Run it from the repository with pnpm: 1. Open a terminal in the repository root. 2. Run `pnpm --filter @jeeboard/testkit bench`. 3. Read the JSON result on the standard output. NOTE: The script imports the compiled `dist` output. The `bench` script builds the package before it runs the benchmark. ## API reference See the generated [TypeDoc page for @jeeboard/testkit](../api/modules/testkit_src.html).