import { describe, expect, test } from "bun:test"; import { actionActivateViewSchema as legacyActionActivateViewSchema } from "./core-view-host-actions.js"; import { actionActivateViewSchema, actionCloseViewSchema, actionPrerenderViewSchema, coreViewHostActionSchema, } from "./core-view-host-actions-v3.js"; import { createGeneration } from "./v3-generation.js"; describe("core view host actions", () => { test("accepts a URL-only hosted source inside the lifecycle discriminator", () => { const message = { t: "getuserfeedback:action:prerenderView", gen: createGeneration(4), viewId: "view-hosted", flowRunId: "run-hosted", instanceId: "instance-a", hosted: { url: "https://view.example.com/document.html" }, }; expect(coreViewHostActionSchema.parse(message)).toEqual(message); }); test("rejects ambiguous, missing, or message-supplied trust sources", () => { const envelope = { t: "getuserfeedback:action:prerenderView", gen: createGeneration(4), viewId: "view-hosted", flowRunId: "run-hosted", instanceId: "instance-a", }; for (const source of [ {}, { srcdoc: "", hosted: { url: "https://view.example.com" } }, { srcdoc: "" }, { hosted: {} }, { hosted: null }, { hosted: { url: "" } }, { hosted: { url: 42 } }, { hosted: { url: "https://view.example.com", trustedOrigin: "https://view.example.com", }, }, { hosted: { url: "https://view.example.com" }, trustedOrigin: "https://view.example.com", }, ]) { expect( coreViewHostActionSchema.safeParse({ ...envelope, ...source }).success, ).toBe(false); } }); test("keeps the published activation action shape unchanged", () => { const legacyAction = { t: "getuserfeedback:action:activateView", gen: createGeneration(4), viewId: "view-a", flowRunId: "run-a", instanceId: "instance-a", }; expect(legacyActionActivateViewSchema.safeParse(legacyAction).success).toBe( true, ); expect( legacyActionActivateViewSchema.safeParse({ ...legacyAction, activationAttemptId: "activation-a", }).success, ).toBe(false); }); test("parses the lifecycle actions emitted by core", () => { const gen = createGeneration(4); expect( coreViewHostActionSchema.parse({ t: "getuserfeedback:action:prerenderView", gen, viewId: "view-a", flowRunId: "run-a", srcdoc: "", instanceId: "instance-a", openRequest: { source: "command", flowId: "flow-a", requestId: "request-a", }, }), ).toEqual({ t: "getuserfeedback:action:prerenderView", gen, viewId: "view-a", flowRunId: "run-a", srcdoc: "", instanceId: "instance-a", openRequest: { source: "command", flowId: "flow-a", requestId: "request-a", }, }); expect( coreViewHostActionSchema.parse({ t: "getuserfeedback:action:activateView", activationAttemptId: "activation-a", gen, viewId: "view-a", flowRunId: "run-a", instanceId: "instance-a", openRequest: { source: "command", flowId: "flow-a", requestId: "request-a", flowHandleId: "handle-a", hideCloseButton: true, }, }), ).toMatchObject({ t: "getuserfeedback:action:activateView", viewId: "view-a", }); expect( coreViewHostActionSchema.parse({ t: "getuserfeedback:action:activateView", activationAttemptId: "activation-inline", gen, viewId: "view-inline", flowRunId: "run-inline", instanceId: "instance-a", openRequest: { source: "command", requestId: "request-inline", flowHandleId: "handle-inline", }, }), ).toMatchObject({ t: "getuserfeedback:action:activateView", openRequest: { source: "command", requestId: "request-inline", }, }); expect( coreViewHostActionSchema.parse({ t: "getuserfeedback:action:closeView", gen, viewId: "view-a", closeAttemptId: "attempt-a", }), ).toMatchObject({ t: "getuserfeedback:action:closeView", viewId: "view-a", }); }); test("keeps lifecycle action validation strict", () => { const gen = createGeneration(0); expect( actionPrerenderViewSchema.safeParse({ t: "getuserfeedback:action:prerenderView", gen, viewId: "view-a", srcdoc: "", instanceId: "instance-a", }).success, ).toBe(false); expect( actionActivateViewSchema.safeParse({ t: "getuserfeedback:action:activateView", gen, viewId: "view-a", instanceId: "instance-a", }).success, ).toBe(false); expect( actionPrerenderViewSchema.safeParse({ t: "getuserfeedback:action:prerenderView", gen, viewId: " ", flowRunId: "run-a", srcdoc: "", instanceId: "instance-a", }).success, ).toBe(false); expect( actionActivateViewSchema.safeParse({ t: "getuserfeedback:action:activateView", gen, viewId: "view-a", flowRunId: "run-a", instanceId: "instance-a", openRequest: { source: "command", flowId: "flow-a", }, }).success, ).toBe(false); expect( actionCloseViewSchema.safeParse({ t: "getuserfeedback:action:closeView", gen, viewId: "view-a", }).success, ).toBe(false); expect( actionCloseViewSchema.safeParse({ t: "getuserfeedback:action:closeView", gen, viewId: "view-a", closeAttemptId: "attempt-a", extra: true, }).success, ).toBe(false); }); test("rejects non-lifecycle protocol messages", () => { expect( coreViewHostActionSchema.safeParse({ t: "getuserfeedback:event:ready", gen: createGeneration(0), }).success, ).toBe(false); }); });