open-standards/wai/examples/watch-together/test/watch-together.test.mjs
Transaction Science 632f8cf394 wai: the developer's path, hello, create, dev and verify, a floor build with no system library, and nine examples
wai-rs gains the feature floor (the mandatory floor of SPEC 5: PNG, FLAC and
zstd, decoded with no system codec library) and quickstart (the floor, and the
receipt checks hello and verify run). The wai command line gains four verbs,
one module each, named in one table (dx::VERBS):

- hello: name, dispatch, rebuild and receipt on a picture made here, the
  receipt laid out as http-delivery 8 lays receipts beside an object;
- create: a new project from one of the examples, whose files the binary
  holds; tests/cli_dx.rs holds them to wai/examples/ byte for byte;
- dev: the relay, the room service and the room page on one loopback origin,
  as a production front serves them; --check checks each piece through it;
- verify: the receipt indexes under a directory, checked offline.

The project file a template writes, wai.json, is read in one place
(dx/project.rs). wai/examples/ holds video-room, watch-together, messaging,
encoder-ingest, package-and-deliver, verify-receipts, apple-app, android-app
and remote-desktop, each with a README and a test that runs it through
wai create and wai dev. wai/quickstart/ builds the room client and the
archive that holds wai, the relay, the room service, the remote host and the
packager.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 22:58:39 -04:00

52 lines
2.9 KiB
JavaScript

// The watch-together example, as a person runs it: `wai create` makes the project (and its
// sample tone), `wai dev` runs the watch room, and two browsers press Watch. One plays, then
// the other pauses: both must apply each control at its instant, within a quarter second of
// the room, and stop at one position.
//
// node wai/examples/watch-together/test/watch-together.test.mjs
import { check, create, dev, dump, finish, frame, launch, open, sleep, STEP, tmpdir, until } from "../../../quickstart/test/lib.mjs";
const out = tmpdir("watch-together");
const dir = await create("watch-together", out);
const d = await dev(dir);
const browser = await launch();
const pages = [];
const BOUND_US = 250000;
try {
const enter = async (name) => {
const p = await open(browser, `${d.url}/`, name);
pages.push(p);
const f = await frame(p, "/room/");
await until(`${name}: the room page ready`, STEP, () => f.evaluate(() => window.roomTest && window.roomTest.state && window.roomTest.state.phase === "idle"), 200);
await f.click("#watch");
const s = await until(`${name}: in the watch room`, STEP, async () => {
const x = await f.evaluate(() => window.roomTest.snapshot());
if (x.errors.length) throw new Error(x.errors[0]);
return x.cowatch && x.cowatch.joined != null && x;
}, 200);
return { p, f, s };
};
const A = await enter("ada");
const B = await enter("ben");
check("each page fetched the room's source and checked it by the content hash its policy names", A.s.cowatch.verified_source && B.s.cowatch.verified_source);
const cws = async () => Promise.all([A, B].map(async (x) => (await x.f.evaluate(() => window.roomTest.snapshot())).cowatch));
const together = (what, n, playing, after) => until(what, STEP, async () => {
const all = await cws();
return all.every((c) => c.controls >= n && c.room_now >= after && c.playing === playing && c.error_us != null && Math.abs(c.error_us) <= BOUND_US && !c.stopped) && all;
}, 250);
const playAt = await A.f.evaluate(() => window.roomTest.watchControl("play"));
const t1 = await together("both play on Ada's control", 1, true, playAt);
check("a play starts both players together, each within a quarter second of the room", true, t1.map((c) => `${(c.error_us / 1000).toFixed(1)} ms`).join(", "));
await sleep(3000);
const pauseAt = await B.f.evaluate(() => window.roomTest.watchControl("pause"));
const t2 = await together("both pause on Ben's control", 2, false, pauseAt);
const pos = t2.map((c) => c.current_time);
check("a pause stops both players at one position", Math.abs(pos[0] - pos[1]) * 1e6 <= BOUND_US && pos[0] > 1, pos.map((x) => x.toFixed(3)).join(" s, ") + " s");
} catch (e) {
check("the example ran to its end", false, (e && e.stack || String(e)).split("\n").slice(0, 3).join(" | "));
} finally {
dump(pages, out);
await browser.close().catch(() => {});
await d.stop();
}
finish("watch-together");