Files
pleno-vue/scripts/test-graph/component-node-catalog.mjs
T
Jeppe B 12a2d73b32 Replace monolithic E2E matrices with dependency-aware component CI
Generate small reusable component partitions with explicit provider dependencies, GitHub-hosted concurrency, fail-closed evidence, and exact mobile store gates. Include the literal-i18n fix from #206 so validation covers the exact post-merge tree.
2026-07-20 23:52:15 +02:00

534 lines
19 KiB
JavaScript

export const GRAPH_VERSION = 2;
export const browserLanes = [
"chromium-mobile",
"chromium-desktop",
"chromium-tablet",
"webkit-mobile",
"webkit-desktop",
"webkit-tablet",
"firefox-mobile",
"firefox-desktop",
"firefox-tablet",
];
const contractLanes = ["contract"];
const uiLanes = [...contractLanes, ...browserLanes];
const chromiumCtLanes = ["ct-chromium-desktop", "ct-chromium-mobile"];
const defaultLayers = {
runtime: 10,
store: 10,
service: 10,
middleware: 10,
composable: 20,
feature: 30,
component: 50,
router: 60,
view: 60,
};
const node = (id, label, options) => ({
id,
label,
kind: options.kind,
layer: options.layer ?? defaultLayers[options.kind],
priority: options.priority ?? 50,
sourcePatterns: options.sourcePatterns,
testPatterns: options.testPatterns || [],
lanes: options.lanes ?? contractLanes,
partitions: options.partitions || {},
dependsOn: options.dependsOn || [],
runtimeDependencies: options.runtimeDependencies || [],
importPolicy: options.importPolicy || "semantic-owner",
});
const sourceFileNode = (kind, sourceDirectory, fileName, options = {}) => {
const baseName = fileName.replace(/\.(?:js|ts)$/u, "");
const slug = baseName
.replace(/([a-z0-9])([A-Z])/gu, "$1-$2")
.replace(/[^a-zA-Z0-9]+/gu, "-")
.toLowerCase();
const testPatterns = options.testPatterns || [];
return node(`${kind}-${slug}`, options.label || baseName, {
kind,
priority: 150,
sourcePatterns: [`src/${sourceDirectory}/${fileName}`],
testPatterns,
lanes: testPatterns.length > 0 ? contractLanes : [],
runtimeDependencies: options.runtimeDependencies || [],
});
};
const serviceTestPatterns = {
accountDeletion: ["tests/unit/account-deletion.spec.js"],
apiHealth: ["tests/unit/api-health.spec.js"],
attachmentDownloadLinks: ["tests/unit/attachment-download-links.spec.js"],
buefyDatepicker: ["tests/unit/buefy-datepicker.spec.js"],
dateOnly: ["tests/unit/date-only.spec.js"],
economicTransferQueue: ["tests/unit/economic-transfer-queue.spec.js"],
edgeGateways: ["tests/unit/edge-gateway-service.spec.js"],
frontendMaintenance: ["tests/unit/frontend-maintenance.spec.js"],
installAxiosRequestQueue: ["tests/unit/axios-request-queue.spec.js", "tests/unit/request-queue-concurrency.spec.js"],
invoiceMonthSplitWarning: ["tests/unit/invoice-month-split-warning.spec.js"],
orderDateEvents: ["tests/unit/order-date-events.spec.js"],
relativeDateShortcuts: ["tests/unit/relative-date-shortcuts.spec.js"],
releaseBootstrap: ["tests/unit/release-bootstrap.spec.js"],
releaseChannelAvailability: ["tests/unit/release-channel-availability.spec.js"],
releaseTimeline: ["tests/unit/release-timeline.spec.js"],
releaseUpdate: ["tests/unit/release-update.spec.js"],
requestQueue: ["tests/unit/request-queue-progress.spec.js"],
sessionPayload: ["tests/unit/session-payload.spec.js"],
sessionStorage: ["tests/unit/session-storage.spec.js"],
shellyRelayOptions: ["tests/unit/shelly-relay-options.spec.js"],
TableExcelExportService: ["tests/unit/table-excel-export-service.spec.js"],
tableHeaderPreferences: ["tests/unit/table-header-preferences.spec.js"],
};
const serviceFiles = [
"AutoTableExportService.js",
"CSVService.js",
"PasskeyAuthService.js",
"TableExcelExportService.js",
"TwoFactorAuthService.js",
"accountDeletion.js",
"apiHealth.js",
"attachmentDownloadLinks.js",
"attachmentPreview.js",
"buefyDatepicker.js",
"dateOnly.js",
"departmentCustomerPricing.js",
"departmentDailyReportComplaintCategories.js",
"departmentDailyReportComplaintCustomers.js",
"departmentVisibility.js",
"economicCustomerIdentifiers.js",
"economicTransferQueue.js",
"edgeGateways.js",
"errorReportLauncher.js",
"errorReports.js",
"frontendMaintenance.js",
"installAxiosRequestQueue.js",
"invoiceMonthSplitWarning.js",
"limitedBackoffice.js",
"localeFormatting.js",
"moduleUsage.js",
"orderDateEvents.js",
"orderReceiptPrint.js",
"relativeDateShortcuts.js",
"releaseBootstrap.js",
"releaseChannelAvailability.js",
"releaseHeaders.js",
"releaseTimeline.js",
"releaseTrust.js",
"releaseUpdate.js",
"requestErrorTrace.js",
"requestQueue.js",
"selfServeDynamicImage.js",
"sessionPayload.js",
"sessionStorage.js",
"shellyRelayOptions.js",
"subuserPasswordPolicy.js",
"superuserCoolify.js",
"superuserCron.js",
"superuserDepartmentOverview.js",
"superuserReleases.js",
"superuserSecurity.js",
"tableHeaderPreferences.js",
];
const composableTestPatterns = {
departmentOutsideGateCapabilities: ["tests/unit/department-outside-gate-capabilities.spec.js"],
departmentSelfServeEnabled: ["tests/unit/department-self-serve-enabled.spec.js"],
useDraftTransactionCustomer: ["tests/unit/use-draft-transaction-customer.spec.js"],
useOrderMetadataAutosave: ["tests/unit/use-order-metadata-autosave.spec.js"],
useSelfServeLogic: ["tests/unit/use-self-serve-logic.spec.js", "tests/unit/use-self-serve-logic-production.spec.js"],
useWashDepartments: ["tests/unit/use-wash-departments.spec.js"],
useWashFlowState: ["tests/unit/use-wash-flow-state.spec.js", "tests/unit/use-wash-flow-state-production.spec.js"],
useWashProgress: ["tests/unit/use-wash-progress.spec.js"],
useWashSessionActions: [
"tests/unit/use-wash-session-actions.spec.js",
"tests/unit/use-wash-session-actions-production.spec.js",
],
};
const composableFiles = [
"departmentOutsideGateCapabilities.js",
"departmentSelfServeEnabled.js",
"useAppToast.js",
"useDraftTransactionCustomer.js",
"useEconomicQueueJob.js",
"useHoldToTrigger.ts",
"useOrderMetadataAutosave.js",
"usePosAddCustomerForm.js",
"useSelfServeLogic.js",
"useStoredSessionGuestRedirect.js",
"useStripeReaderAvailability.js",
"useWashDepartments.js",
"useWashFlowState.js",
"useWashProgress.js",
"useWashSessionActions.js",
];
/**
* Semantic test nodes, not a raw directory inventory. Import inference augments
* these explicit boundaries, while runtimeDependencies records relationships
* hidden behind router registration, global Vue state, or runtime injection.
*/
export const componentNodeCatalog = [
node("runtime-assets", "Static runtime assets", {
kind: "runtime",
layer: 10,
priority: 100,
sourcePatterns: ["src/assets/**", "src/themes/**", "public/**"],
lanes: [],
}),
node("runtime-foundation", "Runtime foundation", {
kind: "runtime",
layer: 60,
priority: 10,
sourcePatterns: ["src/App.vue", "src/ThemeConfig.vue", "src/main.js", "src/releaseBootstrap.js", "index.html"],
testPatterns: ["tests/unit/app-*.spec.js", "tests/e2e/release-bootstrap.spec.js"],
lanes: contractLanes,
runtimeDependencies: ["component-release"],
}),
node("runtime-config", "Runtime configuration primitives", {
kind: "runtime",
layer: 10,
priority: 100,
sourcePatterns: ["src/config.js", "src/constants/**"],
lanes: [],
}),
node("runtime-router", "Router and route contracts", {
kind: "router",
layer: 60,
priority: 100,
sourcePatterns: ["src/router.js"],
testPatterns: ["tests/unit/router-*.spec.js", "tests/e2e/navigation.smoke.spec.js"],
lanes: uiLanes,
runtimeDependencies: ["runtime-middleware"],
}),
node("runtime-middleware", "Route middleware", {
kind: "middleware",
sourcePatterns: ["src/middleware/**"],
testPatterns: ["tests/unit/requires-permission.spec.js", "tests/unit/restricted-page-wrapper.spec.js"],
runtimeDependencies: ["runtime-store"],
}),
node("runtime-store", "Shared application store", {
kind: "store",
sourcePatterns: ["src/store/**"],
lanes: [],
}),
node("runtime-i18n", "Runtime internationalization", {
kind: "runtime",
sourcePatterns: ["src/i18n/**", "src/components/i18n/**"],
testPatterns: ["tests/unit/i18n-*.spec.js", "tests/e2e/i18n*.spec.*"],
lanes: uiLanes,
}),
...serviceFiles.map((fileName) =>
sourceFileNode("service", "services", fileName, {
testPatterns: serviceTestPatterns[fileName.replace(/\.js$/u, "")] || [],
})
),
...composableFiles.map((fileName) =>
sourceFileNode("composable", "composables", fileName, {
testPatterns: composableTestPatterns[fileName.replace(/\.(?:js|ts)$/u, "")] || [],
})
),
node("feature-customer", "Customer rules and pricing features", {
kind: "feature",
priority: 100,
sourcePatterns: ["src/features/customer/**"],
testPatterns: ["tests/unit/customer-*.spec.js", "tests/e2e/pos-customer-rules.spec.js"],
lanes: contractLanes,
runtimeDependencies: ["runtime-store"],
}),
node("feature-edge-gateways", "Edge gateway features", {
kind: "feature",
priority: 100,
sourcePatterns: ["src/features/edgeGateways/**"],
testPatterns: ["tests/unit/edge-gateway-*.spec.js", "tests/e2e/edge-gateways*.spec.js"],
lanes: contractLanes,
runtimeDependencies: ["runtime-store"],
}),
node("feature-shared", "Shared feature modules", {
kind: "feature",
priority: 5,
sourcePatterns: ["src/features/**"],
lanes: [],
runtimeDependencies: ["runtime-store"],
}),
node("component-action-settings-wheel", "Action settings wheel", {
kind: "component",
layer: 40,
priority: 200,
sourcePatterns: [
"src/components/displays/buttons/ActionSettingsWheelButton.vue",
"src/components/displays/buttons/ActionSettingsWheelItem.vue",
"src/components/displays/buttons/ActionSettingsWheelItemLabel.vue",
"src/components/displays/buttons/ActionSettingsWheelSelectItem.vue",
"src/components/displays/buttons/ActionSettingsWheelToggleItem.vue",
],
testPatterns: ["tests/unit/action-settings-wheel-button.spec.js", "tests/unit/i18n-action-settings-wheel.spec.js"],
lanes: contractLanes,
runtimeDependencies: ["runtime-i18n", "runtime-store"],
importPolicy: "exact-standalone",
}),
node("component-date-period-selector", "Date period selector", {
kind: "component",
layer: 40,
priority: 200,
sourcePatterns: ["src/components/displays/buttons/DatePeriodSelector.vue"],
testPatterns: ["tests/unit/date-period-selector.spec.js", "tests/ct/date-period-selector.ct.spec.ts"],
lanes: [...contractLanes, ...chromiumCtLanes],
runtimeDependencies: ["runtime-i18n", "runtime-store"],
importPolicy: "exact-standalone",
}),
node("component-pagination", "Pagination and table primitives", {
kind: "component",
layer: 40,
priority: 100,
sourcePatterns: ["src/components/displays/pagination/**", "src/components/pagination/**"],
testPatterns: ["tests/unit/*pagination*.spec.js", "tests/unit/table-*.spec.js"],
lanes: contractLanes,
runtimeDependencies: ["runtime-store", "component-date-period-selector"],
}),
node("component-session", "Session and identity components", {
kind: "component",
layer: 40,
priority: 100,
sourcePatterns: ["src/components/session/**"],
testPatterns: ["tests/unit/session-*.spec.js", "tests/e2e/session-*.spec.ts"],
lanes: uiLanes,
runtimeDependencies: ["runtime-store"],
}),
node("component-request", "Request and connectivity components", {
kind: "component",
layer: 40,
priority: 100,
sourcePatterns: ["src/components/request/**"],
testPatterns: ["tests/unit/request-*.spec.js", "tests/unit/connectivity-*.spec.js"],
lanes: contractLanes,
runtimeDependencies: ["runtime-store"],
}),
node("component-navigation", "Navigation and viewport components", {
kind: "component",
priority: 100,
sourcePatterns: [
"src/components/menus/**",
"src/components/models/navigation/**",
"src/components/page/**",
"src/components/viewport/**",
],
testPatterns: ["tests/unit/navigation-*.spec.js", "tests/unit/header-*.spec.js"],
lanes: contractLanes,
runtimeDependencies: ["runtime-router", "component-session"],
}),
node("component-pos", "Point of sale components", {
kind: "component",
priority: 120,
sourcePatterns: [
"src/components/shop/**",
"src/components/models/pos/**",
"src/components/displays/department/pos/**",
],
testPatterns: [
"tests/unit/pos-*.spec.js",
"tests/unit/orders-*.spec.js",
"tests/e2e/pos*.spec.*",
"tests/e2e/admin-pos-*.spec.ts",
],
lanes: uiLanes,
runtimeDependencies: ["component-action-settings-wheel", "component-pagination", "feature-customer"],
}),
node("component-release", "Release management components", {
kind: "component",
priority: 100,
sourcePatterns: ["src/components/release/**"],
testPatterns: ["tests/unit/release-*.spec.js", "tests/e2e/release-*.spec.*"],
lanes: uiLanes,
runtimeDependencies: ["component-action-settings-wheel", "runtime-store"],
}),
node("component-self-serve", "Self-serve components", {
kind: "component",
priority: 110,
sourcePatterns: ["src/components/displays/selfServe/**"],
testPatterns: ["tests/unit/self-serve-*.spec.js", "tests/e2e/self-serve-*.spec.js"],
lanes: uiLanes,
runtimeDependencies: ["runtime-store", "component-request"],
}),
node("component-edge-gateway", "Edge gateway components", {
kind: "component",
priority: 110,
sourcePatterns: ["src/components/displays/edgeGateway/**"],
testPatterns: ["tests/e2e/edge-gateways*.spec.js"],
lanes: browserLanes,
runtimeDependencies: ["feature-edge-gateways", "component-request"],
}),
node("component-department", "Department components", {
kind: "component",
priority: 80,
sourcePatterns: ["src/components/displays/department/**", "src/components/forms/department/**"],
testPatterns: ["tests/unit/department-*.spec.js"],
lanes: contractLanes,
runtimeDependencies: [
"component-action-settings-wheel",
"component-date-period-selector",
"component-pagination",
"runtime-store",
],
}),
node("component-superuser", "Superuser components", {
kind: "component",
priority: 80,
sourcePatterns: ["src/components/displays/superuser/**", "src/components/forms/superUser/**"],
testPatterns: ["tests/unit/superuser-*.spec.js"],
lanes: contractLanes,
runtimeDependencies: ["component-action-settings-wheel", "component-pagination", "runtime-store"],
}),
node("component-user", "User and subuser components", {
kind: "component",
priority: 80,
sourcePatterns: ["src/components/displays/user/**", "src/components/forms/user/**"],
testPatterns: ["tests/unit/user-*.spec.js", "tests/unit/subuser-*.spec.js"],
lanes: contractLanes,
runtimeDependencies: ["component-action-settings-wheel", "component-pagination", "runtime-store"],
}),
node("component-forms", "Shared forms and selectors", {
kind: "component",
layer: 40,
priority: 40,
sourcePatterns: ["src/components/forms/**", "src/components/displays/selectors/**", "src/components/search/**"],
lanes: [],
runtimeDependencies: ["runtime-store"],
}),
node("component-shared", "Other shared components", {
kind: "component",
layer: 40,
priority: 1,
sourcePatterns: ["src/components/**"],
testPatterns: ["tests/unit/**"],
lanes: contractLanes,
runtimeDependencies: ["runtime-store"],
}),
node("view-auth", "Authentication views", {
kind: "view",
priority: 100,
sourcePatterns: ["src/views/auth/**", "src/views/pages/auth/**"],
testPatterns: ["tests/e2e/auth*.spec.*", "tests/e2e/passkeyAuth.spec.ts", "tests/e2e/twoFactorAuth.spec.ts"],
lanes: browserLanes,
runtimeDependencies: ["component-session", "component-forms", "runtime-router"],
}),
node("view-backoffice", "Limited backoffice views", {
kind: "view",
priority: 100,
sourcePatterns: ["src/views/backoffice/**"],
testPatterns: ["tests/e2e/limited-backoffice.spec.ts"],
lanes: browserLanes,
runtimeDependencies: ["component-department", "component-navigation", "runtime-router"],
}),
node("view-admin", "Department administrator views", {
kind: "view",
priority: 110,
sourcePatterns: ["src/views/dashboards/departmentDashboard/**"],
testPatterns: ["tests/e2e/admin*.spec.*", "tests/e2e/pos*.spec.*"],
lanes: browserLanes,
runtimeDependencies: [
"component-date-period-selector",
"component-department",
"component-pos",
"component-navigation",
"runtime-router",
],
}),
node("view-superuser", "Superuser views", {
kind: "view",
priority: 100,
sourcePatterns: ["src/views/dashboards/superUserDashboard/**"],
testPatterns: ["tests/e2e/superuser-*.spec.*", "tests/e2e/invoice*.spec.*", "tests/e2e/invoicing-*.spec.*"],
lanes: browserLanes,
runtimeDependencies: [
"component-action-settings-wheel",
"component-date-period-selector",
"component-superuser",
"component-navigation",
"runtime-router",
],
}),
node("view-user", "User and subuser views", {
kind: "view",
priority: 100,
sourcePatterns: ["src/views/dashboards/userDashboard/**"],
testPatterns: ["tests/e2e/user*.spec.*", "tests/e2e/subuser*.spec.*"],
lanes: browserLanes,
runtimeDependencies: [
"component-action-settings-wheel",
"component-user",
"component-navigation",
"runtime-router",
],
}),
node("view-guest", "Guest, booking, and PWA views", {
kind: "view",
priority: 100,
sourcePatterns: ["src/views/guest/**"],
testPatterns: ["tests/e2e/guest-*.spec.*", "tests/e2e/booking-*.spec.*", "tests/e2e/qr-*.spec.*"],
lanes: browserLanes,
runtimeDependencies: ["component-forms", "component-navigation", "runtime-router"],
}),
node("view-shared", "Other views and pages", {
kind: "view",
priority: 1,
sourcePatterns: ["src/views/**"],
testPatterns: ["tests/e2e/**"],
lanes: browserLanes,
runtimeDependencies: ["component-date-period-selector", "component-shared", "runtime-router"],
}),
];
export const compositionImportRules = [
{
importer: "src/router.js",
importedPatterns: ["src/views/**"],
mode: "invert",
reason: "Router lazy registration composes views; views depend on the router contract.",
},
{
importer: "src/main.js",
importedPatterns: ["src/App.vue", "src/components/**", "src/views/**"],
mode: "ignore",
reason: "Application bootstrap composition is covered by build and browser lanes.",
},
{
importer: "src/App.vue",
importedPatterns: ["src/components/**", "src/views/**"],
mode: "ignore",
reason: "The root shell composes children rather than providing their reusable contract.",
},
];
export const failClosedChangePatterns = [
/^src\//u,
/^public\//u,
/^index\.html$/u,
/^package(?:-lock)?\.json$/u,
/^vite\.config\.js$/u,
/^playwright(?:\..+)?\.config\.(?:js|ts)$/u,
/^playwright\.global-(?:setup|teardown)\.mjs$/u,
/^scripts\/test-graph\//u,
/^scripts\/run-playwright-/u,
/^tests\//u,
/^\.github\/workflows\//u,
];
export const forceFullChangePatterns = [
/^tests\/(?:ct|e2e)\/(?:fixtures|support)\//u,
/^tests\/unit\/(?:setup|support)(?:\.|\/)/u,
/^(?:vite|vitest|eslint)\.config\.(?:js|ts|mjs)$/u,
/^playwright(?:\..+)?\.config\.(?:js|ts)$/u,
/^playwright\.global-(?:setup|teardown)\.mjs$/u,
/^package(?:-lock)?\.json$/u,
/^(?:\.dockerignore|\.env|\.prettierrc\.json|Dockerfile(?:\..+)?|Gemfile(?:\.lock)?|build\.gradle|capacitor\.config\.ts|env\.d\.ts|gradle\.properties|gradlew(?:\.bat)?|jsconfig\.json|manifest-checksum\.txt|nginx\..+\.conf|openapi\.yaml|settings\.gradle|tsconfig\.json|twa-manifest\.json|wdio\.conf\.js)$/u,
/^scripts\//u,
/^(?:android|ios|app|fastlane|gradle)\//u,
/^\.github\/workflows\//u,
];