import assert from "node:assert/strict"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeAll, describe, it, vi } from "vitest"; vi.mock("@hivegrid/ui-mock", async () => import("../packages/ui-mock/src/index.ts")); let uiMockVisual: typeof import("./verify-ui-mock-visual.ts"); const fixtures: string[] = []; function createFixture() { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "ui-mock-visual-")); fixtures.push(rootDir); return rootDir; } afterEach(() => { while (fixtures.length > 0) { fs.rmSync(fixtures.pop()!, { recursive: true, force: true }); } }); beforeAll(async () => { uiMockVisual = await import("./verify-ui-mock-visual.ts"); }); describe("verify-ui-mock-visual", () => { it("generates a real hot-swap mock module, page, and snapshot", () => { const drill = uiMockVisual.buildUiMockVisualDrill(); assert.match(drill.module, /export const mockPage/); assert.match(drill.module, /"route": "\/mock\/phase-c\/hot-swap"/); assert.match(drill.html, /data-ui-mock-page="phase-c-hot-swap"/); assert.match(drill.snapshot, /"pageId": "phase-c-hot-swap"/); }); it("generates the AK-P20 interim fixture from all five view-model slots", () => { const drill = uiMockVisual.buildP20InterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"demoData":/); assert.match(drill.html, /data-ui-mock-page="ak-p20-interim"/); assert.match(drill.html, /\/:tenant\/onboarding\/checklist/); assert.match(drill.snapshot, /"pageId": "ak-p20-interim"/); }); it("generates the VA-P24a interim fixture from the self-service subscription VM", () => { const drill = uiMockVisual.buildVaP24aInterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"minimumSeatQuantity": 3/); assert.match(drill.module, /"requiresPaymentMethod": true/); assert.match(drill.html, /data-ui-mock-page="va-p24a-interim"/); assert.match(drill.snapshot, /"pageId": "va-p24a-interim"/); }); it("generates the VA-P26a interim fixture from the access-scoped portal dashboard VM", () => { const drill = uiMockVisual.buildVaP26aInterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"portalName": "Acme Kundenportal"/); assert.match(drill.module, /"screenRoutes":/); assert.match(drill.html, /data-ui-mock-page="va-p26a-customer-portal-interim"/); assert.match(drill.snapshot, /"pageId": "va-p26a-customer-portal-interim"/); }); it("generates the VA-P26b interim fixture from interaction, lifecycle and admin VMs", () => { const drill = uiMockVisual.buildVaP26bInterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"viewerKind": "customer"/); assert.match(drill.module, /"kind": "subcontractor"/); assert.match(drill.module, /"eventType": "lifecycle"/); assert.match(drill.html, /data-ui-mock-page="va-p26b-customer-portal-interim"/); assert.match(drill.snapshot, /"pageId": "va-p26b-customer-portal-interim"/); }); it("generates the VA-P26c domain-admin interim fixture from the entitlement and switch VM", () => { const drill = uiMockVisual.buildVaP26cInterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"pendingSwitch":/); assert.match(drill.module, /"canActivateSwitch": true/); assert.match(drill.html, /data-ui-mock-page="va-p26c-customer-portal-domain-interim"/); assert.match(drill.snapshot, /"pageId": "va-p26c-customer-portal-domain-interim"/); }); it("generates the AK-P15a telephony interim fixture from all four view-model surfaces", () => { const drill = uiMockVisual.buildTelephonyInterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"clickToCall":/); assert.match(drill.module, /"permission": "telephony\.manage"/); assert.match(drill.html, /data-ui-mock-page="ak-p15a-telephony-interim"/); assert.match(drill.snapshot, /"pageId": "ak-p15a-telephony-interim"/); }); it("generates AK-P15b chat index and detail fixtures from the shared chat view models", () => { const indexDrill = uiMockVisual.buildChatIndexInterimVisualDrill(); const detailDrill = uiMockVisual.buildChatDetailInterimVisualDrill(); assert.match(indexDrill.module, /"screenRoute": "\/chat"/); assert.match(indexDrill.module, /"permission": "chat\.read_own_channels"/); assert.match(indexDrill.html, /data-ui-mock-page="ak-p15b-chat-index-interim"/); assert.match(indexDrill.snapshot, /"pageId": "ak-p15b-chat-index-interim"/); assert.match(detailDrill.module, /"screenRoute": "\/chat\/:id"/); assert.match(detailDrill.module, /"source": "http_ack"/); assert.doesNotMatch(detailDrill.module, /"source": "local_echo"/); assert.match(detailDrill.html, /data-ui-mock-page="ak-p15b-chat-detail-interim"/); assert.match(detailDrill.snapshot, /"pageId": "ak-p15b-chat-detail-interim"/); }); it("generates the R-P11a Spotlight interim fixture from the shared search VM", () => { const drill = uiMockVisual.buildSpotlightInterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"desktop": "modal"/); assert.match(drill.module, /"mobile": "viewport"/); assert.match(drill.html, /data-ui-mock-page="r-p11a-spotlight-interim"/); assert.match(drill.snapshot, /"pageId": "r-p11a-spotlight-interim"/); }); it("passes when committed static baselines match the generated drill", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); const output = uiMockVisual.formatUiMockVisualResult(result); assert.deepEqual(result.failures, []); // 7 hot-swap + 7 AK-P20 + 6 R-P11b dashboard + 10 R-P11a Spotlight + 6 R-P12b flow editor // + 7 AK-UI-01 email inbox + 7 AK-UI-02 email detail + 7 AK-UI-03 email compose // + 7 AK-UI-05 plugin catalog + 8 AK-P15a telephony + 8 AK-P15b chat index // + 9 AK-P15b chat detail + 7 VA-P24a + 7 VA-P26a + 9 VA-P26b // + 9 VA-P26c domain-admin + 3 R-P6 CRM contact-list + 3 R-P7 lead-inbox // + 3 R-P8 pipeline-kanban checks. assert.equal(result.passes.length, 130); assert.match(output, /^PASS UI mock static artifact baseline verification/); assert.match(output, /renders through JSDOM in CI/); assert.match(output, /rendered hot-swap navigation has exactly one active route/); assert.match(output, /rendered R-P11b dashboard state payload carries the derived role layout/); assert.match(output, /rendered R-P11b dashboard state payload ships only the active layout/); assert.match(output, /r-p11b-interim\.snapshot\.json: matches generated R-P11b dashboard interim artifact/); assert.match(output, /r-p11a-spotlight-interim\.snapshot\.json: matches generated R-P11a Spotlight interim artifact/); }); it("generates the R-P12b flow editor fixture with the reverse recipient contract", () => { const drill = uiMockVisual.buildAutomationEditorVisualDrill(); assert.match(drill.module, /"designStatus": "subframe"/); assert.match(drill.module, /"recipients":/); assert.match(drill.html, /data-ui-mock-page="r-p12b-editor"/); assert.match(drill.snapshot, /"pageId": "r-p12b-editor"/); }); it("generates the AK-UI-01 email inbox interim fixture from the shared inbox VM", () => { const drill = uiMockVisual.buildEmailInboxInterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"screenRoute": "\/emails"/); assert.match(drill.module, /"emptyHeadlineKey": "emailInbox\.list\.empty\.headline"/); assert.match(drill.html, /data-ui-mock-page="ak-ui-01-email-inbox-interim"/); assert.match(drill.snapshot, /"pageId": "ak-ui-01-email-inbox-interim"/); }); it("fails when the AK-UI-01 email inbox interim baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/ak-ui-01-email-inbox.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /ak-ui-01-email-inbox\.html: interim baseline drifted/); }); it("generates the AK-UI-02 email detail interim fixture from the shared detail VM", () => { const drill = uiMockVisual.buildEmailDetailInterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"screenRoute": "\/emails\/:id"/); assert.match(drill.module, /"sourceKey": "emailInbox\.detail\.match\.manual"/); // The sanitized body must round-trip into the baseline untouched. assert.match(drill.module, /"allowExternalImages": false/); assert.match(drill.html, /data-ui-mock-page="ak-ui-02-email-detail-interim"/); assert.match(drill.snapshot, /"pageId": "ak-ui-02-email-detail-interim"/); }); it("fails when the AK-UI-02 email detail interim baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/ak-ui-02-email-detail.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /ak-ui-02-email-detail\.html: interim baseline drifted/); }); it("generates the AK-UI-03 email compose interim fixture from the shared compose VM", () => { const drill = uiMockVisual.buildEmailComposeInterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"screenRoute": "\/emails\/new"/); assert.match(drill.module, /"noMailboxKey": "emailInbox\.compose\.noMailbox"/); assert.match(drill.module, /blockquote/); assert.match(drill.html, /data-ui-mock-page="ak-ui-03-email-compose-interim"/); assert.match(drill.snapshot, /"pageId": "ak-ui-03-email-compose-interim"/); }); it("fails when the AK-UI-03 email compose interim baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/ak-ui-03-email-compose.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /ak-ui-03-email-compose\.html: interim baseline drifted/); }); it("generates the AK-UI-05 plugin catalog interim fixture from the shared plugin catalog VM", () => { const drill = uiMockVisual.buildPluginCatalogInterimVisualDrill(); assert.match(drill.module, /"designStatus": "interim"/); assert.match(drill.module, /"screenRoute": "\/settings\/integrations\/plugins"/); // The consent gate travels with the VM: a plugin with a missing grant folds // the enable into the approval instead of exposing a plain enable button. assert.match(drill.module, /"grantOp": "grantAndEnable"/); assert.match(drill.module, /"hook:workflow\.executed"/); assert.match(drill.html, /data-ui-mock-page="ak-ui-05-plugins-interim"/); assert.match(drill.snapshot, /"pageId": "ak-ui-05-plugins-interim"/); }); it("fails when the AK-UI-05 plugin catalog interim baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/ak-ui-05-plugins.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /ak-ui-05-plugins\.html: interim baseline drifted/); }); it("fails when the R-P12b flow editor baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/r-p12b-editor.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /r-p12b-editor\.html: interim baseline drifted/); }); it("fails when the R-P11a Spotlight interim baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/r-p11a-spotlight-interim.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /r-p11a-spotlight-interim\.html: interim baseline drifted/); }); it("fails when the R-P11b dashboard interim baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/r-p11b-interim.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /r-p11b-interim\.html: interim baseline drifted/); }); it("fails when the HTML visual baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/hot-swap.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /hot-swap\.html: static artifact baseline drifted/); }); it("fails when the AK-P20 interim visual baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/ak-p20-interim.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /ak-p20-interim\.html: static artifact baseline drifted/); }); it("fails when the VA-P24a interim visual baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/va-p24a-interim.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /va-p24a-interim\.html: static artifact baseline drifted/); }); it("fails when the VA-P26a customer-portal interim visual baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/va-p26a-customer-portal-interim.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /va-p26a-customer-portal-interim\.html: static artifact baseline drifted/); }); it("fails when the VA-P26b customer-portal interim visual baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/va-p26b-customer-portal-interim.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /va-p26b-customer-portal-interim\.html: static artifact baseline drifted/); }); it("fails when the VA-P26c domain-admin interim visual baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/va-p26c-customer-portal-domain-interim.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /va-p26c-customer-portal-domain-interim\.html: static artifact baseline drifted/); }); it("fails when the AK-P15a telephony interim visual baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/ak-p15a-telephony-interim.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /ak-p15a-telephony-interim\.html: static artifact baseline drifted/); }); it("fails when the AK-P15b chat index interim visual baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/ak-p15b-chat-index-interim.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /ak-p15b-chat-index-interim\.html: static artifact baseline drifted/); }); it("fails when the AK-P15b chat detail interim visual baseline drifts", () => { const rootDir = createFixture(); uiMockVisual.writeUiMockVisualBaseline(rootDir); fs.appendFileSync(path.join(rootDir, "packages/ui-mock/visual-baseline/ak-p15b-chat-detail-interim.html"), "\n\n", "utf8"); const result = uiMockVisual.verifyUiMockVisualBaseline(rootDir); assert.match(result.failures.join("\n"), /ak-p15b-chat-detail-interim\.html: static artifact baseline drifted/); }); it("blocks visual baseline update mode when CI is set", () => { assert.throws( () => uiMockVisual.assertVisualBaselineUpdateAllowed({ CI: "true" }), /Refusing to update UI mock static artifact baselines while CI is set\./ ); }); });