Skip to content

Testing

  • Unit — vitest (vp test) for the protocol codec, plugin router, RPC messenger, and transports; JUnit for the Kotlin host; XCTest for the Swift host.
  • Type check — tsc --noEmit (part of vp check).
  • Integration — Playwright e2e against the mock transport exercising the real binary protocol (npm run test:e2e:web).
  • Multi-instance harness — a dev harness hosting N worklet instances behind a management server, asserting per-instance isolation + lifecycle (npm run test:e2e:harness).
  • Native end-to-end — npm run test:ios runs the Swift host’s XCTest suite plus a UI test on the iOS simulator (discovery list, Ping / Payload Echo / Roundtrip / Storage permission); the Android emulator job runs the ReferenceJourneyTest WebView journey.
  • Host invariants — security.android.test.ts and security.apple.test.ts read the host sources and manifests for the docs/security.md §5 invariants; error-codes-parity.test.ts checks that the Kotlin and Swift ErrorCodes match core/messages.

The mock-transport suite proves the protocol, not the real stack. The consumer app additionally runs an end-to-end pass over the built web app served by the worklet’s own loopback server — the same-origin topology that runs on device.

  1. Boot the worklet serving the built web app. Build the web app, bundle the worklet entry, and start it with the web-assets directory mounted at / (dev mode: createWorkletRuntime({ webAssets, deviceMode: false }), auth off, port 8080).
  2. Drive Playwright against the loopback origin — http://127.0.0.1:8080, not the Vite dev server. The page, its WebSocket (same-origin cookie login), and its media URLs all come from one server, exactly like on device.
  3. Assert every rendered photo actually decoded. Wait for img.complete && img.naturalWidth > 0 on every <img>; a 200 response with corrupt bytes fails here because naturalWidth stays 0.
  4. Assert no console errors. Fail the spec on any console.error or uncaught exception.
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') errors.push(msg.text());
});
page.on('pageerror', (err) => errors.push(String(err)));
await page.goto('http://127.0.0.1:8080/');
await expect
.poll(() =>
page
.locator('img')
.evaluateAll((imgs) =>
imgs.every((img) => img.complete && img.naturalWidth > 0),
),
)
.toBe(true);
expect(errors).toEqual([]);

This catches regressions a mock suite cannot: corrupt served images, directive crashes in the real render tree (e.g. dereferencing a useStore marker — see Web Rendering), and stuck-loading wedges that only appear against the real backend.

contract.test.ts freezes the package.json exports keys, the exported value names of each @ekrooh/bare subpath, and the built type surface (dist/*.d.mts, snapshotted in api/public-api.d.mts.snap), so an accidental break of a stability boundary fails CI as a test, not a review note. After an intended, reviewed change: npm run build:pkg && npm run api:snapshot. The Kotlin API (android/api/bare-host.api, ./gradlew :bare-host:apiDump) and the Swift API (ios/public-api.swift.snap, vp test security.apple.test.ts -u) are pinned the same way.

Terminal window
npm run build:pkg # compile dist/ — prerequisite
vp check # format + lint + type-check
vp test # unit tests
npm run docs:check # docs links, imports, snippet type-check
npm run test:e2e:web