import { describe, expect, it } from "vite-plus/test"; import { isPickedElementPayload, isPreviewAnnotationPayload } from "./PickedElementPayload.ts"; function validPayload(overrides?: Record): Record { return { pageUrl: "https://example.com/", pageTitle: "Example", tagName: "button", selector: "button.submit", htmlPreview: "", componentName: "SubmitButton", source: { functionName: "SubmitButton", fileName: "/repo/src/Button.tsx", lineNumber: 12, columnNumber: 5, }, stack: [ { functionName: "SubmitButton", fileName: "/repo/src/Button.tsx", lineNumber: 12, columnNumber: 5, }, ], styles: ".submit { color: white; }", pickedAt: "2026-05-03T18:00:00.000Z", ...overrides, }; } describe("isPickedElementPayload", () => { it("accepts a complete, well-typed payload", () => { expect(isPickedElementPayload(validPayload())).toBe(true); }); it("accepts nullable string fields when null", () => { expect( isPickedElementPayload( validPayload({ pageTitle: null, selector: null, componentName: null, source: null }), ), ).toBe(true); }); it("accepts an empty stack array", () => { expect(isPickedElementPayload(validPayload({ stack: [] }))).toBe(true); }); it("accepts stack frames with null fields", () => { expect( isPickedElementPayload( validPayload({ stack: [ { functionName: null, fileName: null, lineNumber: null, columnNumber: null, }, ], }), ), ).toBe(true); }); it("rejects null and primitive inputs", () => { expect(isPickedElementPayload(null)).toBe(false); expect(isPickedElementPayload(undefined)).toBe(false); expect(isPickedElementPayload("string")).toBe(false); expect(isPickedElementPayload(42)).toBe(false); expect(isPickedElementPayload([])).toBe(false); }); it.each<[string, Record]>([ ["missing pageUrl", validPayload({ pageUrl: undefined })], ["wrong-type pageUrl", validPayload({ pageUrl: 123 })], ["missing tagName", validPayload({ tagName: undefined })], ["missing htmlPreview", validPayload({ htmlPreview: undefined })], ["missing styles", validPayload({ styles: undefined })], ["missing pickedAt", validPayload({ pickedAt: undefined })], ["wrong-type pageTitle", validPayload({ pageTitle: 99 })], ["wrong-type selector", validPayload({ selector: 99 })], ["wrong-type componentName", validPayload({ componentName: 99 })], ])("rejects payloads with %s", (_label, value) => { expect(isPickedElementPayload(value)).toBe(false); }); it("rejects malformed source frames", () => { expect( isPickedElementPayload( validPayload({ source: { functionName: 0, fileName: null, lineNumber: null, columnNumber: null, }, }), ), ).toBe(false); }); it("rejects non-finite numeric line/column numbers", () => { expect( isPickedElementPayload( validPayload({ source: { functionName: null, fileName: null, lineNumber: Number.POSITIVE_INFINITY, columnNumber: null, }, }), ), ).toBe(false); expect( isPickedElementPayload( validPayload({ source: { functionName: null, fileName: null, lineNumber: Number.NaN, columnNumber: null, }, }), ), ).toBe(false); }); it("rejects malformed stack arrays", () => { expect(isPickedElementPayload(validPayload({ stack: "not-an-array" }))).toBe(false); expect(isPickedElementPayload(validPayload({ stack: [{ bogus: true }] }))).toBe(false); }); }); function validAnnotation(overrides?: Record): Record { return { id: "annotation_1", pageUrl: "https://example.com/", pageTitle: "Example", comment: "Make this clearer", elements: [ { id: "element_1", element: validPayload(), rect: { x: 10, y: 20, width: 100, height: 40 }, }, ], regions: [{ id: "region_1", rect: { x: 5, y: 6, width: 20, height: 30 } }], strokes: [ { id: "stroke_1", color: "#7c3aed", width: 4, points: [ { x: 10, y: 10 }, { x: 20, y: 20 }, ], bounds: { x: 6, y: 6, width: 18, height: 18 }, }, ], styleChanges: [ { targetId: "element_1", selector: "button.submit", property: "opacity", previousValue: "1", value: "0.5", }, ], screenshot: null, createdAt: "2026-06-11T00:00:00.000Z", ...overrides, }; } describe("isPreviewAnnotationPayload", () => { it("accepts a structured annotation draft before screenshot capture", () => { expect(isPreviewAnnotationPayload(validAnnotation())).toBe(true); }); it("rejects screenshots supplied by the guest preload", () => { expect(isPreviewAnnotationPayload(validAnnotation({ screenshot: { dataUrl: "bad" } }))).toBe( false, ); }); it("rejects malformed geometry and nested element payloads", () => { expect( isPreviewAnnotationPayload( validAnnotation({ regions: [{ id: "region_1", rect: { x: 0, y: 0, width: "wide" } }] }), ), ).toBe(false); expect( isPreviewAnnotationPayload( validAnnotation({ elements: [{ id: "element_1", element: {}, rect: {} }] }), ), ).toBe(false); }); });