// ----------------------------------------------------------------------------- // analytics-sbom-lake.spec.ts // Sprint: SPRINT_20260120_031_FE_sbom_analytics_console // Task: TASK-031-005 - E2E route load + filter behavior // ----------------------------------------------------------------------------- import { expect, test, type Page } from '@playwright/test'; import { policyAuthorSession } from '../../src/app/testing'; const analyticsSession = { ...policyAuthorSession, scopes: [...policyAuthorSession.scopes, 'analytics.read'], }; const mockConfig = { authority: { issuer: 'https://127.0.0.1:4400/authority', clientId: 'stella-ops-ui', authorizeEndpoint: 'https://127.0.0.1:4400/authority/connect/authorize', tokenEndpoint: 'https://127.0.0.1:4400/authority/connect/token', logoutEndpoint: 'https://127.0.0.1:4400/authority/connect/logout', redirectUri: 'http://127.0.0.1:4400/auth/callback', postLogoutRedirectUri: 'http://127.0.0.1:4400/', scope: 'openid profile email ui.read', audience: 'https://scanner.local', dpopAlgorithms: ['ES256'], refreshLeewaySeconds: 60, }, apiBaseUrls: { authority: '/authority', scanner: 'https://scanner.local', policy: 'https://scanner.local', concelier: 'https://concelier.local', attestor: 'https://attestor.local', }, quickstartMode: true, setup: 'complete', }; const oidcConfig = { issuer: mockConfig.authority.issuer, authorization_endpoint: mockConfig.authority.authorizeEndpoint, token_endpoint: mockConfig.authority.tokenEndpoint, jwks_uri: 'https://127.0.0.1:4400/authority/.well-known/jwks.json', response_types_supported: ['code'], subject_types_supported: ['public'], id_token_signing_alg_values_supported: ['RS256'], }; const createResponse = (items: T[]) => ({ tenantId: 'tenant-analytics', actorId: 'actor-analytics', dataAsOf: '2026-01-20T00:00:00Z', cached: false, cacheTtlSeconds: 300, items, count: items.length, }); const mockSuppliers = [ { supplier: 'Acme Corp', componentCount: 120, artifactCount: 8, teamCount: 2, criticalVulnCount: 4, highVulnCount: 10, environments: ['Prod'], }, ]; const mockLicenses = [ { licenseConcluded: 'Apache-2.0', licenseCategory: 'permissive', componentCount: 240, artifactCount: 12, ecosystems: ['npm'], }, ]; const mockVulnerabilities = [ { vulnId: 'CVE-2026-1234', severity: 'high', cvssScore: 8.8, epssScore: 0.12, kevListed: true, fixAvailable: true, rawComponentCount: 3, rawArtifactCount: 2, effectiveComponentCount: 2, effectiveArtifactCount: 1, vexMitigated: 1, }, ]; const mockBacklog = [ { service: 'api-gateway', environment: 'Prod', component: 'openssl', version: '1.1.1k', vulnId: 'CVE-2026-1234', severity: 'high', fixedVersion: '1.1.1l', }, ]; const mockAttestation = [ { environment: 'Prod', team: 'platform', totalArtifacts: 12, withProvenance: 10, provenancePct: 83.3, slsaLevel2Plus: 8, slsa2Pct: 66.7, missingProvenance: 2, }, ]; const mockVulnTrends = [ { snapshotDate: '2026-01-01', environment: 'Prod', totalVulns: 10, fixableVulns: 5, vexMitigated: 2, netExposure: 8, kevVulns: 1, }, ]; const mockComponentTrends = [ { snapshotDate: '2026-01-01', environment: 'Prod', totalComponents: 200, uniqueSuppliers: 12, }, ]; const setupAnalyticsMocks = async (page: Page) => { await page.route('**/api/analytics/suppliers**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(createResponse(mockSuppliers)), }) ); await page.route('**/api/analytics/licenses**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(createResponse(mockLicenses)), }) ); await page.route('**/api/analytics/vulnerabilities**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(createResponse(mockVulnerabilities)), }) ); await page.route('**/api/analytics/backlog**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(createResponse(mockBacklog)), }) ); await page.route('**/api/analytics/attestation-coverage**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(createResponse(mockAttestation)), }) ); await page.route('**/api/analytics/trends/vulnerabilities**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(createResponse(mockVulnTrends)), }) ); await page.route('**/api/analytics/trends/components**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(createResponse(mockComponentTrends)), }) ); }; const setupSession = async (page: Page, session: typeof policyAuthorSession) => { await page.addInitScript((sessionValue) => { try { window.sessionStorage.clear(); } catch { // Ignore storage errors in restricted contexts. } (window as any).__stellaopsTestSession = sessionValue; }, session); await page.route('**/config.json', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockConfig), }) ); await page.route('**/platform/envsettings.json', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockConfig), }) ); await page.route('**/authority/**', (route) => { const url = route.request().url(); if (url.includes('/.well-known/openid-configuration')) { return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(oidcConfig), }); } if (url.includes('/.well-known/jwks.json')) { return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ keys: [] }), }); } return route.abort(); }); }; test.describe.skip('SBOM Lake Analytics Console' /* TODO: SBOM Lake filter selectors need verification against actual component */, () => { test.beforeEach(async ({ page }) => { await setupSession(page, analyticsSession); await setupAnalyticsMocks(page); }); test('loads analytics panels and updates filters', async ({ page }) => { await page.goto('/analytics/sbom-lake?env=Prod&severity=high&days=90'); await expect( page.getByRole('heading', { level: 1, name: 'SBOM Lake' }) ).toBeVisible({ timeout: 10000 }); await expect(page.locator('#envFilter')).toHaveValue('Prod'); await expect(page.locator('#severityFilter')).toHaveValue('high'); await expect(page.locator('#daysFilter')).toHaveValue('90'); await expect(page.getByText('Acme Corp')).toBeVisible(); await expect(page.locator('.table-grid .data-table').first()).toContainText( 'api-gateway' ); await page.locator('#envFilter').selectOption('QA'); await expect(page).toHaveURL(/env=QA/); }); }); test.describe('SBOM Lake Analytics Guard', () => { test.beforeEach(async ({ page }) => { await setupSession(page, policyAuthorSession); await setupAnalyticsMocks(page); }); test('falls back to mission board when analytics route is unavailable', async ({ page }) => { await page.goto('/analytics/sbom-lake'); await expect(page.locator('app-root')).toHaveCount(1); await expect(page.locator('body')).toContainText(/Stella Ops|Mission|Dashboard/i); const pathname = new URL(page.url()).pathname; const knownFallbacks = [ '/analytics/sbom-lake', '/mission-control/board', '/mission-control', '/setup', ]; expect(knownFallbacks.some((path) => pathname.startsWith(path))).toBe(true); }); });