/** * Extended Route Rendering Tests — Batch 2 (40 routes) * * Tests additional SPA routes beyond the critical set. * Same verification pattern: navigate, check content, check for NG errors. */ import { test, expect } from '../fixtures/auth.fixture'; import { navigateAndWait, assertPageHasContent } from '../helpers/nav.helper'; function setupErrorCollector(page: import('@playwright/test').Page) { const errors: string[] = []; page.on('console', (msg) => { const text = msg.text(); if (msg.type() === 'error' && /NG0\d{3,4}/.test(text)) { errors.push(text); } }); return errors; } const EXTENDED_ROUTES: { path: string; name: string }[] = [ // Legacy routes { path: '/environments', name: 'Environments' }, { path: '/home', name: 'Home Dashboard (legacy)' }, { path: '/dashboard/sources', name: 'Sources Dashboard' }, { path: '/console/status', name: 'Console Status' }, { path: '/console/admin', name: 'Console Admin' }, { path: '/console/configuration', name: 'Configuration' }, // JobEngine (legacy paths) { path: '/jobengine', name: 'JobEngine (legacy)' }, { path: '/jobengine/jobs', name: 'JobEngine Jobs' }, { path: '/jobengine/quotas', name: 'JobEngine Quotas' }, { path: '/release-jobengine', name: 'Release JobEngine' }, // Policy Studio { path: '/policy-studio/packs', name: 'Policy Studio Packs' }, // Module-specific routes { path: '/concelier/trivy-db-settings', name: 'Trivy DB Settings' }, { path: '/risk', name: 'Risk Dashboard' }, { path: '/graph', name: 'Graph Explorer' }, { path: '/lineage', name: 'Lineage' }, { path: '/reachability', name: 'Reachability Center' }, { path: '/timeline', name: 'Timeline' }, { path: '/evidence-thread', name: 'Evidence Thread' }, // Vulnerability routes { path: '/vulnerabilities', name: 'Vulnerability Explorer' }, { path: '/vulnerabilities/triage', name: 'Vulnerability Triage' }, // Triage routes { path: '/triage/inbox', name: 'Triage Inbox' }, { path: '/triage/artifacts', name: 'Triage Artifacts' }, { path: '/triage/quiet-lane', name: 'Quiet Lane' }, { path: '/triage/ai-recommendations', name: 'AI Recommendations' }, // Notify & Admin { path: '/notify', name: 'Notify Panel' }, { path: '/admin/notifications', name: 'Admin Notifications' }, // Ops routes { path: '/ops/feeds', name: 'Feed Mirror' }, { path: '/ops/signals', name: 'Signals Dashboard' }, { path: '/ops/packs', name: 'Pack Registry Browser' }, { path: '/admin/policy/governance', name: 'Policy Governance Admin' }, { path: '/admin/policy/simulation', name: 'Policy Simulation Admin' }, { path: '/scheduler', name: 'Scheduler' }, { path: '/exceptions', name: 'Exceptions' }, // More admin routes { path: '/admin/registries', name: 'Registry Admin' }, { path: '/admin/issuers', name: 'Issuer Trust' }, { path: '/ops/scanner', name: 'Scanner Ops' }, { path: '/ops/offline-kit', name: 'Offline Kit' }, { path: '/ops/aoc', name: 'AOC Compliance' }, { path: '/admin/audit', name: 'Audit Log' }, // Welcome page (no auth) { path: '/welcome', name: 'Welcome Page' }, ]; test.describe('Extended Route Rendering (Batch 2)', () => { for (const route of EXTENDED_ROUTES) { test(`renders ${route.name} (${route.path})`, async ({ authenticatedPage: page }) => { const ngErrors = setupErrorCollector(page); await navigateAndWait(page, route.path, { timeout: 30_000 }); await page.waitForTimeout(2000); await assertPageHasContent(page); expect( ngErrors, `Angular errors on ${route.path}: ${ngErrors.join('\n')}` ).toHaveLength(0); }); } }); test.describe('Extended Route — Deep Paths', () => { const DEEP_PATHS: { path: string; name: string }[] = [ { path: '/ops/quotas', name: 'Quota Dashboard' }, { path: '/ops/jobengine/dead-letter', name: 'Dead Letter Queue' }, { path: '/ops/jobengine/slo', name: 'SLO Burn Rate' }, { path: '/ops/health', name: 'Platform Health' }, { path: '/ops/doctor', name: 'Doctor Diagnostics' }, { path: '/ops/agents', name: 'Agent Fleet' }, { path: '/analyze/unknowns', name: 'Unknowns Tracking' }, { path: '/analyze/patch-map', name: 'Patch Map Explorer' }, { path: '/ops/binary-index', name: 'Binary Index Ops' }, { path: '/settings/determinization-config', name: 'Determinization Config' }, { path: '/sbom-sources', name: 'SBOM Sources' }, { path: '/sbom/diff', name: 'SBOM Diff' }, { path: '/deploy/diff', name: 'Deploy Diff' }, { path: '/vex/timeline', name: 'VEX Timeline' }, { path: '/workspace/dev', name: 'Developer Workspace' }, { path: '/workspace/audit', name: 'Auditor Workspace' }, { path: '/ai/autofix', name: 'AI Autofix' }, { path: '/ai/chat', name: 'AI Chat' }, { path: '/ai/chips', name: 'AI Chips Showcase' }, { path: '/ai-runs', name: 'AI Runs' }, { path: '/change-trace', name: 'Change Trace' }, { path: '/aoc/verify', name: 'AOC Verification' }, { path: '/audit/reasons', name: 'Audit Reasons' }, { path: '/triage/audit-bundles', name: 'Triage Audit Bundles' }, ]; for (const route of DEEP_PATHS) { test(`renders ${route.name} (${route.path})`, async ({ authenticatedPage: page }) => { const ngErrors = setupErrorCollector(page); await navigateAndWait(page, route.path, { timeout: 30_000 }); await page.waitForTimeout(2000); await assertPageHasContent(page); expect( ngErrors, `Angular errors on ${route.path}: ${ngErrors.join('\n')}` ).toHaveLength(0); }); } }); test.describe('Setup Wizard Route (no auth required)', () => { test('renders setup page', async ({ page }) => { // Setup wizard does NOT need auth — test with bare page await page.goto('/setup', { waitUntil: 'networkidle', timeout: 30_000 }); await page.waitForTimeout(2000); const bodyText = await page.locator('body').innerText(); expect(bodyText.trim().length).toBeGreaterThan(10); }); });