// The messaging example, as a person runs it: `wai create` makes the project, `wai dev` runs a // conversation beside a call room, and two browsers open its page. Both join the conversation // and each opens what the other sealed; then one calls the other, the callee's inbox rings, // it answers, and each receives and decodes the other's camera and microphone. // // node wai/examples/messaging/test/messaging.test.mjs import { check, create, dev, dump, finish, frame, launch, open, STEP, tmpdir, until } from "../../../quickstart/test/lib.mjs"; const out = tmpdir("messaging"); const dir = await create("messaging", out); const d = await dev(dir); const browser = await launch(); const pages = []; try { const people = []; for (const name of ["alice", "bob"]) { const p = await open(browser, `${d.url}/`, name); pages.push(p); const m = await frame(p, "/room/messages.html"); await until(`${name}: the messages page ready`, STEP, () => m.evaluate(() => !!(window.msgTest && window.msgTest.join)), 200); const phase = await m.evaluate(() => window.msgTest.join()); if (phase !== "member") throw new Error(`${name} did not join: ${phase}`); const c = await frame(p, "/room/call.html"); await until(`${name}: the call page ready`, STEP, () => c.evaluate(() => window.callTest && window.callTest.state.phase === "ready"), 200); people.push({ p, m, c, key: await c.evaluate(() => window.callTest.state.me) }); } const [A, B] = people; await until("both keyed into one group of two", STEP, async () => { const s = await Promise.all([A, B].map((x) => x.m.evaluate(() => window.msgTest.state))); return s.every((x) => x.members.length === 2 && x.epoch && x.epoch.epoch === s[0].epoch.epoch); }, 300); await A.m.evaluate(() => window.msgTest.send("hello from alice")); await B.m.evaluate(() => window.msgTest.send("hello from bob")); const texts = (v) => v.messages.filter((m) => m.outcome === "accepted").map((m) => m.text).sort().join("|"); for (const x of [A, B]) { const got = await until(`${x.p.name} opens both messages`, STEP, async () => { const t = texts(await x.m.evaluate(() => window.msgTest.view())); return t === "hello from alice|hello from bob" && t; }, 300); check(`${x.p.name}'s page opens and verifies what each member sealed`, true, got); } // Alice calls Bob; his inbox rings; he answers. await A.c.evaluate((k) => { window.callTest.place([k]).catch((e) => { window.callTest.state.error = String(e); }); }, B.key); const ringing = await until("bob's inbox rings", STEP, async () => { const s = await B.c.evaluate(() => window.callTest.state); return s.inbox.find((x) => x.state === "ringing") && s; }, 300); check("the callee's inbox holds the invitation, checked with its call before it rings", ringing.inbox[0].check.ok && ringing.inbox[0].check.caller === A.key); await B.c.evaluate(() => { window.callTest.accept().catch((e) => { window.callTest.state.error = String(e); }); }); const room = (x) => frame(x.p, "/room/index.html"); const principal = async (x) => (await room(x)).evaluate(() => window.roomTest.snapshot().me.principal); for (const x of [A, B]) { await until(`${x.p.name}'s call publishes`, STEP * 2, async () => (await (await room(x)).evaluate(() => window.roomTest && window.roomTest.state.phase)) === "publishing", 500); } const [pa, pb] = [await principal(A), await principal(B)]; for (const [x, other] of [[A, pb], [B, pa]]) { const t = await until(`${x.p.name} decodes the other's call media`, STEP * 2, async () => { const s = await (await room(x)).evaluate(() => window.roomTest.snapshot()); const t = s.subs[other]; return t && t.video.decoded >= 20 && t.audio.decoded >= 30 && t; }, 500); check(`${x.p.name}'s call carries the other's camera and microphone, decoded`, true, `${t.video.decoded} video frames, ${t.audio.decoded} audio frames`); } check("caller and callee publish under the grants their call came with", pa === A.key && pb === B.key); } 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("messaging");