| 1 | // Run: tsx src/__tests__/mock-fork-fixture.test.ts |
| 2 | // The browser-dev fixture must resolve through the same identity the transcript |
| 3 | // renders: a dev source that answers a fork read has to name the answer message |
| 4 | // the user is looking at, or the entry can never be enabled in `pnpm dev`. |
| 5 | import { JSDOM } from "jsdom"; |
| 6 | |
| 7 | let passed = 0; |
| 8 | let failed = 0; |
| 9 | function ok(value: unknown, label: string) { |
| 10 | if (value) { passed += 1; process.stdout.write(` PASS ${label}\n`); } |
| 11 | else { failed += 1; process.stdout.write(` FAIL ${label}\n`); } |
| 12 | } |
| 13 | |
| 14 | console.log("\nmock fork fixture"); |
| 15 | |
| 16 | const dom = new JSDOM("<!doctype html><html><body></body></html>", { url: "http://localhost/?mock=demo" }); |
| 17 | globalThis.window = dom.window as unknown as Window & typeof globalThis; |
| 18 | globalThis.document = dom.window.document; |
| 19 | |
| 20 | const { app } = await import("../lib/bridge"); |
| 21 | const { historyMessagesToItems } = await import("../lib/historyItems"); |
| 22 | |
| 23 | const tabs = await app.ListTabs(); |
| 24 | const tab = tabs.find((candidate) => candidate.active) ?? tabs[0]; |
| 25 | ok(Boolean(tab?.id), "the dev mock lists an active tab"); |
| 26 | |
| 27 | const history = await app.HistoryForTab(tab!.id); |
| 28 | const { items } = historyMessagesToItems(history, "h"); |
| 29 | const answers = items.filter((item) => item.kind === "assistant" && item.text.trim()); |
| 30 | ok(answers.length > 0, "the dev source renders assistant answers"); |
| 31 | |
| 32 | const set = await app.ForkTargetsForTab(tab!.id); |
| 33 | ok(Array.isArray(set.targets) && set.targets.length > 0, "the dev source answers with fork targets"); |
| 34 | ok(set.verifiable === true, "a dev source with message identity proves its boundaries"); |
| 35 | |
| 36 | const keys = new Set(items.map((item) => item.id)); |
| 37 | const matched = set.targets.filter((target) => target.messageId && keys.has(`m:${target.messageId}`)); |
| 38 | ok(matched.length > 0, "a target names a message the transcript actually renders"); |
| 39 | ok(matched.every((target) => target.available), "every matched dev target is forkable"); |
| 40 | ok(matched.every((target) => target.messageId === `m:${target.messageId}`.slice(2)), "the target keeps the raw message id the transcript keys on"); |
| 41 | ok(set.targets.some((target) => !target.available && target.reason === "turn_open") === false, |
| 42 | "an idle dev source offers no open turn"); |
| 43 | |
| 44 | console.log(`\n${passed} passed, ${failed} failed, ${passed + failed} total`); |
| 45 | if (failed > 0) process.exit(1); |
| 46 |