#!/usr/bin/env tsx import assert from "node:assert/strict"; import fs from "node:fs"; import { createRequire } from "node:module"; import path from "node:path"; import { fileURLToPath } from "node:url"; // Use a relative path so vitest's Vite-based resolver can find the workspace package // even when it has not been hoisted into the top-level node_modules/@hivegrid directory. import { createAutomationEditorFixturePage, createChatDetailInterimFixturePage, createChatIndexInterimFixturePage, createCrmInterimFixturePage, createDashboardInterimFixturePage, createEmailComposeInterimFixturePage, createEmailDetailInterimFixturePage, createEmailInboxInterimFixturePage, createHotSwapDrillPage, createLeadInterimFixturePage, createP20InterimFixturePage, createPluginCatalogInterimFixturePage, createProjektBoardInterimFixturePage, createSpotlightInterimFixturePage, createTelephonyInterimFixturePage, createVaP24aInterimFixturePage, createVaP26aInterimFixturePage, createVaP26bInterimFixturePage, createVaP26cInterimFixturePage, generateMockArtifacts, type P20InterimViewModels, } from "../packages/ui-mock/src/index.js"; const require = createRequire(import.meta.url); const { JSDOM } = require("jsdom") as { JSDOM: new (html: string) => { window: { document: Document } }; }; const BASELINE_DIR = "packages/ui-mock/visual-baseline"; const BASELINE_FILES = { module: "hot-swap.mock.ts", html: "hot-swap.html", snapshot: "hot-swap.snapshot.json", } as const; // R-P11b dashboard interim screen (AD-28) — interim baseline, re-baselined on the D-DASH design-nachzug. const R_P11B_BASELINE_FILES = { module: "r-p11b-interim.mock.ts", html: "r-p11b-interim.html", snapshot: "r-p11b-interim.snapshot.json", } as const; // R-P11a Spotlight interim screen (AD-28) - re-baseline on the final D-DASH design follow-up. const R_P11A_BASELINE_FILES = { module: "r-p11a-spotlight-interim.mock.ts", html: "r-p11a-spotlight-interim.html", snapshot: "r-p11a-spotlight-interim.snapshot.json", } as const; // R-P6 typed CRM contact list (AD-28) — interim baseline, re-baselined on the D-CRM design-nachzug. const CRM_INTERIM_BASELINE_FILES = { module: "r-p6-crm-interim.mock.ts", html: "r-p6-crm-interim.html", snapshot: "r-p6-crm-interim.snapshot.json", } as const; // R-P7 lead-inbox interim screen (AD-28) — interim baseline, re-baselined on the D-P7 design-nachzug. const LEADS_INTERIM_BASELINE_FILES = { module: "r-p7-leads-interim.mock.ts", html: "r-p7-leads-interim.html", snapshot: "r-p7-leads-interim.snapshot.json", } as const; // R-P8 pipeline-kanban interim screen (AD-28) — interim baseline, re-baselined on the D-P8/D-KANBAN design-nachzug. const PROJEKT_INTERIM_BASELINE_FILES = { module: "r-p8-projects-interim.mock.ts", html: "r-p8-projects-interim.html", snapshot: "r-p8-projects-interim.snapshot.json", } as const; const P20_INTERIM_BASELINE_FILES = { module: "ak-p20-interim.mock.ts", html: "ak-p20-interim.html", snapshot: "ak-p20-interim.snapshot.json", } as const; // VA-P24a self-service subscription builder (AD-28) — re-baseline on the VD-P24 design-nachzug. const VA_P24A_INTERIM_BASELINE_FILES = { module: "va-p24a-interim.mock.ts", html: "va-p24a-interim.html", snapshot: "va-p24a-interim.snapshot.json", } as const; // VA-P26a customer portal (AD-28) — re-baseline on the VD-P26 design-nachzug. const VA_P26A_INTERIM_BASELINE_FILES = { module: "va-p26a-customer-portal-interim.mock.ts", html: "va-p26a-customer-portal-interim.html", snapshot: "va-p26a-customer-portal-interim.snapshot.json", } as const; // VA-P26b customer-portal interaction, admin and lifecycle screens (AD-28). const VA_P26B_INTERIM_BASELINE_FILES = { module: "va-p26b-customer-portal-interim.mock.ts", html: "va-p26b-customer-portal-interim.html", snapshot: "va-p26b-customer-portal-interim.snapshot.json", } as const; // VA-P26c customer-portal custom-domain admin screen (AD-28). const VA_P26C_INTERIM_BASELINE_FILES = { module: "va-p26c-customer-portal-domain-interim.mock.ts", html: "va-p26c-customer-portal-domain-interim.html", snapshot: "va-p26c-customer-portal-domain-interim.snapshot.json", } as const; // AK-P15a Aircall telephony interim screens (AD-28) — re-baseline on the D-P15 design-nachzug. const AK_P15A_TELEPHONY_BASELINE_FILES = { module: "ak-p15a-telephony-interim.mock.ts", html: "ak-p15a-telephony-interim.html", snapshot: "ak-p15a-telephony-interim.snapshot.json", } as const; // AK-P15b team-chat interim index (AD-28) — re-baseline on the D-CHAT design-nachzug. const AK_P15B_CHAT_INDEX_BASELINE_FILES = { module: "ak-p15b-chat-index-interim.mock.ts", html: "ak-p15b-chat-index-interim.html", snapshot: "ak-p15b-chat-index-interim.snapshot.json", } as const; // AK-P15b team-chat interim detail (AD-28) — re-baseline on the D-CHAT design-nachzug. const AK_P15B_CHAT_DETAIL_BASELINE_FILES = { module: "ak-p15b-chat-detail-interim.mock.ts", html: "ak-p15b-chat-detail-interim.html", snapshot: "ak-p15b-chat-detail-interim.snapshot.json", } as const; // R-P12b flow editor (Subframe delta) — contract snapshot of picker/context-menu/node-config/reverse-recipient surfaces. const R_P12B_BASELINE_FILES = { module: "r-p12b-editor.mock.ts", html: "r-p12b-editor.html", snapshot: "r-p12b-editor.snapshot.json", } as const; // AK-UI-01 email inbox interim screen (AD-28) — interim baseline, re-baselined on the design-nachzug. const AK_UI_01_BASELINE_FILES = { module: "ak-ui-01-email-inbox.mock.ts", html: "ak-ui-01-email-inbox.html", snapshot: "ak-ui-01-email-inbox.snapshot.json", } as const; // AK-UI-02 email message detail interim screen (AD-28) — interim baseline, re-baselined on the design-nachzug. const AK_UI_02_BASELINE_FILES = { module: "ak-ui-02-email-detail.mock.ts", html: "ak-ui-02-email-detail.html", snapshot: "ak-ui-02-email-detail.snapshot.json", } as const; // AK-UI-03 email compose interim screen (AD-28) — interim baseline, re-baselined on the design-nachzug. const AK_UI_03_BASELINE_FILES = { module: "ak-ui-03-email-compose.mock.ts", html: "ak-ui-03-email-compose.html", snapshot: "ak-ui-03-email-compose.snapshot.json", } as const; // AK-UI-05 tenant plugin catalog interim screen (AD-28) — interim baseline, re-baselined on the D-P5 design-nachzug. const AK_UI_05_BASELINE_FILES = { module: "ak-ui-05-plugins.mock.ts", html: "ak-ui-05-plugins.html", snapshot: "ak-ui-05-plugins.snapshot.json", } as const; const P20_INTERIM_VIEW_MODELS = { subscription: { status: "empty" }, usage: { status: "empty" }, billing: { status: "empty" }, demoData: { status: "empty" }, checklist: { status: "empty" }, } as const satisfies P20InterimViewModels; export type UiMockVisualResult = { readonly passes: readonly string[]; readonly failures: readonly string[]; readonly observations: readonly string[]; }; export function buildUiMockVisualDrill() { const page = createHotSwapDrillPage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildDashboardInterimVisualDrill() { const page = createDashboardInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildSpotlightInterimVisualDrill() { const page = createSpotlightInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildAutomationEditorVisualDrill() { const page = createAutomationEditorFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildEmailInboxInterimVisualDrill() { const page = createEmailInboxInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildEmailDetailInterimVisualDrill() { const page = createEmailDetailInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildEmailComposeInterimVisualDrill() { const page = createEmailComposeInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildPluginCatalogInterimVisualDrill() { const page = createPluginCatalogInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildP20InterimVisualDrill() { const page = createP20InterimFixturePage(P20_INTERIM_VIEW_MODELS); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildVaP24aInterimVisualDrill() { const page = createVaP24aInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildCrmInterimVisualDrill() { const page = createCrmInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildLeadInterimVisualDrill() { const page = createLeadInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildProjektBoardInterimVisualDrill() { const page = createProjektBoardInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildVaP26aInterimVisualDrill() { const page = createVaP26aInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildVaP26bInterimVisualDrill() { const page = createVaP26bInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildVaP26cInterimVisualDrill() { const page = createVaP26cInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildTelephonyInterimVisualDrill() { const page = createTelephonyInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildChatIndexInterimVisualDrill() { const page = createChatIndexInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function buildChatDetailInterimVisualDrill() { const page = createChatDetailInterimFixturePage(); const artifacts = generateMockArtifacts(page); return { module: artifacts.module, html: artifacts.html, snapshot: `${JSON.stringify(artifacts.snapshot, null, 2)}\n`, }; } export function writeUiMockVisualBaseline(rootDir = process.cwd()) { const drill = buildUiMockVisualDrill(); const crmInterimDrill = buildCrmInterimVisualDrill(); const leadInterimDrill = buildLeadInterimVisualDrill(); const projektBoardInterimDrill = buildProjektBoardInterimVisualDrill(); const p20InterimDrill = buildP20InterimVisualDrill(); const vaP24aInterimDrill = buildVaP24aInterimVisualDrill(); const vaP26aInterimDrill = buildVaP26aInterimVisualDrill(); const vaP26bInterimDrill = buildVaP26bInterimVisualDrill(); const vaP26cInterimDrill = buildVaP26cInterimVisualDrill(); const telephonyInterimDrill = buildTelephonyInterimVisualDrill(); const chatIndexInterimDrill = buildChatIndexInterimVisualDrill(); const chatDetailInterimDrill = buildChatDetailInterimVisualDrill(); const baselineDir = path.join(rootDir, BASELINE_DIR); fs.mkdirSync(baselineDir, { recursive: true }); for (const [files, artifacts] of [ [BASELINE_FILES, drill], [CRM_INTERIM_BASELINE_FILES, crmInterimDrill], [LEADS_INTERIM_BASELINE_FILES, leadInterimDrill], [PROJEKT_INTERIM_BASELINE_FILES, projektBoardInterimDrill], [P20_INTERIM_BASELINE_FILES, p20InterimDrill], [VA_P24A_INTERIM_BASELINE_FILES, vaP24aInterimDrill], [VA_P26A_INTERIM_BASELINE_FILES, vaP26aInterimDrill], [VA_P26B_INTERIM_BASELINE_FILES, vaP26bInterimDrill], [VA_P26C_INTERIM_BASELINE_FILES, vaP26cInterimDrill], [AK_P15A_TELEPHONY_BASELINE_FILES, telephonyInterimDrill], [AK_P15B_CHAT_INDEX_BASELINE_FILES, chatIndexInterimDrill], [AK_P15B_CHAT_DETAIL_BASELINE_FILES, chatDetailInterimDrill], ] as const) { for (const [artifactName, fileName] of Object.entries(files)) { fs.writeFileSync(path.join(baselineDir, fileName), artifacts[artifactName as keyof typeof artifacts], "utf8"); } } const dashboardDrill = buildDashboardInterimVisualDrill(); for (const [artifactName, fileName] of Object.entries(R_P11B_BASELINE_FILES)) { fs.writeFileSync(path.join(baselineDir, fileName), dashboardDrill[artifactName as keyof typeof dashboardDrill], "utf8"); } const spotlightDrill = buildSpotlightInterimVisualDrill(); for (const [artifactName, fileName] of Object.entries(R_P11A_BASELINE_FILES)) { fs.writeFileSync(path.join(baselineDir, fileName), spotlightDrill[artifactName as keyof typeof spotlightDrill], "utf8"); } const automationEditorDrill = buildAutomationEditorVisualDrill(); for (const [artifactName, fileName] of Object.entries(R_P12B_BASELINE_FILES)) { fs.writeFileSync(path.join(baselineDir, fileName), automationEditorDrill[artifactName as keyof typeof automationEditorDrill], "utf8"); } const emailInboxDrill = buildEmailInboxInterimVisualDrill(); for (const [artifactName, fileName] of Object.entries(AK_UI_01_BASELINE_FILES)) { fs.writeFileSync(path.join(baselineDir, fileName), emailInboxDrill[artifactName as keyof typeof emailInboxDrill], "utf8"); } const emailDetailDrill = buildEmailDetailInterimVisualDrill(); for (const [artifactName, fileName] of Object.entries(AK_UI_02_BASELINE_FILES)) { fs.writeFileSync(path.join(baselineDir, fileName), emailDetailDrill[artifactName as keyof typeof emailDetailDrill], "utf8"); } const emailComposeDrill = buildEmailComposeInterimVisualDrill(); for (const [artifactName, fileName] of Object.entries(AK_UI_03_BASELINE_FILES)) { fs.writeFileSync(path.join(baselineDir, fileName), emailComposeDrill[artifactName as keyof typeof emailComposeDrill], "utf8"); } const pluginCatalogDrill = buildPluginCatalogInterimVisualDrill(); for (const [artifactName, fileName] of Object.entries(AK_UI_05_BASELINE_FILES)) { fs.writeFileSync(path.join(baselineDir, fileName), pluginCatalogDrill[artifactName as keyof typeof pluginCatalogDrill], "utf8"); } } export function verifyUiMockVisualBaseline(rootDir = process.cwd()): UiMockVisualResult { const drill = buildUiMockVisualDrill(); const crmInterimDrill = buildCrmInterimVisualDrill(); const leadInterimDrill = buildLeadInterimVisualDrill(); const projektBoardInterimDrill = buildProjektBoardInterimVisualDrill(); const p20InterimDrill = buildP20InterimVisualDrill(); const vaP24aInterimDrill = buildVaP24aInterimVisualDrill(); const vaP26aInterimDrill = buildVaP26aInterimVisualDrill(); const vaP26bInterimDrill = buildVaP26bInterimVisualDrill(); const vaP26cInterimDrill = buildVaP26cInterimVisualDrill(); const telephonyInterimDrill = buildTelephonyInterimVisualDrill(); const chatIndexInterimDrill = buildChatIndexInterimVisualDrill(); const chatDetailInterimDrill = buildChatDetailInterimVisualDrill(); const passes: string[] = []; const failures: string[] = []; const observations: string[] = []; assert.equal(drill.module.includes("export const mockPage"), true); assert.equal(drill.html.includes('data-ui-mock-route="/mock/phase-c/hot-swap"'), true); const domSmoke = verifyRenderedHotSwapDrill(drill.html); passes.push(...domSmoke.passes); failures.push(...domSmoke.failures); assert.equal(crmInterimDrill.module.includes('"designStatus": "interim"'), true); assert.equal(crmInterimDrill.html.includes('data-ui-mock-page="r-p6-crm-interim"'), true); assert.equal(leadInterimDrill.module.includes('"designStatus": "interim"'), true); assert.equal(leadInterimDrill.html.includes('data-ui-mock-page="r-p7-leads-interim"'), true); assert.equal(projektBoardInterimDrill.module.includes('"designStatus": "interim"'), true); assert.equal(projektBoardInterimDrill.html.includes('data-ui-mock-page="r-p8-projects-interim"'), true); assert.equal(p20InterimDrill.module.includes('"designStatus": "interim"'), true); assert.equal(p20InterimDrill.html.includes('data-ui-mock-page="ak-p20-interim"'), true); const p20DomSmoke = verifyRenderedP20InterimDrill(p20InterimDrill.html); passes.push(...p20DomSmoke.passes); failures.push(...p20DomSmoke.failures); assert.equal(vaP24aInterimDrill.module.includes('"designStatus": "interim"'), true); assert.equal(vaP24aInterimDrill.html.includes('data-ui-mock-page="va-p24a-interim"'), true); const vaP24aDomSmoke = verifyRenderedVaP24aInterimDrill(vaP24aInterimDrill.html); passes.push(...vaP24aDomSmoke.passes); failures.push(...vaP24aDomSmoke.failures); assert.equal(vaP26aInterimDrill.module.includes('"designStatus": "interim"'), true); assert.equal(vaP26aInterimDrill.html.includes('data-ui-mock-page="va-p26a-customer-portal-interim"'), true); const vaP26aDomSmoke = verifyRenderedVaP26aInterimDrill(vaP26aInterimDrill.html); passes.push(...vaP26aDomSmoke.passes); failures.push(...vaP26aDomSmoke.failures); assert.equal(vaP26bInterimDrill.module.includes('"designStatus": "interim"'), true); assert.equal(vaP26bInterimDrill.html.includes('data-ui-mock-page="va-p26b-customer-portal-interim"'), true); const vaP26bDomSmoke = verifyRenderedVaP26bInterimDrill(vaP26bInterimDrill.html); passes.push(...vaP26bDomSmoke.passes); failures.push(...vaP26bDomSmoke.failures); assert.equal(vaP26cInterimDrill.module.includes('"designStatus": "interim"'), true); assert.equal(vaP26cInterimDrill.html.includes('data-ui-mock-page="va-p26c-customer-portal-domain-interim"'), true); const vaP26cDomSmoke = verifyRenderedVaP26cInterimDrill(vaP26cInterimDrill.html); passes.push(...vaP26cDomSmoke.passes); failures.push(...vaP26cDomSmoke.failures); assert.equal(telephonyInterimDrill.module.includes('"designStatus": "interim"'), true); assert.equal(telephonyInterimDrill.html.includes('data-ui-mock-page="ak-p15a-telephony-interim"'), true); const telephonyDomSmoke = verifyRenderedTelephonyInterimDrill(telephonyInterimDrill.html); passes.push(...telephonyDomSmoke.passes); failures.push(...telephonyDomSmoke.failures); assert.equal(chatIndexInterimDrill.module.includes('"designStatus": "interim"'), true); assert.equal(chatIndexInterimDrill.html.includes('data-ui-mock-page="ak-p15b-chat-index-interim"'), true); const chatIndexDomSmoke = verifyRenderedChatIndexInterimDrill(chatIndexInterimDrill.html); passes.push(...chatIndexDomSmoke.passes); failures.push(...chatIndexDomSmoke.failures); assert.equal(chatDetailInterimDrill.module.includes('"source": "http_ack"'), true); assert.equal(chatDetailInterimDrill.module.includes('"source": "local_echo"'), false); assert.equal(chatDetailInterimDrill.html.includes('data-ui-mock-page="ak-p15b-chat-detail-interim"'), true); const chatDetailDomSmoke = verifyRenderedChatDetailInterimDrill(chatDetailInterimDrill.html); passes.push(...chatDetailDomSmoke.passes); failures.push(...chatDetailDomSmoke.failures); for (const [files, artifacts] of [ [BASELINE_FILES, drill], [CRM_INTERIM_BASELINE_FILES, crmInterimDrill], [LEADS_INTERIM_BASELINE_FILES, leadInterimDrill], [PROJEKT_INTERIM_BASELINE_FILES, projektBoardInterimDrill], [P20_INTERIM_BASELINE_FILES, p20InterimDrill], [VA_P24A_INTERIM_BASELINE_FILES, vaP24aInterimDrill], [VA_P26A_INTERIM_BASELINE_FILES, vaP26aInterimDrill], [VA_P26B_INTERIM_BASELINE_FILES, vaP26bInterimDrill], [VA_P26C_INTERIM_BASELINE_FILES, vaP26cInterimDrill], [AK_P15A_TELEPHONY_BASELINE_FILES, telephonyInterimDrill], [AK_P15B_CHAT_INDEX_BASELINE_FILES, chatIndexInterimDrill], [AK_P15B_CHAT_DETAIL_BASELINE_FILES, chatDetailInterimDrill], ] as const) { for (const [artifactName, fileName] of Object.entries(files)) { const relativePath = `${BASELINE_DIR}/${fileName}`; const baselinePath = path.join(rootDir, relativePath); if (!fs.existsSync(baselinePath)) { failures.push(`${relativePath}: missing UI mock static artifact baseline`); continue; } const expected = normalizeNewlines(fs.readFileSync(baselinePath, "utf8")); const actual = normalizeNewlines(artifacts[artifactName as keyof typeof artifacts]); if (actual === expected) { passes.push(`${relativePath}: matches generated hot-swap drill artifact`); } else { failures.push(`${relativePath}: static artifact baseline drifted; run pnpm run verify:ui-visual -- --update after reviewing the change`); } } } const dashboardDrill = buildDashboardInterimVisualDrill(); assert.equal(dashboardDrill.module.includes("export const mockPage"), true); assert.equal(dashboardDrill.html.includes('data-ui-mock-route="/mock/r-p11b/dashboard"'), true); const dashboardDomSmoke = verifyRenderedDashboardInterimDrill(dashboardDrill.html); passes.push(...dashboardDomSmoke.passes); failures.push(...dashboardDomSmoke.failures); for (const [artifactName, fileName] of Object.entries(R_P11B_BASELINE_FILES)) { const relativePath = `${BASELINE_DIR}/${fileName}`; const baselinePath = path.join(rootDir, relativePath); if (!fs.existsSync(baselinePath)) { failures.push(`${relativePath}: missing R-P11b dashboard interim baseline`); continue; } const expected = normalizeNewlines(fs.readFileSync(baselinePath, "utf8")); const actual = normalizeNewlines(dashboardDrill[artifactName as keyof typeof dashboardDrill]); if (actual === expected) { passes.push(`${relativePath}: matches generated R-P11b dashboard interim artifact`); } else { failures.push(`${relativePath}: interim baseline drifted; run pnpm run verify:ui-visual -- --update after reviewing the change`); } } const spotlightDrill = buildSpotlightInterimVisualDrill(); assert.equal(spotlightDrill.module.includes("export const mockPage"), true); assert.equal(spotlightDrill.html.includes('data-ui-mock-route="/mock/r-p11a/spotlight"'), true); const spotlightDomSmoke = verifyRenderedSpotlightInterimDrill(spotlightDrill.html); passes.push(...spotlightDomSmoke.passes); failures.push(...spotlightDomSmoke.failures); for (const [artifactName, fileName] of Object.entries(R_P11A_BASELINE_FILES)) { const relativePath = `${BASELINE_DIR}/${fileName}`; const baselinePath = path.join(rootDir, relativePath); if (!fs.existsSync(baselinePath)) { failures.push(`${relativePath}: missing R-P11a Spotlight interim baseline`); continue; } const expected = normalizeNewlines(fs.readFileSync(baselinePath, "utf8")); const actual = normalizeNewlines(spotlightDrill[artifactName as keyof typeof spotlightDrill]); if (actual === expected) { passes.push(`${relativePath}: matches generated R-P11a Spotlight interim artifact`); } else { failures.push(`${relativePath}: interim baseline drifted; run pnpm run verify:ui-visual -- --update after reviewing the change`); } } const automationEditorDrill = buildAutomationEditorVisualDrill(); assert.equal(automationEditorDrill.module.includes("export const mockPage"), true); assert.equal(automationEditorDrill.html.includes('data-ui-mock-route="/mock/r-p12b/editor"'), true); const automationEditorDomSmoke = verifyRenderedAutomationEditorDrill(automationEditorDrill.html); passes.push(...automationEditorDomSmoke.passes); failures.push(...automationEditorDomSmoke.failures); for (const [artifactName, fileName] of Object.entries(R_P12B_BASELINE_FILES)) { const relativePath = `${BASELINE_DIR}/${fileName}`; const baselinePath = path.join(rootDir, relativePath); if (!fs.existsSync(baselinePath)) { failures.push(`${relativePath}: missing R-P12b flow editor baseline`); continue; } const expected = normalizeNewlines(fs.readFileSync(baselinePath, "utf8")); const actual = normalizeNewlines(automationEditorDrill[artifactName as keyof typeof automationEditorDrill]); if (actual === expected) { passes.push(`${relativePath}: matches generated R-P12b flow editor artifact`); } else { failures.push(`${relativePath}: interim baseline drifted; run pnpm run verify:ui-visual -- --update after reviewing the change`); } } const emailInboxDrill = buildEmailInboxInterimVisualDrill(); assert.equal(emailInboxDrill.module.includes("export const mockPage"), true); assert.equal(emailInboxDrill.html.includes('data-ui-mock-route="/mock/ak-ui-01/emails"'), true); const emailInboxDomSmoke = verifyRenderedEmailInboxInterimDrill(emailInboxDrill.html); passes.push(...emailInboxDomSmoke.passes); failures.push(...emailInboxDomSmoke.failures); for (const [artifactName, fileName] of Object.entries(AK_UI_01_BASELINE_FILES)) { const relativePath = `${BASELINE_DIR}/${fileName}`; const baselinePath = path.join(rootDir, relativePath); if (!fs.existsSync(baselinePath)) { failures.push(`${relativePath}: missing AK-UI-01 email inbox interim baseline`); continue; } const expected = normalizeNewlines(fs.readFileSync(baselinePath, "utf8")); const actual = normalizeNewlines(emailInboxDrill[artifactName as keyof typeof emailInboxDrill]); if (actual === expected) { passes.push(`${relativePath}: matches generated AK-UI-01 email inbox interim artifact`); } else { failures.push(`${relativePath}: interim baseline drifted; run pnpm run verify:ui-visual -- --update after reviewing the change`); } } const emailDetailDrill = buildEmailDetailInterimVisualDrill(); assert.equal(emailDetailDrill.module.includes("export const mockPage"), true); assert.equal(emailDetailDrill.html.includes('data-ui-mock-route="/mock/ak-ui-02/emails/msg-1"'), true); const emailDetailDomSmoke = verifyRenderedEmailDetailInterimDrill(emailDetailDrill.html); passes.push(...emailDetailDomSmoke.passes); failures.push(...emailDetailDomSmoke.failures); for (const [artifactName, fileName] of Object.entries(AK_UI_02_BASELINE_FILES)) { const relativePath = `${BASELINE_DIR}/${fileName}`; const baselinePath = path.join(rootDir, relativePath); if (!fs.existsSync(baselinePath)) { failures.push(`${relativePath}: missing AK-UI-02 email detail interim baseline`); continue; } const expected = normalizeNewlines(fs.readFileSync(baselinePath, "utf8")); const actual = normalizeNewlines(emailDetailDrill[artifactName as keyof typeof emailDetailDrill]); if (actual === expected) { passes.push(`${relativePath}: matches generated AK-UI-02 email detail interim artifact`); } else { failures.push(`${relativePath}: interim baseline drifted; run pnpm run verify:ui-visual -- --update after reviewing the change`); } } const emailComposeDrill = buildEmailComposeInterimVisualDrill(); assert.equal(emailComposeDrill.module.includes("export const mockPage"), true); assert.equal(emailComposeDrill.html.includes('data-ui-mock-route="/mock/ak-ui-03/emails/new"'), true); const emailComposeDomSmoke = verifyRenderedEmailComposeInterimDrill(emailComposeDrill.html); passes.push(...emailComposeDomSmoke.passes); failures.push(...emailComposeDomSmoke.failures); for (const [artifactName, fileName] of Object.entries(AK_UI_03_BASELINE_FILES)) { const relativePath = `${BASELINE_DIR}/${fileName}`; const baselinePath = path.join(rootDir, relativePath); if (!fs.existsSync(baselinePath)) { failures.push(`${relativePath}: missing AK-UI-03 email compose interim baseline`); continue; } const expected = normalizeNewlines(fs.readFileSync(baselinePath, "utf8")); const actual = normalizeNewlines(emailComposeDrill[artifactName as keyof typeof emailComposeDrill]); if (actual === expected) { passes.push(`${relativePath}: matches generated AK-UI-03 email compose interim artifact`); } else { failures.push(`${relativePath}: interim baseline drifted; run pnpm run verify:ui-visual -- --update after reviewing the change`); } } const pluginCatalogDrill = buildPluginCatalogInterimVisualDrill(); assert.equal(pluginCatalogDrill.module.includes("export const mockPage"), true); assert.equal(pluginCatalogDrill.html.includes('data-ui-mock-route="/mock/ak-ui-05/plugins"'), true); const pluginCatalogDomSmoke = verifyRenderedPluginCatalogInterimDrill(pluginCatalogDrill.html); passes.push(...pluginCatalogDomSmoke.passes); failures.push(...pluginCatalogDomSmoke.failures); for (const [artifactName, fileName] of Object.entries(AK_UI_05_BASELINE_FILES)) { const relativePath = `${BASELINE_DIR}/${fileName}`; const baselinePath = path.join(rootDir, relativePath); if (!fs.existsSync(baselinePath)) { failures.push(`${relativePath}: missing AK-UI-05 plugin catalog interim baseline`); continue; } const expected = normalizeNewlines(fs.readFileSync(baselinePath, "utf8")); const actual = normalizeNewlines(pluginCatalogDrill[artifactName as keyof typeof pluginCatalogDrill]); if (actual === expected) { passes.push(`${relativePath}: matches generated AK-UI-05 plugin catalog interim artifact`); } else { failures.push(`${relativePath}: interim baseline drifted; run pnpm run verify:ui-visual -- --update after reviewing the change`); } } observations.push( "The UI mock static drill suite, including the AK-P20 and AK-P15a interim fixtures, renders through JSDOM in CI; browser screenshot and E2E commands remain intentionally outside this gate." ); observations.push("R-P11b dashboard baseline is an AD-28 interim baseline; re-baseline when the final D-DASH optics land."); observations.push("R-P11a Spotlight baseline is an AD-28 interim baseline; re-baseline when the final D-DASH optics land."); observations.push("R-P12b flow editor baseline pins the Subframe-delta surface contract (picker, context menu, node config, reverse recipients)."); observations.push("AK-UI-01 email inbox baseline is an AD-28 interim baseline; re-baseline when the final email inbox optics land (design-nachzug)."); observations.push("AK-UI-02 email detail baseline is an AD-28 interim baseline; re-baseline when the final email detail optics land (design-nachzug)."); observations.push("AK-UI-03 email compose baseline is an AD-28 interim baseline; re-baseline when the final compose optics land (design-nachzug)."); observations.push("AK-UI-05 plugin catalog baseline is an AD-28 interim baseline; re-baseline when the final D-P5 optics land (design-nachzug)."); observations.push("AK-P15a telephony baseline is an AD-28 interim baseline; re-baseline when the final D-P15 optics land (design-nachzug)."); observations.push("AK-P15b chat index and detail baselines are AD-28 interim baselines; re-baseline when the final D-CHAT optics land (design-nachzug)."); observations.push("VA-P24a self-service billing baseline is an AD-28 interim baseline; re-baseline when the final VD-P24 optics land (design-nachzug)."); observations.push("VA-P26a customer portal baseline is an AD-28 interim baseline; re-baseline when the final VD-P26 optics land (design-nachzug)."); observations.push( "VA-P26b customer portal interaction, admin and lifecycle baseline is an AD-28 interim baseline; re-baseline when the final VD-P26 optics land (design-nachzug)." ); observations.push( "VA-P26c customer portal domain-admin baseline is an AD-28 interim baseline; re-baseline when the final VD-P26 optics land (design-nachzug)." ); return { passes, failures, observations }; } function verifyRenderedVaP26aInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const activeLinks = [...document.querySelectorAll(".mock-link")].filter((item) => item.getAttribute("data-active") === "true"); const stateScript = document.querySelector("#mock-page-state"); if (body?.getAttribute("data-ui-mock-route") === "/:tenant" && shell?.getAttribute("data-ui-mock-page") === "va-p26a-customer-portal-interim") { passes.push("rendered VA-P26a interim HTML exposes the canonical portal route and page id"); } else { failures.push("rendered VA-P26a interim HTML is missing its canonical route or page id"); } if (activeLinks.length === 1) { passes.push("rendered VA-P26a interim navigation has exactly one active route"); } else { failures.push(`rendered VA-P26a interim navigation expected one active route but found ${activeLinks.length}`); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; if (state?.status === "ready" && state?.data?.designStatus === "interim" && state?.data?.viewModel?.data?.portalName === "Acme Kundenportal") { passes.push("rendered VA-P26a interim state contains the access-scoped portal dashboard VM"); } else { failures.push("rendered VA-P26a interim state is missing the portal dashboard VM"); } } catch { failures.push("rendered VA-P26a interim state payload is missing or invalid"); } if (document.querySelectorAll(".mock-grid .mock-card").length === 3) { passes.push("rendered VA-P26a interim HTML includes all three contract metric cards"); } else { failures.push("rendered VA-P26a interim HTML is missing contract metric cards"); } return { passes, failures }; } function verifyRenderedVaP26bInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const activeLinks = [...document.querySelectorAll(".mock-link")].filter((item) => item.getAttribute("data-active") === "true"); const stateScript = document.querySelector("#mock-page-state"); if (body?.getAttribute("data-ui-mock-route") === "/:tenant/upload" && shell?.getAttribute("data-ui-mock-page") === "va-p26b-customer-portal-interim") { passes.push("rendered VA-P26b interim HTML exposes the canonical upload route and page id"); } else { failures.push("rendered VA-P26b interim HTML is missing its canonical route or page id"); } if (activeLinks.length === 1) { passes.push("rendered VA-P26b interim navigation has exactly one active route"); } else { failures.push(`rendered VA-P26b interim navigation expected one active route but found ${activeLinks.length}`); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; const data = state?.data; const interactions = data?.viewModels?.interactions?.data; const lifecycle = data?.viewModels?.lifecycle?.data; const admin = data?.viewModels?.admin?.data; if (state?.status === "ready" && data?.designStatus === "interim" && data?.screenRoutes?.length === 5 && data?.surfaces?.length === 3) { passes.push("rendered VA-P26b interim state preserves all five real routes and three VM surfaces"); } else { failures.push("rendered VA-P26b interim state is missing routes or VM surfaces"); } if (interactions?.maxFiles === 10 && interactions?.complaintForm?.formSlug === "reklamation" && lifecycle?.actions?.canRequestDeletion === true) { passes.push("rendered VA-P26b portal VMs preserve upload, complaint and lifecycle contracts"); } else { failures.push("rendered VA-P26b portal VMs lost upload, complaint or lifecycle contracts"); } if (admin?.users?.[0]?.kind === "subcontractor" && admin?.inbox?.[0]?.kind === "complaint" && admin?.inbox?.[1]?.kind === "upload") { passes.push("rendered VA-P26b admin VM preserves subcontractor isolation and inbox priority"); } else { failures.push("rendered VA-P26b admin VM lost subcontractor or service-inbox contracts"); } } catch { failures.push("rendered VA-P26b interim state payload is missing or invalid"); } if (document.querySelectorAll(".mock-grid .mock-card").length === 3) { passes.push("rendered VA-P26b interim HTML includes all three contract metric cards"); } else { failures.push("rendered VA-P26b interim HTML is missing contract metric cards"); } return { passes, failures }; } function verifyRenderedVaP26cInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const activeLinks = [...document.querySelectorAll(".mock-link")].filter((item) => item.getAttribute("data-active") === "true"); const stateScript = document.querySelector("#mock-page-state"); const registriesScript = document.querySelector("#mock-page-registries"); if ( body?.getAttribute("data-ui-mock-route") === "/:tenant/settings/customer-portal/domain" && shell?.getAttribute("data-ui-mock-page") === "va-p26c-customer-portal-domain-interim" ) { passes.push("rendered VA-P26c domain-admin HTML exposes its canonical route and page id"); } else { failures.push("rendered VA-P26c domain-admin HTML is missing its canonical route or page id"); } if (activeLinks.length === 1) passes.push("rendered VA-P26c domain-admin navigation has exactly one active route"); else failures.push(`rendered VA-P26c domain-admin navigation expected one active route but found ${activeLinks.length}`); try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; const data = state?.data; const vm = data?.viewModel?.data; if (state?.status === "ready" && data?.designStatus === "interim" && data?.screenRoute === "/:tenant/settings/customer-portal/domain") { passes.push("rendered VA-P26c state preserves its AD-28 marker and canonical screen route"); } else { failures.push("rendered VA-P26c state lost its interim marker or screen route"); } if (vm?.portalEntitlementSource === "base_plan" && vm?.domain?.status === "cooldown" && vm?.pendingSwitch?.status === "dns_verified") { passes.push("rendered VA-P26c VM preserves entitlement, safe fallback lifecycle and pending-switch state"); } else { failures.push("rendered VA-P26c VM lost entitlement, domain lifecycle or pending-switch state"); } if (vm?.pendingSwitch?.txtRecordValue === "hivegrid-verification-proof-once" && vm?.actions?.canActivateSwitch === true) { passes.push("rendered VA-P26c VM preserves the one-time claim-bound DNS proof and activation action"); } else { failures.push("rendered VA-P26c VM lost the claim-bound DNS proof or activation action"); } } catch { failures.push("rendered VA-P26c state payload is missing or invalid"); } try { const registries = registriesScript?.textContent ? JSON.parse(decodeHtmlEntities(registriesScript.textContent)) : null; if (registries?.sidebar?.[0]?.permission === "portal.admin" && registries?.breadcrumbs?.length === 3) { passes.push("rendered VA-P26c registry preserves the portal.admin gate and domain breadcrumb"); } else { failures.push("rendered VA-P26c registry lost its permission gate or breadcrumb contract"); } } catch { failures.push("rendered VA-P26c registry payload is missing or invalid"); } return { passes, failures }; } function verifyRenderedVaP24aInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const activeLinks = [...document.querySelectorAll(".mock-link")].filter((item) => item.getAttribute("data-active") === "true"); const breadcrumbs = [...document.querySelectorAll(".mock-breadcrumbs span")].filter((item) => item.textContent !== "/"); const stateScript = document.querySelector("#mock-page-state"); if (body?.getAttribute("data-ui-mock-route") === "/:tenant/settings/billing" && shell?.getAttribute("data-ui-mock-page") === "va-p24a-interim") { passes.push("rendered VA-P24a interim HTML exposes the canonical subscription route and page id"); } else { failures.push("rendered VA-P24a interim HTML is missing its canonical route or page id"); } if (activeLinks.length === 1 && breadcrumbs.length === 2) { passes.push("rendered VA-P24a interim registries expose one active route and two breadcrumbs"); } else { failures.push(`rendered VA-P24a interim registries expected one active route and two breadcrumbs but found ${activeLinks.length}/${breadcrumbs.length}`); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; const viewModel = state?.data?.viewModel; if (state?.status === "ready" && state?.data?.designStatus === "interim" && state?.data?.screenRoute === "/:tenant/settings/billing") { passes.push("rendered VA-P24a state preserves its ready AD-28 interim route contract"); } else { failures.push("rendered VA-P24a state lost its ready/interim route contract"); } const plans: readonly { readonly prices?: readonly unknown[] }[] = viewModel?.catalog?.plans ?? []; const priceCount = plans.reduce((total, plan) => total + (plan.prices?.length ?? 0), 0); if ( viewModel?.overview?.status === "ready" && plans.length === 2 && priceCount === 4 && viewModel?.minimumSeatQuantity === 3 && viewModel?.canManage === true && viewModel?.trialPolicy?.requiresManualActivation === false && viewModel?.trialPolicy?.requiresPaymentMethod === true ) { passes.push("rendered VA-P24a state carries the serializable overview, catalog, seats and trial policy VM"); } else { failures.push("rendered VA-P24a state lost its self-service subscription view-model contract"); } } catch { failures.push("rendered VA-P24a interim state payload is missing or invalid"); } return { passes, failures }; } function verifyRenderedTelephonyInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const activeLinks = [...document.querySelectorAll(".mock-link")].filter((item) => item.getAttribute("data-active") === "true"); const stateScript = document.querySelector("#mock-page-state"); const registriesScript = document.querySelector("#mock-page-registries"); if (body?.getAttribute("data-ui-mock-route") === "/:tenant/calls" && shell?.getAttribute("data-ui-mock-page") === "ak-p15a-telephony-interim") { passes.push("rendered AK-P15a telephony interim HTML exposes the calls route and interim page id"); } else { failures.push("rendered AK-P15a telephony interim HTML is missing its route or page id"); } if (activeLinks.length === 1) { passes.push("rendered AK-P15a telephony registry has exactly one active route"); } else { failures.push(`rendered AK-P15a telephony registry expected one active route but found ${activeLinks.length}`); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; const surfaces: readonly string[] = state?.data?.surfaces ?? []; const viewModels = state?.data?.viewModels; if (state?.status === "ready" && state?.data?.designStatus === "interim" && surfaces.join(",") === "settings,calls,callDetail,clickToCall") { passes.push("rendered AK-P15a telephony state preserves all four AD-28 interim surfaces"); } else { failures.push("rendered AK-P15a telephony state lost its interim marker or surface contract"); } if ( viewModels?.settings?.data?.connection?.provider === "aircall" && viewModels?.calls?.data?.rows?.length === 2 && viewModels?.callDetail?.data?.id === "call-1" && viewModels?.clickToCall?.mode === "provider" ) { passes.push("rendered AK-P15a telephony state carries the serializable settings, list, detail, and click-to-call VMs"); } else { failures.push("rendered AK-P15a telephony state lost one or more serializable view models"); } } catch { failures.push("rendered AK-P15a telephony state payload is missing or invalid"); } try { const registries = registriesScript?.textContent ? JSON.parse(decodeHtmlEntities(registriesScript.textContent)) : null; const permissions = (registries?.sidebar ?? []).map((item: { permission?: string }) => item.permission); if (permissions.join(",") === "telephony.read_own,telephony.manage" && registries?.breadcrumbs?.length === 2) { passes.push("rendered AK-P15a telephony registry preserves permission gates and call-detail breadcrumbs"); } else { failures.push("rendered AK-P15a telephony registry lost its permission gates or breadcrumbs"); } } catch { failures.push("rendered AK-P15a telephony registry payload is missing or invalid"); } return { passes, failures }; } function verifyRenderedChatIndexInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const activeLinks = [...document.querySelectorAll(".mock-link")].filter((item) => item.getAttribute("data-active") === "true"); const stateScript = document.querySelector("#mock-page-state"); const registriesScript = document.querySelector("#mock-page-registries"); if (body?.getAttribute("data-ui-mock-route") === "/:tenant/chat" && shell?.getAttribute("data-ui-mock-page") === "ak-p15b-chat-index-interim") { passes.push("rendered AK-P15b chat index interim HTML exposes the channel index route and page id"); } else { failures.push("rendered AK-P15b chat index interim HTML is missing its route or page id"); } if (activeLinks.length === 1) { passes.push("rendered AK-P15b chat index registry has exactly one active route"); } else { failures.push(`rendered AK-P15b chat index registry expected one active route but found ${activeLinks.length}`); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; if (state?.status === "ready" && state?.data?.designStatus === "interim" && state?.data?.screenRoute === "/chat") { passes.push("rendered AK-P15b chat index state preserves its AD-28 interim marker and screen route"); } else { failures.push("rendered AK-P15b chat index state lost its interim marker or screen route"); } const viewModel = state?.data?.viewModel; const rows: readonly { id?: string; hasUnread?: boolean; hasMention?: boolean; isMuted?: boolean }[] = viewModel?.data?.rows ?? []; const discoverRows: readonly { id?: string; joinHref?: string | null; detailHref?: string | null; canJoin?: boolean }[] = viewModel?.data?.discoverRows ?? []; if ( viewModel?.status === "ready" && rows.map((row) => row.id).join(",") === "chat-group-montage,chat-dm-mara" && rows[0]?.hasUnread === true && rows[0]?.hasMention === true && rows[1]?.isMuted === true && viewModel?.data?.totals?.unread === 3 && viewModel?.data?.permissions?.canDiscover === true && discoverRows.length === 1 && discoverRows[0]?.id === "chat-public-service" && discoverRows[0]?.joinHref === "/:tenant/chat" && discoverRows[0]?.detailHref === null && discoverRows[0]?.canJoin === true ) { passes.push("rendered AK-P15b chat index state keeps member channels and join-only public discovery available"); } else { failures.push("rendered AK-P15b chat index state lost its member-channel list contract"); } } catch { failures.push("rendered AK-P15b chat index state payload is missing or invalid"); } try { const registries = registriesScript?.textContent ? JSON.parse(decodeHtmlEntities(registriesScript.textContent)) : null; if ( registries?.sidebar?.length === 1 && registries.sidebar[0]?.permission === "chat.read_own_channels" && registries?.breadcrumbs?.length === 1 && registries.breadcrumbs[0]?.permission === "chat.read_own_channels" ) { passes.push("rendered AK-P15b chat index registry preserves its read permission and breadcrumb contract"); } else { failures.push("rendered AK-P15b chat index registry lost its read permission or breadcrumb contract"); } } catch { failures.push("rendered AK-P15b chat index registry payload is missing or invalid"); } return { passes, failures }; } function verifyRenderedChatDetailInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const activeLinks = [...document.querySelectorAll(".mock-link")].filter((item) => item.getAttribute("data-active") === "true"); const stateScript = document.querySelector("#mock-page-state"); const registriesScript = document.querySelector("#mock-page-registries"); if (body?.getAttribute("data-ui-mock-route") === "/:tenant/chat/:id" && shell?.getAttribute("data-ui-mock-page") === "ak-p15b-chat-detail-interim") { passes.push("rendered AK-P15b chat detail interim HTML exposes the channel detail route and page id"); } else { failures.push("rendered AK-P15b chat detail interim HTML is missing its route or page id"); } if (activeLinks.length === 1) { passes.push("rendered AK-P15b chat detail registry has exactly one active route"); } else { failures.push(`rendered AK-P15b chat detail registry expected one active route but found ${activeLinks.length}`); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; if (state?.status === "ready" && state?.data?.designStatus === "interim" && state?.data?.screenRoute === "/chat/:id") { passes.push("rendered AK-P15b chat detail state preserves its AD-28 interim marker and screen route"); } else { failures.push("rendered AK-P15b chat detail state lost its interim marker or screen route"); } const viewModel = state?.data?.viewModel; const messages: readonly { id?: string; clientMessageId?: string; source?: string; deliveryState?: string }[] = viewModel?.data?.messages ?? []; if ( viewModel?.status === "ready" && messages.length === 2 && messages.map((message) => message.source).join(",") === "persisted,http_ack" && messages[1]?.id === "message-raffael-1" && messages[1]?.clientMessageId === "client-raffael-1" && messages[1]?.deliveryState === "sent" ) { passes.push("rendered AK-P15b chat detail state reconciles local echo and HTTP acknowledgement into one sent message"); } else { failures.push("rendered AK-P15b chat detail state lost its optimistic-send reconciliation contract"); } if ( viewModel?.data?.composer?.enabled === true && viewModel?.data?.composer?.canAttachFiles === false && viewModel?.data?.connection?.state === "online" && viewModel?.data?.history?.hasOlder === true && viewModel?.data?.history?.olderCursor === "cursor-message-older" && viewModel?.data?.typingLabels?.join(",") === "Mara Klein" ) { passes.push("rendered AK-P15b chat detail state keeps the live composer, connection, typing, and cursor-history contract"); } else { failures.push("rendered AK-P15b chat detail state lost its live composer, connection, typing, or history contract"); } } catch { failures.push("rendered AK-P15b chat detail state payload is missing or invalid"); } try { const registries = registriesScript?.textContent ? JSON.parse(decodeHtmlEntities(registriesScript.textContent)) : null; if ( registries?.sidebar?.length === 1 && registries.sidebar[0]?.permission === "chat.read_own_channels" && registries?.breadcrumbs?.length === 2 && registries.breadcrumbs.every((item: { permission?: string }) => item.permission === "chat.read_own_channels") ) { passes.push("rendered AK-P15b chat detail registry preserves its read permission and detail breadcrumbs"); } else { failures.push("rendered AK-P15b chat detail registry lost its read permission or detail breadcrumbs"); } } catch { failures.push("rendered AK-P15b chat detail registry payload is missing or invalid"); } return { passes, failures }; } function verifyRenderedEmailDetailInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const stateScript = document.querySelector("#mock-page-state"); if ( body?.getAttribute("data-ui-mock-route") === "/mock/ak-ui-02/emails/msg-1" && shell?.getAttribute("data-ui-mock-page") === "ak-ui-02-email-detail-interim" ) { passes.push("rendered AK-UI-02 email detail interim HTML exposes its canonical mock route and page id"); } else { failures.push("rendered AK-UI-02 email detail interim HTML is missing its canonical mock route or page id"); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; if (state?.status === "ready" && state?.data?.designStatus === "interim" && state?.data?.screenRoute === "/emails/:id") { passes.push("rendered AK-UI-02 email detail state preserves its AD-28 interim marker and screen route"); } else { failures.push("rendered AK-UI-02 email detail state is missing its interim marker or screen route"); } const viewModel = state?.data?.viewModel; const thread: readonly { id?: string; isFocused?: boolean }[] = viewModel?.data?.thread ?? []; if ( viewModel?.status === "ready" && thread.map((entry) => entry.id).join(",") === "msg-0,msg-1" && thread.filter((entry) => entry.isFocused).length === 1 ) { passes.push("rendered AK-UI-02 email detail state carries the serializable thread VM with exactly one focused message"); } else { failures.push("rendered AK-UI-02 email detail state lost the thread VM contract"); } // The sanitized bodies must survive serialization and external images must stay blocked by default. if (viewModel?.data?.allowExternalImages === false && String(viewModel?.data?.thread?.[1]?.safeBodyHtml ?? "").includes("
")) { passes.push("rendered AK-UI-02 email detail state keeps the sanitized bodies and the blocked-image default"); } else { failures.push("rendered AK-UI-02 email detail state lost the sanitized bodies or the blocked-image default"); } } catch { failures.push("rendered AK-UI-02 email detail state payload is not parseable JSON"); } return { passes, failures }; } function verifyRenderedEmailComposeInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const stateScript = document.querySelector("#mock-page-state"); if ( body?.getAttribute("data-ui-mock-route") === "/mock/ak-ui-03/emails/new" && shell?.getAttribute("data-ui-mock-page") === "ak-ui-03-email-compose-interim" ) { passes.push("rendered AK-UI-03 email compose interim HTML exposes its canonical mock route and page id"); } else { failures.push("rendered AK-UI-03 email compose interim HTML is missing its canonical mock route or page id"); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; if (state?.status === "ready" && state?.data?.designStatus === "interim" && state?.data?.screenRoute === "/emails/new") { passes.push("rendered AK-UI-03 email compose state preserves its AD-28 interim marker and screen route"); } else { failures.push("rendered AK-UI-03 email compose state is missing its interim marker or screen route"); } const viewModel = state?.data?.viewModel; const mailboxes: readonly { id?: string }[] = viewModel?.data?.mailboxes ?? []; if (viewModel?.status === "ready" && mailboxes.map((mailbox) => mailbox.id).join(",") === "mailbox-1,mailbox-2") { passes.push("rendered AK-UI-03 email compose state carries the serializable compose VM with its mailbox options"); } else { failures.push("rendered AK-UI-03 email compose state lost the compose VM contract"); } if (viewModel?.data?.noMailboxKey === "emailInbox.compose.noMailbox" && String(viewModel?.data?.prefill?.quotedHtml ?? "").includes("
row.pluginId).join(",") === "com.demo.outbound,com.demo.workflow-hook") { passes.push("rendered AK-UI-05 plugin catalog state carries the serializable plugin catalog VM with both fixture plugins"); } else { failures.push("rendered AK-UI-05 plugin catalog state lost the plugin catalog VM contract"); } // The consent gate is the security-relevant part of this screen: a plugin // with a missing grant must never be directly enable-able, and its approval // has to fold the enable into the grant op. const pending = rows[1]; if (pending?.statusKey === "shared.disabled" && pending?.canEnable === false && pending?.grantOp === "grantAndEnable") { passes.push("rendered AK-UI-05 plugin catalog state keeps the consent gate: no enable without an approved capability grant"); } else { failures.push("rendered AK-UI-05 plugin catalog state lost the capability consent gate"); } } catch { failures.push("rendered AK-UI-05 plugin catalog state payload is not parseable JSON"); } return { passes, failures }; } function verifyRenderedEmailInboxInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const stateScript = document.querySelector("#mock-page-state"); if (body?.getAttribute("data-ui-mock-route") === "/mock/ak-ui-01/emails" && shell?.getAttribute("data-ui-mock-page") === "ak-ui-01-email-inbox-interim") { passes.push("rendered AK-UI-01 email inbox interim HTML exposes its canonical mock route and page id"); } else { failures.push("rendered AK-UI-01 email inbox interim HTML is missing its canonical mock route or page id"); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; if (state?.status === "ready" && state?.data?.designStatus === "interim" && state?.data?.screenRoute === "/emails") { passes.push("rendered AK-UI-01 email inbox state preserves its AD-28 interim marker and screen route"); } else { failures.push("rendered AK-UI-01 email inbox state is missing its interim marker or screen route"); } const viewModel = state?.data?.viewModel; const rows: readonly { id?: string }[] = viewModel?.data?.rows ?? []; if (viewModel?.status === "ready" && rows.map((row) => row.id).join(",") === "msg-1,msg-2" && viewModel?.data?.nextCursor) { passes.push("rendered AK-UI-01 email inbox state carries the serializable inbox list VM with rows and cursor"); } else { failures.push("rendered AK-UI-01 email inbox state lost the inbox list VM contract"); } if ( viewModel?.data?.emptyHeadlineKey === "emailInbox.list.empty.headline" && viewModel?.data?.noMailboxHeadlineKey === "emailInbox.list.noMailbox.headline" ) { passes.push("rendered AK-UI-01 email inbox state keeps the Klartext empty/no-mailbox keys"); } else { failures.push("rendered AK-UI-01 email inbox state lost the Klartext empty/no-mailbox keys"); } } catch { failures.push("rendered AK-UI-01 email inbox state payload is not parseable JSON"); } return { passes, failures }; } function verifyRenderedAutomationEditorDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const stateScript = document.querySelector("#mock-page-state"); if (body?.getAttribute("data-ui-mock-route") === "/mock/r-p12b/editor" && shell?.getAttribute("data-ui-mock-page") === "r-p12b-editor") { passes.push("rendered R-P12b flow editor HTML exposes its canonical mock route and page id"); } else { failures.push("rendered R-P12b flow editor HTML is missing its canonical mock route or page id"); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; if (state?.status === "ready" && state?.data?.designStatus === "subframe" && state?.data?.editor?.steps?.length === 3) { passes.push("rendered R-P12b flow editor state pins the Subframe design marker and the three-step fixture flow"); } else { failures.push("rendered R-P12b flow editor state is missing its subframe marker or fixture steps"); } const recipients: readonly unknown[] = state?.data?.reverseExample?.recipients ?? []; if (state?.data?.reverseExample?.reversible === false && recipients.length === 2) { passes.push("rendered R-P12b flow editor state preserves the non-retractable reverse recipient contract"); } else { failures.push("rendered R-P12b flow editor state lost the reverse recipient contract"); } } catch { failures.push("rendered R-P12b flow editor state payload is not parseable JSON"); } return { passes, failures }; } function verifyRenderedSpotlightInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const activeLinks = [...document.querySelectorAll(".mock-link")].filter((item) => item.getAttribute("data-active") === "true"); const stateScript = document.querySelector("#mock-page-state"); const registriesScript = document.querySelector("#mock-page-registries"); if (body?.getAttribute("data-ui-mock-route") === "/mock/r-p11a/spotlight" && shell?.getAttribute("data-ui-mock-page") === "r-p11a-spotlight-interim") { passes.push("rendered R-P11a Spotlight interim HTML exposes only its canonical mock route and page id"); } else { failures.push("rendered R-P11a Spotlight interim HTML is missing its canonical mock route or page id"); } if (activeLinks.length === 0) { passes.push("rendered R-P11a Spotlight interim navigation has no user-facing route entry"); } else { failures.push(`rendered R-P11a Spotlight interim expected no active navigation route but found ${activeLinks.length}`); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; const groups: readonly { entityType?: string; items?: readonly unknown[] }[] = state?.data?.groups ?? []; if (state?.status === "ready" && state?.data?.designStatus === "interim") { passes.push("rendered R-P11a Spotlight state preserves its AD-28 interim marker"); } else { failures.push("rendered R-P11a Spotlight state is missing its ready/interim marker"); } if (state?.data?.surfaces?.desktop === "modal" && state?.data?.surfaces?.mobile === "viewport") { passes.push("rendered R-P11a Spotlight state preserves desktop modal and mobile viewport surfaces"); } else { failures.push("rendered R-P11a Spotlight state lost its desktop/mobile surface contract"); } if (groups.map((group) => group.entityType).join(",") === "contact,project" && groups.every((group) => (group.items?.length ?? 0) === 1)) { passes.push("rendered R-P11a Spotlight state preserves stable grouped search results"); } else { failures.push("rendered R-P11a Spotlight state lost its grouped search results"); } if (state?.data?.initialResultLimit === 20 && state?.data?.maxResultLimit === 50 && state?.data?.recentSearches?.length === 1) { passes.push("rendered R-P11a Spotlight state preserves result limits and RecentSearch fixture data"); } else { failures.push("rendered R-P11a Spotlight state lost result limits or RecentSearch fixture data"); } } catch { failures.push("rendered R-P11a Spotlight state payload is missing or invalid"); } try { const registries = registriesScript?.textContent ? JSON.parse(decodeHtmlEntities(registriesScript.textContent)) : null; if ( registries?.portals?.length === 1 && registries.portals[0]?.slot === "shell.commandPalette" && registries.portals[0]?.permission === "search.use" && !registries?.sidebar && !registries?.mobile ) { passes.push("rendered R-P11a Spotlight registry is permission-gated and portal-only"); } else { failures.push("rendered R-P11a Spotlight registry is not permission-gated portal-only chrome"); } } catch { failures.push("rendered R-P11a Spotlight registry payload is missing or invalid"); } return { passes, failures }; } function verifyRenderedDashboardInterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const stateScript = document.querySelector("#mock-page-state"); if (body?.getAttribute("data-ui-mock-route") === "/mock/r-p11b/dashboard" && body?.getAttribute("data-ui-mock-status") === "ready") { passes.push("rendered R-P11b dashboard interim HTML exposes canonical mock route and ready status"); } else { failures.push("rendered R-P11b dashboard interim HTML is missing canonical mock route or ready status"); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; // The payload ships exactly the layout the server derived from the member's // seat — the other fixed layouts are neither rendered nor selectable. const layout: { role?: string; mobileFirst?: boolean; widgets?: readonly unknown[] } | undefined = state?.data?.layout; if (state?.status === "ready" && state?.data?.activeRole === "gf" && layout?.role === "gf") { passes.push("rendered R-P11b dashboard state payload carries the derived role layout"); } else { failures.push("rendered R-P11b dashboard state payload is missing the derived role layout"); } if (state?.data?.roles === undefined && (layout?.widgets?.length ?? 0) > 0) { passes.push("rendered R-P11b dashboard state payload ships only the active layout"); } else { failures.push("rendered R-P11b dashboard state payload still carries the non-active role layouts"); } } catch { failures.push("rendered R-P11b dashboard state payload is missing or invalid"); } return { passes, failures }; } function verifyRenderedP20InterimDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const activeLinks = [...document.querySelectorAll(".mock-link")].filter((item) => item.getAttribute("data-active") === "true"); const cards = [...document.querySelectorAll(".mock-grid .mock-card")]; const activity = [...document.querySelectorAll(".mock-activity li")]; const stateScript = document.querySelector("#mock-page-state"); if (body?.getAttribute("data-ui-mock-route") === "/:tenant/settings/billing" && shell?.getAttribute("data-ui-mock-page") === "ak-p20-interim") { passes.push("rendered AK-P20 interim HTML exposes the subscription route and interim page id"); } else { failures.push("rendered AK-P20 interim HTML is missing its route or page id"); } if (activeLinks.length === 1 && activity.length === 5) { passes.push("rendered AK-P20 interim navigation has one active route and covers five screens"); } else { failures.push(`rendered AK-P20 interim fixture expected one active route and five screens but found ${activeLinks.length}/${activity.length}`); } if (cards.length === 3 && document.body.textContent?.includes("AK-P20 interim UI")) { passes.push("rendered AK-P20 interim page includes coverage cards and its interim title"); } else { failures.push("rendered AK-P20 interim page is missing coverage cards or its interim title"); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; const viewModelCount = state?.data?.viewModels ? Object.keys(state.data.viewModels).length : 0; if (state?.status === "ready" && state?.data?.designStatus === "interim" && viewModelCount === 5) { passes.push("rendered AK-P20 interim state preserves all five serializable view models"); } else { failures.push("rendered AK-P20 interim state is missing its interim marker or view models"); } } catch { failures.push("rendered AK-P20 interim state payload is missing or invalid"); } return { passes, failures }; } function verifyRenderedHotSwapDrill(html: string): { passes: string[]; failures: string[] } { const passes: string[] = []; const failures: string[] = []; const document = new JSDOM(html).window.document; const body = document.body; const shell = document.querySelector(".mock-shell"); const activeLinks = [...document.querySelectorAll(".mock-link")].filter((item) => item.getAttribute("data-active") === "true"); const cards = [...document.querySelectorAll('section[aria-label="Mock metrics"] .mock-card')]; const stateScript = document.querySelector("#mock-page-state"); if (body?.getAttribute("data-ui-mock-route") === "/mock/phase-c/hot-swap" && shell?.getAttribute("data-ui-mock-page") === "phase-c-hot-swap") { passes.push("rendered hot-swap HTML exposes canonical mock route and page id"); } else { failures.push("rendered hot-swap HTML is missing canonical mock route or page id"); } if (activeLinks.length === 1) { passes.push("rendered hot-swap navigation has exactly one active route"); } else { failures.push(`rendered hot-swap navigation expected one active route but found ${activeLinks.length}`); } if (cards.length >= 3 && document.body.textContent?.includes("Serialized state")) { passes.push("rendered hot-swap page includes metric cards and serialized state"); } else { failures.push("rendered hot-swap page is missing metric cards or serialized state"); } try { const state = stateScript?.textContent ? JSON.parse(decodeHtmlEntities(stateScript.textContent)) : null; if (state?.status === "ready") { passes.push("rendered hot-swap state payload parses as ready"); } else { failures.push("rendered hot-swap state payload is missing or invalid"); } } catch { failures.push("rendered hot-swap state payload is missing or invalid"); } return { passes, failures }; } function decodeHtmlEntities(value: string) { return value .replace(/"/g, '"') .replace(/'/g, "'") .replace(/</g, "<") .replace(/>/g, ">") .replace(/&/g, "&"); } export function formatUiMockVisualResult(result: UiMockVisualResult): string { const status = result.failures.length > 0 ? "FAIL" : "PASS"; return [ `${status} UI mock static artifact baseline verification`, ...result.passes.map((pass) => `PASS ${pass}`), ...result.observations.map((observation) => `OBSERVE ${observation}`), ...result.failures.map((failure) => `FAIL ${failure}`), `Summary: ${result.passes.length} passed, ${result.observations.length} observed, ${result.failures.length} failed`, "", ].join("\n"); } function normalizeNewlines(value: string): string { return value.replace(/\r\n/g, "\n"); } export function assertVisualBaselineUpdateAllowed(env: NodeJS.ProcessEnv = process.env): void { if (env.CI) { throw new Error("Refusing to update UI mock static artifact baselines while CI is set."); } } const isCli = process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1]); if (isCli) { if (process.argv.includes("--update")) { assertVisualBaselineUpdateAllowed(); writeUiMockVisualBaseline(process.cwd()); } const result = verifyUiMockVisualBaseline(process.cwd()); const output = formatUiMockVisualResult(result); if (result.failures.length > 0) { process.stderr.write(output); process.exitCode = 1; } else { process.stdout.write(output); } }