clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

feat/141-demo-review-fixes
lofi / package / recipes / launch-handler_test.ts
launch-handler_test.ts 3.5K
1import {2 type InstalledAppLaunchParameters,3 type InstalledAppLaunchQueue,4 installInstalledAppLaunchConsumer,5 parseInstalledAppLaunchTarget,6} from "./launch-handler.ts";78function assert(condition: unknown, message: string): asserts condition {9 if (!condition) throw new Error(message);10}1112Deno.test("launch target parser accepts only exact-origin URLs inside path scope", () => {13 const valid = parseInstalledAppLaunchTarget(14 "https://app.example/field-notes/tasks/?id=1#selected",15 "https://app.example/field-notes/",16 );17 assert(valid.ok, `valid launch failed: ${JSON.stringify(valid)}`);18 assert(19 valid.target.url === "https://app.example/field-notes/tasks/?id=1#selected",20 "valid launch URL drifted",21 );2223 const origin = parseInstalledAppLaunchTarget(24 "https://other.example/field-notes/",25 "https://app.example/field-notes/",26 );27 assert(!origin.ok && origin.issue === "outside-origin", "foreign origin passed");2829 const scope = parseInstalledAppLaunchTarget(30 "https://app.example/other/",31 "https://app.example/field-notes/",32 );33 assert(!scope.ok && scope.issue === "outside-scope", "out-of-scope path passed");3435 const credentialed = parseInstalledAppLaunchTarget(36 "https://user:secret@app.example/field-notes/",37 "https://app.example/field-notes/",38 );39 assert(40 !credentialed.ok && credentialed.issue === "credentialed-target",41 "credentialed URL passed",42 );43});4445Deno.test("launch target parser rejects missing, malformed, and unsafe scope configuration", () => {46 const missing = parseInstalledAppLaunchTarget(undefined, "https://app.example/");47 assert(!missing.ok && missing.issue === "missing-target", "missing launch target passed");48 const malformed = parseInstalledAppLaunchTarget("not a URL", "https://app.example/");49 assert(!malformed.ok && malformed.issue === "invalid-target", "malformed target passed");50 let rejected = false;51 try {52 parseInstalledAppLaunchTarget("https://app.example/", "https://app.example/no-slash");53 } catch (error) {54 rejected = error instanceof TypeError;55 }56 assert(rejected, "ambiguous scope configuration passed");57});5859Deno.test("launch consumer feature-detects unsupported browsers", () => {60 const consumer = installInstalledAppLaunchConsumer({61 scope: "https://app.example/",62 onLaunch: () => {63 throw new Error("unsupported consumer ran");64 },65 });66 assert(!consumer.supported, "missing launchQueue was reported as supported");67 consumer.dispose();68});6970Deno.test("latest launch consumer owns delivery and stale disposal is harmless", () => {71 let current: (parameters: InstalledAppLaunchParameters) => void = () => {};72 const queue: InstalledAppLaunchQueue = {73 setConsumer(consumer) {74 current = consumer;75 },76 };77 const calls: string[] = [];78 const first = installInstalledAppLaunchConsumer({79 queue,80 scope: "https://app.example/app/",81 onLaunch: () => calls.push("first"),82 });83 const second = installInstalledAppLaunchConsumer({84 queue,85 scope: "https://app.example/app/",86 onLaunch: (target) => calls.push(target.url),87 onRejected: (issue) => calls.push(issue),88 });89 first.dispose();90 current({ targetURL: "https://app.example/app/tasks/" });91 current({ targetURL: "https://other.example/app/tasks/" });92 assert(93 calls.join(",") === "https://app.example/app/tasks/,outside-origin",94 `unexpected launch delivery: ${calls.join(",")}`,95 );96 second.dispose();97 current({ targetURL: "https://app.example/app/ignored/" });98 assert(calls.length === 2, "disposed consumer still handled a launch");99});