import { describe, expect, it } from "bun:test"; import { areViewHostSnapshotsEqual, cloneViewHostSnapshots, isFlowLayoutUpdatedDetail, isInstanceViewHostSnapshotsChangedDetail, isNavigationOutcome, isViewHostSnapshot, } from "./host-event-contract"; import { commandSettledDetailSchema, flowLayoutUpdatedDetailSchema, flowStateChangedDetailSchema, instanceFlowStateChangedDetailSchema, navigationRequestedDetailSchema, viewHostSnapshotSchema, } from "./host-event-schemas"; describe("commandSettledDetailSchema", () => { it("accepts instance feedback command settlements", () => { for (const kind of [ "instanceFeedback.capture", "instanceFeedback.listMine", ] as const) { expect( commandSettledDetailSchema.safeParse({ instanceId: "instance_123", kind, ok: true, requestId: "req_123", result: { status: "accepted" }, }).success, ).toBe(true); expect( commandSettledDetailSchema.safeParse({ error: { code: "INSTANCE_FEEDBACK_FAILED", message: "failed" }, instanceId: "instance_123", kind, ok: false, requestId: "req_123", }).success, ).toBe(true); } }); }); describe("navigationRequestedDetailSchema", () => { it("accepts versioned HTTP(S) navigation details during the target rollout", () => { expect( navigationRequestedDetailSchema.safeParse({ instanceId: "instance_123", target: "self", url: "https://example.com/settings", version: 2, }).success, ).toBe(true); expect( navigationRequestedDetailSchema.safeParse({ instanceId: "instance_123", target: "_blank", url: "https://example.com/settings", version: 1, }).success, ).toBe(true); expect( navigationRequestedDetailSchema.safeParse({ instanceId: "instance_123", url: "https://example.com/settings", version: 2, }).success, ).toBe(true); expect( navigationRequestedDetailSchema.safeParse({ instanceId: "instance_123", target: "blank", url: "mailto:team@example.com", version: 2, }).success, ).toBe(false); expect( navigationRequestedDetailSchema.safeParse({ instanceId: "instance_123", target: "_blank", url: "https://example.com/settings", version: 2, }).success, ).toBe(false); expect( navigationRequestedDetailSchema.safeParse({ instanceId: "instance_123", target: "self", url: "https://example.com/settings", version: 1, }).success, ).toBe(false); expect( navigationRequestedDetailSchema.safeParse({ instanceId: "instance_123", target: "_blank", url: "not a url", }).success, ).toBe(false); }); it("accepts only the negotiated synchronous settlement callback shape", () => { expect( navigationRequestedDetailSchema.safeParse({ instanceId: "instance_123", settle: () => {}, url: "https://example.com/settings", version: 2, }).success, ).toBe(true); expect( navigationRequestedDetailSchema.safeParse({ instanceId: "instance_123", settle: "succeeded", url: "https://example.com/settings", version: 2, }).success, ).toBe(false); }); }); describe("flowLayoutUpdatedDetailSchema", () => { it("accepts only timed one-shot layout occurrences", () => { const detail = { instanceId: "instance_123", flowHandleId: "handle_123", width: 320, height: 240, transitionPolicy: { kind: "timed" as const, durationMs: 260, easing: [0.4, 0, 0.2, 1] as const, }, }; expect(flowLayoutUpdatedDetailSchema.safeParse(detail).success).toBe(true); expect(isFlowLayoutUpdatedDetail(detail)).toBe(true); expect( flowLayoutUpdatedDetailSchema.safeParse({ ...detail, transitionPolicy: { kind: "instant", durationMs: 0 }, }).success, ).toBe(false); }); }); describe("isNavigationOutcome", () => { it("accepts only canonical synchronous navigation outcomes", () => { for (const outcome of ["succeeded", "failed", "unhandled"]) { expect(isNavigationOutcome(outcome)).toBe(true); } expect(isNavigationOutcome("handled")).toBe(false); expect(isNavigationOutcome(undefined)).toBe(false); }); }); describe("isInstanceViewHostSnapshotsChangedDetail", () => { it("compares snapshot identity, state, and transition policy", () => { const snapshot = { frameAppearance: { border: "1px solid #ddd", borderRadius: "12px", boxShadow: "none", kind: "page" as const, }, flowHandleId: "handle_123", flowRunId: "run_123", height: 420, isLoading: false, isOpen: true, kind: "flowRun" as const, transitionPolicy: { durationMs: 180, easing: [0.2, 0, 0, 1] as [number, number, number, number], kind: "timed" as const, }, }; expect( areViewHostSnapshotsEqual( snapshot, cloneViewHostSnapshots([snapshot])[0], ), ).toBe(true); expect( areViewHostSnapshotsEqual(snapshot, { ...snapshot, flowRunId: "run_other", }), ).toBe(false); expect( areViewHostSnapshotsEqual(snapshot, { ...snapshot, frameAppearance: { ...snapshot.frameAppearance, border: "none" }, }), ).toBe(false); }); it("clones mutable transition policy data", () => { const easing: [number, number, number, number] = [0.1, 0.2, 0.3, 0.4]; const frameAppearance = { border: "1px solid #ddd", borderRadius: "12px", boxShadow: "none", kind: "page" as const, resolvedBorder: { color: { colorSpace: "srgb" as const, red: 221, green: 221, blue: 221, alpha: 1, }, style: "solid" as const, widthPx: 1, }, resolvedBorderRadius: { topLeft: { horizontalPx: 12, verticalPx: 10 }, topRight: { horizontalPx: 12, verticalPx: 10 }, bottomRight: { horizontalPx: 8, verticalPx: 6 }, bottomLeft: { horizontalPx: 8, verticalPx: 6 }, }, }; const clone = cloneViewHostSnapshots([ { frameAppearance, flowRunId: "run_123", isLoading: false, isOpen: true, kind: "flowRun", transitionPolicy: { durationMs: 200, easing, kind: "timed" }, transitionSchedule: { sequence: 7, transactionId: "view-123:layout-7", startAtEpochMs: 1_700_000_000_096, }, }, ]); easing[0] = 1; frameAppearance.border = "none"; frameAppearance.resolvedBorder.color.red = 0; frameAppearance.resolvedBorderRadius.topLeft.horizontalPx = 0; expect(clone[0]?.transitionPolicy).toEqual({ durationMs: 200, easing: [0.1, 0.2, 0.3, 0.4], kind: "timed", }); expect(clone[0]?.transitionSchedule).toEqual({ sequence: 7, transactionId: "view-123:layout-7", startAtEpochMs: 1_700_000_000_096, }); expect(clone[0]?.frameAppearance).toEqual({ border: "1px solid #ddd", borderRadius: "12px", boxShadow: "none", kind: "page", resolvedBorder: { color: { colorSpace: "srgb", red: 221, green: 221, blue: 221, alpha: 1, }, style: "solid", widthPx: 1, }, resolvedBorderRadius: { topLeft: { horizontalPx: 12, verticalPx: 10 }, topRight: { horizontalPx: 12, verticalPx: 10 }, bottomRight: { horizontalPx: 8, verticalPx: 6 }, bottomLeft: { horizontalPx: 8, verticalPx: 6 }, }, }); }); it("accepts snapshot appearances without extending legacy flow state", () => { const page = { border: "1px solid #ddd", borderRadius: "12px", boxShadow: "none", kind: "page" as const, }; const none = { kind: "none" as const }; expect( viewHostSnapshotSchema.safeParse({ kind: "flowRun", flowRunId: "run_123", isOpen: true, isLoading: false, frameAppearance: page, }).success, ).toBe(true); expect( viewHostSnapshotSchema.safeParse({ kind: "pendingFlowHandle", flowHandleId: "handle_123", isOpen: false, isLoading: true, frameAppearance: none, }).success, ).toBe(true); expect( flowStateChangedDetailSchema.safeParse({ instanceId: "instance_123", flowHandleId: "handle_123", isOpen: true, isLoading: false, }).success, ).toBe(true); expect( flowStateChangedDetailSchema.safeParse({ instanceId: "instance_123", flowHandleId: "handle_123", isOpen: true, isLoading: false, frameAppearance: page, }).success, ).toBe(false); expect( instanceFlowStateChangedDetailSchema.safeParse({ instanceId: "instance_123", isOpen: false, isLoading: true, frameAppearance: none, }).success, ).toBe(false); expect( isViewHostSnapshot({ kind: "flowRun", flowRunId: "run_123", isOpen: true, isLoading: false, frameAppearance: page, }), ).toBe(true); }); it("rejects malformed snapshot frame appearances", () => { const missingBorder = { borderRadius: "12px", boxShadow: "none", kind: "page", }; expect( viewHostSnapshotSchema.safeParse({ kind: "flowRun", flowRunId: "run_123", isOpen: true, isLoading: false, frameAppearance: missingBorder, }).success, ).toBe(false); expect( viewHostSnapshotSchema.safeParse({ kind: "flowRun", flowRunId: "run_123", isOpen: true, isLoading: false, frameAppearance: { kind: "none", border: "unexpected" }, }).success, ).toBe(false); expect( isViewHostSnapshot({ kind: "flowRun", flowRunId: "run_123", isOpen: true, isLoading: false, frameAppearance: { kind: "page", border: "only" }, }), ).toBe(false); const inheritedAppearance = Object.create({ kind: "page", border: "none", borderRadius: "0", boxShadow: "none", }) as unknown; expect( viewHostSnapshotSchema.safeParse({ kind: "flowRun", flowRunId: "run_123", isOpen: true, isLoading: false, frameAppearance: inheritedAppearance, }).success, ).toBe(false); }); it("rejects malformed snapshot transition schedules", () => { const snapshot = { kind: "flowRun", flowRunId: "run_123", isOpen: true, isLoading: false, transitionSchedule: { sequence: -1, transactionId: "layout-1", startAtEpochMs: 1_000, }, }; expect(viewHostSnapshotSchema.safeParse(snapshot).success).toBe(false); expect(isViewHostSnapshot(snapshot)).toBe(false); }); it("rejects sparse snapshot arrays", () => { expect( isInstanceViewHostSnapshotsChangedDetail({ instanceId: "instance_123", snapshots: new Array(1), }), ).toBe(false); }); it("rejects invalid snapshot transition policies", () => { expect( isInstanceViewHostSnapshotsChangedDetail({ instanceId: "instance_123", snapshots: [ { flowHandleId: "handle_123", isLoading: true, isOpen: false, kind: "pendingFlowHandle", transitionPolicy: { durationMs: 10, kind: "instant" }, }, ], }), ).toBe(false); }); });