From bd6558bfeea9ec85c04649720aead32ba10bdfbf Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Sat, 5 Sep 2026 05:44:08 +0100 Subject: [PATCH] test: share Paper guard root inventory --- .../src/tests/views/paperRootInventory.ts | 35 +++++++++++ .../views/paperViewLegacySubstrate.spec.ts | 62 ++++++++++--------- .../tests/paper-eyebrow-token.spec.ts | 32 ++-------- 3 files changed, 74 insertions(+), 55 deletions(-) create mode 100644 frontend/taskdeck-web/src/tests/views/paperRootInventory.ts diff --git a/frontend/taskdeck-web/src/tests/views/paperRootInventory.ts b/frontend/taskdeck-web/src/tests/views/paperRootInventory.ts new file mode 100644 index 000000000..1ff618ba5 --- /dev/null +++ b/frontend/taskdeck-web/src/tests/views/paperRootInventory.ts @@ -0,0 +1,35 @@ +export type PaperViewRoot = { + readonly view: string + readonly selector: string + readonly eyebrow?: string +} + +/** + * The complete Paper-idiom view-root inventory used by the static substrate + * guards. Keeping the eyebrow selector beside its view prevents the two guards + * from silently drifting when a new Paper root is added. + */ +export const PAPER_VIEW_ROOTS = [ + { view: 'ActivityView.vue', selector: '.paper-activity', eyebrow: '.paper-activity__eyebrow' }, + { view: 'AgentRunDetailView.vue', selector: '.paper-run-detail', eyebrow: '.paper-run-detail__eyebrow' }, + { view: 'AgentRunsView.vue', selector: '.paper-agent-runs', eyebrow: '.paper-agent-runs__eyebrow' }, + { view: 'AgentsView.vue', selector: '.paper-agents', eyebrow: '.paper-agents__eyebrow' }, + { view: 'ApiKeySettingsView.vue', selector: '.paper-api-keys', eyebrow: '.paper-api-keys__eyebrow' }, + { view: 'AppearanceSettingsView.vue', selector: '.paper-appearance', eyebrow: '.paper-appearance__eyebrow' }, + { view: 'ArchiveView.vue', selector: '.paper-archive', eyebrow: '.paper-archive__eyebrow' }, + { view: 'AutomationChatView.vue', selector: '.paper-chat' }, + { view: 'AutomationQueueView.vue', selector: '.paper-queue', eyebrow: '.paper-queue__eyebrow' }, + { view: 'BoardAccessView.vue', selector: '.paper-access', eyebrow: '.paper-access__eyebrow' }, + { view: 'BoardsListView.vue', selector: '.paper-boards', eyebrow: '.paper-boards__eyebrow' }, + { view: 'CalendarView.vue', selector: '.paper-calendar', eyebrow: '.paper-calendar__eyebrow' }, + { view: 'DevToolsView.vue', selector: '.paper-devtools' }, + { view: 'ExportImportView.vue', selector: '.paper-portability', eyebrow: '.paper-portability__eyebrow' }, + { view: 'IntegrationsView.vue', selector: '.paper-int', eyebrow: '.paper-int__eyebrow' }, + { view: 'MetricsView.vue', selector: '.paper-metrics', eyebrow: '.paper-metrics__eyebrow' }, + { view: 'NotFoundView.vue', selector: '.paper-not-found', eyebrow: '.paper-not-found__eyebrow' }, + { view: 'NotificationInboxView.vue', selector: '.paper-notifications', eyebrow: '.paper-notifications__eyebrow' }, + { view: 'NotificationPreferencesView.vue', selector: '.paper-prefs', eyebrow: '.paper-prefs__eyebrow' }, + { view: 'OpsConsoleView.vue', selector: '.paper-ops', eyebrow: '.paper-ops__eyebrow' }, + { view: 'ProfileSettingsView.vue', selector: '.paper-profile', eyebrow: '.paper-profile__eyebrow' }, + { view: 'SavedViewsView.vue', selector: '.paper-views', eyebrow: '.paper-views__eyebrow' }, +] as const satisfies ReadonlyArray diff --git a/frontend/taskdeck-web/src/tests/views/paperViewLegacySubstrate.spec.ts b/frontend/taskdeck-web/src/tests/views/paperViewLegacySubstrate.spec.ts index 27ae5683b..d0a25be3e 100644 --- a/frontend/taskdeck-web/src/tests/views/paperViewLegacySubstrate.spec.ts +++ b/frontend/taskdeck-web/src/tests/views/paperViewLegacySubstrate.spec.ts @@ -1,5 +1,7 @@ import { describe, expect, it } from 'vitest' +import { PAPER_VIEW_ROOTS } from './paperRootInventory' + import activitySource from '../../views/ActivityView.vue?raw' import agentRunDetailSource from '../../views/AgentRunDetailView.vue?raw' import agentRunsSource from '../../views/AgentRunsView.vue?raw' @@ -50,34 +52,38 @@ import savedViewsSource from '../../views/SavedViewsView.vue?raw' * "node", and its quarantine list may only shrink. */ -const VIEW_ROOTS: ReadonlyArray<{ view: string; selector: string; source: string }> = [ - // #1780 / PR #1807 — the four high-traffic roots the guard was written for. - { view: 'MetricsView.vue', selector: '.paper-metrics', source: metricsSource }, - { view: 'ActivityView.vue', selector: '.paper-activity', source: activitySource }, - { view: 'CalendarView.vue', selector: '.paper-calendar', source: calendarSource }, - { view: 'BoardsListView.vue', selector: '.paper-boards', source: boardsSource }, - // #1775 / #1813 — the Saved Views restyle. - { view: 'SavedViewsView.vue', selector: '.paper-views', source: savedViewsSource }, - // PR #1808 — the six Settings roots. - { view: 'ApiKeySettingsView.vue', selector: '.paper-api-keys', source: apiKeysSource }, - { view: 'AppearanceSettingsView.vue', selector: '.paper-appearance', source: appearanceSource }, - { view: 'BoardAccessView.vue', selector: '.paper-access', source: boardAccessSource }, - { view: 'ExportImportView.vue', selector: '.paper-portability', source: exportImportSource }, - { view: 'NotificationPreferencesView.vue', selector: '.paper-prefs', source: notificationPrefsSource }, - { view: 'ProfileSettingsView.vue', selector: '.paper-profile', source: profileSource }, - // PR #1810 — the secondary views. - { view: 'AgentRunDetailView.vue', selector: '.paper-run-detail', source: agentRunDetailSource }, - { view: 'AgentRunsView.vue', selector: '.paper-agent-runs', source: agentRunsSource }, - { view: 'AgentsView.vue', selector: '.paper-agents', source: agentsSource }, - { view: 'ArchiveView.vue', selector: '.paper-archive', source: archiveSource }, - { view: 'AutomationChatView.vue', selector: '.paper-chat', source: automationChatSource }, - { view: 'AutomationQueueView.vue', selector: '.paper-queue', source: automationQueueSource }, - { view: 'DevToolsView.vue', selector: '.paper-devtools', source: devToolsSource }, - { view: 'IntegrationsView.vue', selector: '.paper-int', source: integrationsSource }, - { view: 'NotFoundView.vue', selector: '.paper-not-found', source: notFoundSource }, - { view: 'NotificationInboxView.vue', selector: '.paper-notifications', source: notificationInboxSource }, - { view: 'OpsConsoleView.vue', selector: '.paper-ops', source: opsConsoleSource }, -] +type PaperViewName = (typeof PAPER_VIEW_ROOTS)[number]['view'] + +const VIEW_SOURCES: Readonly> = { + 'ActivityView.vue': activitySource, + 'AgentRunDetailView.vue': agentRunDetailSource, + 'AgentRunsView.vue': agentRunsSource, + 'AgentsView.vue': agentsSource, + 'ApiKeySettingsView.vue': apiKeysSource, + 'AppearanceSettingsView.vue': appearanceSource, + 'ArchiveView.vue': archiveSource, + 'AutomationChatView.vue': automationChatSource, + 'AutomationQueueView.vue': automationQueueSource, + 'BoardAccessView.vue': boardAccessSource, + 'BoardsListView.vue': boardsSource, + 'CalendarView.vue': calendarSource, + 'DevToolsView.vue': devToolsSource, + 'ExportImportView.vue': exportImportSource, + 'IntegrationsView.vue': integrationsSource, + 'MetricsView.vue': metricsSource, + 'NotFoundView.vue': notFoundSource, + 'NotificationInboxView.vue': notificationInboxSource, + 'NotificationPreferencesView.vue': notificationPrefsSource, + 'OpsConsoleView.vue': opsConsoleSource, + 'ProfileSettingsView.vue': profileSource, + 'SavedViewsView.vue': savedViewsSource, +} + +const VIEW_ROOTS = PAPER_VIEW_ROOTS.map(({ view, selector }) => ({ + view, + selector, + source: VIEW_SOURCES[view], +})) /** * A root satisfies the invariant by painting ANY Paper substrate token, not diff --git a/frontend/taskdeck-web/tests/paper-eyebrow-token.spec.ts b/frontend/taskdeck-web/tests/paper-eyebrow-token.spec.ts index d6a266b69..e2d2e2f33 100644 --- a/frontend/taskdeck-web/tests/paper-eyebrow-token.spec.ts +++ b/frontend/taskdeck-web/tests/paper-eyebrow-token.spec.ts @@ -4,6 +4,8 @@ import { fileURLToPath } from 'node:url' import { describe, expect, it } from 'vitest' +import { PAPER_VIEW_ROOTS } from '../src/tests/views/paperRootInventory' + /** * Canonical eyebrow token across the Paper view roots (#1842, ADR-0053). * @@ -47,33 +49,9 @@ const CANONICAL_EYEBROW_TOKEN = '--mute' /** Its literal fallback, for Legacy ("Paper off") where Paper vars are absent. */ const CANONICAL_EYEBROW_FALLBACK = '#635c4e' -/** - * Every Paper-idiom view root that has a page-header eyebrow. Mirrors the roots - * in `src/tests/views/paperViewLegacySubstrate.spec.ts`, minus - * `AutomationChatView` and `DevToolsView`, which have no eyebrow. - */ -const EYEBROW_ROOTS: ReadonlyArray<[view: string, rule: string]> = [ - ['ActivityView.vue', '.paper-activity__eyebrow'], - ['AgentRunDetailView.vue', '.paper-run-detail__eyebrow'], - ['AgentRunsView.vue', '.paper-agent-runs__eyebrow'], - ['AgentsView.vue', '.paper-agents__eyebrow'], - ['ApiKeySettingsView.vue', '.paper-api-keys__eyebrow'], - ['AppearanceSettingsView.vue', '.paper-appearance__eyebrow'], - ['ArchiveView.vue', '.paper-archive__eyebrow'], - ['AutomationQueueView.vue', '.paper-queue__eyebrow'], - ['BoardAccessView.vue', '.paper-access__eyebrow'], - ['BoardsListView.vue', '.paper-boards__eyebrow'], - ['CalendarView.vue', '.paper-calendar__eyebrow'], - ['ExportImportView.vue', '.paper-portability__eyebrow'], - ['IntegrationsView.vue', '.paper-int__eyebrow'], - ['MetricsView.vue', '.paper-metrics__eyebrow'], - ['NotFoundView.vue', '.paper-not-found__eyebrow'], - ['NotificationInboxView.vue', '.paper-notifications__eyebrow'], - ['NotificationPreferencesView.vue', '.paper-prefs__eyebrow'], - ['OpsConsoleView.vue', '.paper-ops__eyebrow'], - ['ProfileSettingsView.vue', '.paper-profile__eyebrow'], - ['SavedViewsView.vue', '.paper-views__eyebrow'], -] +const EYEBROW_ROOTS = PAPER_VIEW_ROOTS.flatMap(({ view, eyebrow }) => + eyebrow === undefined ? [] : ([[view, eyebrow]] as const), +) /** The four core-loop surfaces, and the eyebrow markup each was measured at. */ const CORE_LOOP_SURFACES: ReadonlyArray<[surface: string, file: string]> = [