Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/scripts/verify-ui-mock-visual.test.ts
T

372 lines
18 KiB
TypeScript

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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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<!-- drift -->\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\./
);
});
});