Files
govoplan-mail/webui/scripts/test-interface-pattern-language.mjs
T
zemion 22c8d0af43 fix(ui): align contextual documentation with headings
Verified with the coordinated workspace changes by devkit full run
2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed).
This shared UI pass does not mark the individual module reviews complete.
2026-09-09 02:03:54 +02:00

115 lines
7.9 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
function read(relativePath) {
return readFileSync(fileURLToPath(new URL(relativePath, import.meta.url)), "utf8");
}
const profiles = `${read("../src/features/mail/MailProfileManagement.tsx")}\n${read("../src/features/mail/MailProfilePolicyEditor.tsx")}`;
const mailbox = read("../src/features/mail/MailboxPage.tsx");
const mailApi = read("../src/api/mail.ts");
const bounces = read("../src/features/mail/MailBouncePage.tsx");
const legacyImport = read("../src/features/mail/MailLegacyImportPage.tsx");
const moduleSource = read("../src/module.ts");
const styles = read("../src/styles/mail-profiles.css");
const migration = read("../../docs/INTERFACE_PATTERN_MIGRATION.md");
assert.match(profiles, /ActionBlockerHint,[\s\S]*DocumentationHelpLink/);
for (const sharedComponent of ["ConnectionTree", "ConfirmDialog", "Dialog", "LoadingFrame"]) {
assert.match(profiles, new RegExp(`\\b${sharedComponent}\\b`));
}
assert.match(profiles, /topicId: "mail\.profiles-and-policy"/);
assert.match(profiles, /disabledReason: credentialMutationBlocker/);
assert.match(profiles, /disabledReason=\{editorSaveBlocker\}/);
assert.match(profiles, /disabledReason=\{policySaveBlocker\}/);
assert.match(profiles, /smtpActionDisabledReason=\{smtpTestBlocker\}/);
assert.match(profiles, /folder_mappings: draft\.imapFolderMappings/);
assert.match(profiles, /listMailProfileImapFolders[\s\S]*listImapFolders/);
assert.match(profiles, /onLookupImapFolders=\{\(\) => void runImapFolderLookup\(\)\}/);
assert.match(profiles, /imapFolderLookupResult=\{imapFolderResult\}/);
assert.match(mailbox, /ActionBlockerHint/);
assert.match(mailbox, /DocumentationHelpLink/);
assert.match(mailbox, /topicId: "mail\.workflow\.read-mailbox"/);
assert.match(mailbox, /disabledReason=\{folderReloadBlocker\}/);
assert.match(mailbox, /folder_mappings\?\.inbox/);
assert.match(mailbox, /detected_folder_mappings\?\.inbox/);
const openFolderNode = mailbox.slice(mailbox.indexOf(" function openFolderNode("), mailbox.indexOf(" function toggleFolderNode("));
assert.doesNotMatch(openFolderNode, /toggleFolderNode\(/, "Folder labels select; only the folder button expands or collapses.");
assert.match(openFolderNode, /setSelectedFolderGroup\(\{ id: node\.id, label: node\.label \}\)/);
assert.match(openFolderNode, /messageDetailRequestRef\.current \+= 1/, "Selecting a grouping invalidates pending message detail.");
assert.match(mailbox, /selectedFolderGroup\?\.id \?\? findFolderNodeId/);
assert.match(mailbox, /<WorkspaceFrame[^>]*height="viewport"/);
assert.match(mailbox, /<WorkspaceActionBar\s+title="i18n:govoplan-mail\.mailbox_workspace"\s+titleHelp=\{<DocumentationHelpLink reference=\{MAILBOX_DOCUMENTATION\} \/>\}\s+variant="workspace"\s+scope="workspace"[\s\S]*refreshable[\s\S]*onReload: \(\) => void reloadMailbox\(\)/);
assert.ok(mailbox.indexOf("<WorkspaceActionBar") < mailbox.indexOf('<aside className="file-tree-panel"'), "Mailbox actions remain outside all selectable panes.");
assert.equal((mailbox.match(/<WorkspaceActionBar/g) ?? []).length, 1, "One persistent page-wide action bar.");
assert.doesNotMatch(mailbox, /mailbox-toolbar|<ToolbarGroup/);
assert.match(mailbox, /const MAILBOX_READ_OPTIONS = \{ cache: "no-store" \}/);
assert.match(mailbox, /listMailServerProfiles\(settings, false, undefined, MAILBOX_READ_OPTIONS\)/);
for (const helper of ["listMailServerProfiles", "listMailboxFolders", "bootstrapMailbox", "listMailboxMessages", "getMailboxMessage"]) {
const start = mailApi.indexOf(`export async function ${helper}(`);
const end = mailApi.indexOf("\nexport async function ", start + 1);
const source = mailApi.slice(start, end < 0 ? undefined : end);
assert.match(source, /options\?: MailboxReadOptions/, `${helper} supports a scoped fresh read.`);
assert.match(source, /\}\), options\)/, `${helper} passes the read options to Core without global invalidation.`);
}
assert.match(mailbox, /<Dialog open=\{mailToolsOpen\}[\s\S]*mailbox_refresh_tools[\s\S]*refresh_mailbox_profiles[\s\S]*refresh_mailbox_folder_catalogue[\s\S]*refresh_mailbox_message_index[\s\S]*mailbox_related_tools/);
const refreshMailbox = mailbox.slice(mailbox.indexOf(" async function reloadMailbox("), mailbox.indexOf(" function selectProfile("));
assert.match(refreshMailbox, /await loadProfiles\(\)[\s\S]*if \(selectedFolderGroup\) await refreshFolderCatalogue\(result.selected\)/);
assert.match(refreshMailbox, /requestAuthority !== authorityRef.current/);
assert.match(refreshMailbox, /profileId === selectedProfileIdRef.current/);
assert.match(refreshMailbox, /if \(!preservePreview\) setSelectedMessage\(null\)/, "Failed preview refresh keeps the previously loaded message.");
assert.doesNotMatch(refreshMailbox, /sendMail|appendMail|moveMail|deleteMail|setFlags|createMail/, "Mailbox reload must remain provider-read-only.");
assert.match(mailbox, /onKeyDown=\{\(event\) => \{[\s\S]*event\.key === "Enter" \|\| event\.key === " "/);
assert.match(bounces, /DocumentationHelpLink/);
assert.match(bounces, /topicId: "mail\.bounce-processing"/);
assert.match(bounces, /<ConfirmDialog[\s\S]*confirmLabel="Remove watcher"[\s\S]*tone="danger"/);
assert.match(bounces, /disabledReason=\{saveWatcherBlocker\}/);
for (const sharedComponent of ["PageLayout", "PageActionBar", "SelectionList", "DataGrid", "Dialog", "ConfirmDialog", "ToggleSwitch", "StatusBadge"]) {
assert.match(legacyImport, new RegExp(`\\b${sharedComponent}\\b`));
}
assert.match(legacyImport, /archetype="collection"/);
assert.match(legacyImport, /variant="collection"[\s\S]*refreshable[\s\S]*reloadAction=/);
assert.match(legacyImport, /topicId: "mail\.workflow\.legacy-pop3-import"/);
assert.match(legacyImport, /helpContextId="mail\.pop3"[\s\S]*helpTopicId="mail\.workflow\.legacy-pop3-import"/);
for (const helpContextId of [
"mail.pop3.source-editor",
"mail.pop3.action.reload",
"mail.pop3.action.save-source",
"mail.pop3.action.import",
"mail.pop3.field.transport-security",
"mail.pop3.field.max-message-size",
"mail.pop3.field.max-batch-size",
"mail.pop3.field.password",
"mail.pop3.field.delete-after-import",
"mail.pop3.confirm-delete-source",
]) {
assert.match(
legacyImport,
new RegExp(`helpContextId(?:=|:\\s*)"${helpContextId.replaceAll(".", "\\.")}"`)
);
}
assert.match(legacyImport, /data-help-context-id="mail\.pop3\.field\.message-selection"/);
assert.match(legacyImport, /<ConfirmDialog[\s\S]*tone="danger"[\s\S]*onConfirm=\{\(\) => void runImport\(\)\}/);
assert.match(legacyImport, /legacy_import_enabled: false[\s\S]*is_active: false[\s\S]*createMailServerCredential[\s\S]*updateMailServerEndpoint/);
assert.match(legacyImport, /expected_transport_revision: preview\.transport_revision/);
assert.doesNotMatch(`${profiles}\n${mailbox}\n${bounces}\n${legacyImport}`, /window\.(?:alert|confirm)\(/);
assert.doesNotMatch(`${profiles}\n${mailbox}\n${bounces}\n${legacyImport}\n${moduleSource}`, /@govoplan\/(?:campaign|files|docs|calendar)-webui|govoplan_(?:campaign|files|docs|calendar)/);
assert.match(moduleSource, /"mail\.profiles"/);
assert.match(styles, /@media \(max-width: 900px\)[\s\S]*\.mail-profile-transport-summary[\s\S]*grid-template-columns: 1fr/);
assert.match(styles, /@media \(max-width: 1280px\)[\s\S]*\.mailbox-shell\.file-manager-shell[\s\S]*grid-template-columns:/);
assert.doesNotMatch(styles, /\.mailbox-toolbar/, "Core owns workspace toolbar wrapping and action ordering.");
assert.match(styles, /@media \(max-width: 760px\)[\s\S]*\.mailbox-message-row[\s\S]*grid-template-columns: 1fr/);
for (const archetype of ["Directory/explorer", "Administration/configuration", "Effective-policy editor", "Evidence/reporting"]) {
assert.match(migration, new RegExp(archetype.replace("/", "\\/")));
}
assert.match(migration, /Shared `Dialog` owns focus entry, Escape handling, focus containment, and focus\s+return/);
assert.match(migration, /Passwords are write-only[\s\S]*saved\s+state marker/);
console.log("Mail surfaces satisfy the recorded interface pattern-language contract.");