import { expect, test, type Page } from '@playwright/test'; import { policyAuthorSession } from '../../src/app/testing'; const shellSession = { ...policyAuthorSession, scopes: [ ...new Set([ ...policyAuthorSession.scopes, 'ui.read', 'admin', 'ui.admin', 'orch:read', 'orch:operate', 'orch:quota', 'findings:read', 'vuln:view', 'vuln:investigate', 'vuln:operate', 'vuln:audit', 'authority:tenants.read', 'advisory:read', 'vex:read', 'exceptions:read', 'exceptions:approve', 'aoc:verify', 'policy:read', 'policy:author', 'policy:review', 'policy:approve', 'policy:simulate', 'policy:audit', 'health:read', 'notify:viewer', 'release:read', 'release:write', 'release:publish', 'sbom:read', 'signer:read', ]), ], }; const mockConfig = { authority: { issuer: 'http://127.0.0.1:4400/authority', clientId: 'stella-ops-ui', authorizeEndpoint: 'http://127.0.0.1:4400/authority/connect/authorize', tokenEndpoint: 'http://127.0.0.1:4400/authority/connect/token', logoutEndpoint: 'http://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 authority:tenants.read advisory:read vex:read exceptions:read exceptions:approve aoc:verify findings:read orch:read vuln:view vuln:investigate vuln:operate vuln:audit', audience: 'http://127.0.0.1:4400/gateway', dpopAlgorithms: ['ES256'], refreshLeewaySeconds: 60, }, apiBaseUrls: { authority: '/authority', scanner: '/scanner', policy: '/policy', concelier: '/concelier', attestor: '/attestor', gateway: '/gateway', }, quickstartMode: true, setup: 'complete', }; const oidcConfig = { issuer: mockConfig.authority.issuer, authorization_endpoint: mockConfig.authority.authorizeEndpoint, token_endpoint: mockConfig.authority.tokenEndpoint, jwks_uri: 'http://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'], }; async function setupShell(page: Page): Promise { await page.addInitScript((session) => { try { window.sessionStorage.clear(); } catch { // ignore storage access errors in restricted contexts } (window as { __stellaopsTestSession?: unknown }).__stellaopsTestSession = session; }, shellSession); await page.route('**/platform/envsettings.json', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockConfig), }), ); await page.route('**/config.json', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockConfig), }), ); await page.route('**/authority/.well-known/openid-configuration', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(oidcConfig), }), ); await page.route('**/.well-known/openid-configuration', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(oidcConfig), }), ); await page.route('**/authority/.well-known/jwks.json', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ keys: [] }), }), ); } async function go(page: Page, path: string): Promise { await page.goto(path, { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('networkidle', { timeout: 5000 }).catch(() => null); } async function ensureShell(page: Page): Promise { await expect(page.locator('aside.sidebar')).toHaveCount(1, { timeout: 15000 }); } test.describe.configure({ mode: 'serial' }); test.describe('Critical path shell verification', () => { test.beforeEach(async ({ page }) => { await setupShell(page); }); test('mission-control to releases run flow renders canonical breadcrumbs', async ({ page }) => { await go(page, '/mission-control/board'); await ensureShell(page); await expect(page.locator('app-breadcrumb nav.breadcrumb')).toContainText('Mission Board'); await go(page, '/releases/versions'); await expect(page.locator('app-breadcrumb nav.breadcrumb')).toContainText('Release Versions'); await go(page, '/releases/promotion-queue'); await expect(page.locator('app-breadcrumb nav.breadcrumb')).toContainText('Promotion Queue'); await go(page, '/releases/runs'); await expect(page.locator('app-breadcrumb nav.breadcrumb')).toContainText('Release Runs'); }); test('security advisories workflow remains user-reachable', async ({ page }) => { await go(page, '/security/advisories-vex'); await ensureShell(page); await expect(page.locator('body')).toContainText(/Advisories|VEX|Disposition/i); }); test('evidence workflow exposes overview, verify/replay, and proofs', async ({ page }) => { await go(page, '/evidence/overview'); await expect(page.locator('body')).toContainText(/Evidence/i); await go(page, '/evidence/verify-replay'); await expect(page.locator('app-breadcrumb nav.breadcrumb')).toContainText('Verify & Replay'); await go(page, '/evidence/proofs'); await expect(page.locator('app-breadcrumb nav.breadcrumb')).toContainText('Proof Chains'); }); test('ops and setup workspaces remain distinct', async ({ page }) => { await go(page, '/ops/operations/data-integrity'); await expect(page.locator('app-breadcrumb nav.breadcrumb')).toContainText('Data Integrity'); await go(page, '/ops/policy'); await expect(page.locator('app-breadcrumb nav.breadcrumb')).toContainText('Policy'); await go(page, '/setup/topology/agents'); await expect(page.locator('app-breadcrumb nav.breadcrumb')).toContainText('Agent Fleet'); }); });