| name | xb-testing |
| description | Write sequential asynchronous functional, integration, or simulator tests for xrblocks apps using the testing addon. Use this when you need to mock WebGL/WebAudio in headless environments (like JSDOM / Vitest), simulate user locomotion, trigger controller pointing, raycasts, and select/squeeze hand inputs. Covers `TestRunner` and `TestRunnerConfig`. |
xb-testing: functional & simulator testing
The testing addon (import { TestRunner } from 'xrblocks/addons/testing') provides a headless functional test framework designed to test scripts, locomotion, interaction, and engine lifecycle sequentially under environments like Vitest/JSDOM.
Bootstrapping a test
Use TestRunner.create to spin up a core instance with a spied canvas/WebGL context:
import {describe, it, expect} from 'vitest';
import {TestRunner} from 'xrblocks/addons/testing';
import {MyScript} from './MyScript';
describe('My Functional Test', () => {
it('verifies script interaction', async () => {
const script = new MyScript();
const runner = await TestRunner.create({
scripts: [script],
});
await runner.step(100);
expect(script.someValue).toBe(true);
await runner.destroy();
});
});
Locomotion (movement)
Simulate user camera translation (in strafe, rise, forward offsets relative to camera orientation):
await runner.move([0, 0, -1], {durationMs: 200});
expect(runner.camera.position.z).toBeLessThan(0);
Pointer & click interactions
Simulate hands or controllers pointing at objects and performing selections (pinches/clicks):
await runner.pointTo(1, targetMesh);
await runner.click(1);
await runner.step(250);
expect(targetMesh.clicked).toBe(true);
Error handling
Any error or exception thrown during script lifecycle (init, update, onSelectEnd, etc.) is caught by the test runner. Call step() or check for errors explicitly:
const crasher = new CrashingScript();
const runner = await TestRunner.create({scripts: [crasher]});
await expect(runner.step(16.67)).rejects.toThrow(
'Script crash inside update loop'
);
Notes
- Auto-mocking:
TestRunner automatically stubs AudioContext, AudioListener parameter curves, and WebGLRenderer capabilities transparently.
- Teardown: Always call
await runner.destroy() in your tests to reset the core singleton, otherwise subsequent tests will share state and leak listeners.