Web UI: feature updates across all modules

Broad UI improvements spanning auth, branding, notifications, agents, analytics,
approvals, audit-log, bundles, configuration, console-admin, dashboard,
deployments, doctor, environments, evidence, feed-mirror, graph, integration-hub,
issuer-trust, lineage, notify, offline-kit, policy, promotions, quota, registry,
release-orchestrator, releases, sbom, scans, secret-detection, security, settings,
setup-wizard, system-health, topology, triage, trust-admin, unknowns, vex-hub,
vulnerabilities, and watchlist features.

Adds new shared components (page-action-outlet, stella-action-card, stella-form-field),
scripts feature module, audit-trust component, e2e test helpers, and release page
e2e specs. Updates auth session model, branding service, color tokens, form styles,
and i18n translations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-03-27 12:28:48 +02:00
parent f767489e26
commit 95357ffbb9
199 changed files with 6315 additions and 2222 deletions

View File

@@ -3,18 +3,18 @@ import { expect, test } from '@playwright/test';
const mockConfig = {
authority: {
issuer: 'https://authority.local',
clientId: 'stella-ops-ui',
authorizeEndpoint: 'https://authority.local/connect/authorize',
tokenEndpoint: 'https://authority.local/connect/token',
logoutEndpoint: 'https://authority.local/connect/logout',
redirectUri: 'http://127.0.0.1:4400/auth/callback',
postLogoutRedirectUri: 'http://127.0.0.1:4400/',
clientId: 'stella-ops-ui',
authorizeEndpoint: 'https://authority.local/connect/authorize',
tokenEndpoint: 'https://authority.local/connect/token',
logoutEndpoint: 'https://authority.local/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: 'https://scanner.local',
dpopAlgorithms: ['ES256'],
refreshLeewaySeconds: 60,
},
audience: 'https://scanner.local',
dpopAlgorithms: ['ES256'],
refreshLeewaySeconds: 60,
},
apiBaseUrls: {
authority: 'https://authority.local',
scanner: 'https://scanner.local',
@@ -73,7 +73,7 @@ test('sign-in flow builds Authority authorization URL', async ({ page }) => {
expect(authorizeUrl.searchParams.get('client_id')).toBe('stella-ops-ui');
});
test('callback without pending state surfaces error message', async ({ page }) => {
await page.route('https://authority.local/**', (route) =>
route.fulfill({ status: 400, body: 'blocked' })
@@ -81,3 +81,48 @@ test('callback without pending state surfaces error message', async ({ page }) =
await page.goto('/auth/callback?code=test-code&state=missing');
await expect(page.getByText(/unable to complete the sign-in flow/i)).toBeVisible({ timeout: 10000 });
});
test('session-expired banner shows when persisted session has expired tokens', async ({ page }) => {
// Seed an expired full session into sessionStorage before Angular boots.
// Only set the full session key — omit the metadata key so trySilentRefresh
// exits early (no subjectHint) and the 'expired' status stays visible.
await page.addInitScript(() => {
const expiredSession = {
tokens: {
accessToken: 'expired-token',
expiresAtEpochMs: Date.now() - 60_000, // expired 1 min ago
refreshToken: 'expired-refresh',
scope: 'openid ui.read',
tokenType: 'Bearer',
},
identity: {
subject: 'user-expired',
name: 'Expired User',
roles: ['ui.read'],
},
dpopKeyThumbprint: 'thumb-expired',
issuedAtEpochMs: Date.now() - 3600_000,
tenantId: 'tenant-test',
scopes: ['ui.read'],
audiences: ['console'],
authenticationTimeEpochMs: Date.now() - 3600_000,
freshAuthActive: false,
freshAuthExpiresAtEpochMs: null,
};
try {
window.sessionStorage.setItem(
'stellaops.auth.session.full',
JSON.stringify(expiredSession)
);
} catch {
// ignore
}
(window as any).__stellaopsTestSession = undefined;
});
await page.goto('/');
const banner = page.getByRole('alert');
await expect(banner).toBeVisible({ timeout: 10_000 });
await expect(banner).toContainText('session has expired');
await expect(page.getByRole('button', { name: /sign in again/i })).toBeVisible();
});