Fix web route regressions from Playwright QA
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# Sprint 20260306-003 - Web Playwright Setup/Reset Iteration Loop
|
||||
|
||||
## Topic & Scope
|
||||
- Run repeated Web QA/developer iterations against `https://stella-ops.local` using Playwright as the primary Tier 2 verification method.
|
||||
- Treat an iteration as: verify runtime state, exercise real page flows/actions, capture defects, diagnose root cause, fix within Web scope, and retest with fresh evidence.
|
||||
- Keep the work bounded to SPA/runtime-facing defects in the Web module; avoid repo-wide builds, mass tests, and unrelated cross-module churn.
|
||||
- Working directory: `src/Web/StellaOps.Web`.
|
||||
- Expected evidence: Playwright interaction logs/screenshots, targeted FE tests only when needed, updated UI docs if behavior changes, and sprint execution log entries.
|
||||
|
||||
## Dependencies & Concurrency
|
||||
- Runtime dependency: `https://stella-ops.local` must remain reachable with the current compose stack.
|
||||
- Existing active work in `SPRINT_20260306_001` and `SPRINT_20260306_002` is treated as parallel ownership; avoid editing their in-flight search-context files unless a confirmed Web defect forces a coordinated fix.
|
||||
- Cross-module edits are not planned for this sprint. If a root cause is confirmed outside `src/Web/StellaOps.Web`, record it and stop at triage inside this sprint.
|
||||
- Safe parallelism:
|
||||
- Route exploration, selector mapping, and evidence capture can proceed without touching source files.
|
||||
- Code changes begin only after a defect is reproduced with fresh Playwright evidence.
|
||||
|
||||
## Documentation Prerequisites
|
||||
- `docs/qa/feature-checks/FLOW.md`
|
||||
- `docs/code-of-conduct/TESTING_PRACTICES.md`
|
||||
- `docs/code-of-conduct/CODE_OF_CONDUCT.md`
|
||||
- `docs/modules/ui/architecture.md`
|
||||
- `src/Web/StellaOps.Web/AGENTS.md`
|
||||
|
||||
## Delivery Tracker
|
||||
|
||||
### FE-QA-LOOP-001 - Establish bounded full-iteration workflow
|
||||
Status: DOING
|
||||
Dependency: none
|
||||
Owners: QA, Developer (FE)
|
||||
Task description:
|
||||
- Define and execute a Web-only iteration loop that does not degrade into shallow page pings.
|
||||
- Use Playwright to drive real route interactions, dialogs, filters, forms, keyboard flows, and negative/error paths where the route semantics support them.
|
||||
- Record concrete issue evidence before any fixes are attempted.
|
||||
|
||||
Completion criteria:
|
||||
- [ ] Fresh Playwright evidence exists for a full route/action sweep against the running stack.
|
||||
- [ ] The sweep records route-specific interactions, not only status codes or generic button clicks.
|
||||
- [ ] Any discovered defects are carried into triage instead of skipped.
|
||||
|
||||
### FE-QA-LOOP-002 - Reproduce and triage confirmed Web defects
|
||||
Status: DOING
|
||||
Dependency: FE-QA-LOOP-001
|
||||
Owners: QA, Developer (FE)
|
||||
Task description:
|
||||
- For each confirmed issue in scope, capture the failing user transaction, isolate the route/component/service boundary involved, and classify the defect.
|
||||
- Keep a strict problems-first loop: do not move to a new defect until the current one is triaged to fix or blocked.
|
||||
|
||||
Completion criteria:
|
||||
- [ ] Each confirmed issue has reproducible steps and captured evidence.
|
||||
- [ ] Root cause is documented with affected Web files or explicitly marked out-of-scope.
|
||||
- [ ] Concurrency risks are noted when another agent owns overlapping files.
|
||||
|
||||
### FE-QA-LOOP-003 - Implement scoped fixes and retest
|
||||
Status: DOING
|
||||
Dependency: FE-QA-LOOP-002
|
||||
Owners: Developer (FE), Test Automation
|
||||
Task description:
|
||||
- Apply minimal Web-only fixes for confirmed defects and add focused regression coverage where practical.
|
||||
- Prefer targeted tests and route-specific Playwright replays over heavy workspace builds.
|
||||
|
||||
Completion criteria:
|
||||
- [ ] Each fix has fresh Playwright retest evidence.
|
||||
- [ ] Any new FE automated coverage is targeted and memory-safe.
|
||||
- [ ] Docs are updated when behavior or operator workflow changes.
|
||||
|
||||
## Execution Log
|
||||
| Date (UTC) | Update | Owner |
|
||||
| --- | --- | --- |
|
||||
| 2026-03-06 | Sprint created to isolate Web-only full QA/developer iterations on `https://stella-ops.local` using Playwright-first behavioral verification. | Project Manager |
|
||||
| 2026-03-06 | FE-QA-LOOP-001 started. Preconditions read: QA FLOW, testing practices, code of conduct, UI architecture, and Web AGENTS. Runtime preflight confirms `stella-ops.local` is reachable and the compose stack is healthy. | QA |
|
||||
| 2026-03-06 | Real authenticated Playwright probe found canonical-route drift hidden by prior shallow sweeps: `/security/advisories-vex`, `/ops/policy/overview`, and `/ops/policy/risk-budget` stayed on their requested URLs but rendered dashboard fallback content instead of the target surfaces. | QA |
|
||||
| 2026-03-06 | Root cause confirmed in active router trees: `app.routes.ts` mounts `security-risk.routes.ts` (missing `advisories-vex`) and `ops.routes.ts` mounts `policy-governance.routes.ts` (missing `overview` and `risk-budget` aliases) while existing code/tests still deep-link to those canonical paths. | Developer (FE) |
|
||||
| 2026-03-06 | Fixed canonical route gaps in `security-risk.routes.ts` and `policy-governance.routes.ts`, corrected stale governance card links to canonical policy paths, and tightened `prealpha-canonical-full-sweep.spec.ts` with route-specific heading/title assertions so dashboard fallback cannot pass silently. | Developer (FE) |
|
||||
| 2026-03-06 | Verification: targeted Playwright regression slice passed locally (`npx playwright test tests/e2e/prealpha-canonical-full-sweep.spec.ts --grep \"advisories-vex|ops/policy$|ops/policy/overview|ops/policy/risk-budget\"` -> 4/4 pass). Angular console bundle rebuilt and synced into `compose_console-dist`; real authenticated Playwright probe against `https://stella-ops.local` confirmed all three routes now render their intended headings. | QA |
|
||||
| 2026-03-06 | Live mission-board interaction probe found a second routing defect: the visible `View SBOM` action linked to `/security/sbom/lake`, which stayed on that URL but rendered dashboard fallback content. Direct navigation proved the intended surface is `/security/sbom-lake`. | QA |
|
||||
| 2026-03-06 | Fixed the mission-board SBOM action to point at `/security/sbom-lake` and extended `prealpha-canonical-full-sweep.spec.ts` to cover both the canonical SBOM Lake route and the dashboard click-through path. | Developer (FE) |
|
||||
| 2026-03-06 | Verification: targeted Playwright slice passed locally (`npx playwright test tests/e2e/prealpha-canonical-full-sweep.spec.ts --grep \"sbom-lake|mission board SBOM card opens SBOM Lake\"` -> 2/2 pass). Frontend bundle rebuilt, synced into `compose_console-dist`, and live authenticated Playwright confirmed the mission-board `View SBOM` action now lands on `https://stella-ops.local/security/sbom-lake` with `SBOM Lake` / `Attestation Coverage Metrics` visible. | QA |
|
||||
|
||||
## Decisions & Risks
|
||||
- Decision: this sprint stays inside `src/Web/StellaOps.Web` plus required sprint/doc updates only.
|
||||
- Decision: Playwright is the primary behavioral verification tool; existing shallow sweep scripts are reference material, not acceptance evidence.
|
||||
- Decision: avoid heavy solution-wide builds/tests due to memory constraints; use targeted FE checks only when a fix requires them.
|
||||
- Decision: canonical route regressions must assert route-specific titles/headings, not only that the URL and shell remain visible. This aligns the implementation with `docs/modules/ui/v2-rewire/S00_route_deprecation_map.md`.
|
||||
- Risk: concurrent agents are actively modifying search-related Web files.
|
||||
- Mitigation: avoid those files unless a reproduced defect proves they are the root cause; record any overlap before editing.
|
||||
- Risk: some visible failures may originate from backend APIs rather than Web code.
|
||||
- Mitigation: capture the exact failing route/action and stop at triage if the root cause leaves Web scope.
|
||||
|
||||
## Next Checkpoints
|
||||
- 2026-03-06: Complete first fresh Playwright route/action sweep and defect list.
|
||||
- 2026-03-06: Triage the first confirmed in-scope defect to root cause.
|
||||
- 2026-03-07: Land first scoped fix with fresh retest evidence and a small commit.
|
||||
@@ -243,7 +243,7 @@ interface MissionSummary {
|
||||
<section class="domain-card" aria-label="SBOM snapshot">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">SBOM Findings Snapshot</h2>
|
||||
<a routerLink="/security/sbom/lake" class="card-link">View SBOM</a>
|
||||
<a routerLink="/security/sbom-lake" class="card-link">View SBOM</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="snapshot-stat">
|
||||
@@ -1147,4 +1147,3 @@ export class DashboardV3Component {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,11 +17,26 @@ export const policyGovernanceRoutes: Routes = [
|
||||
loadComponent: () =>
|
||||
import('./risk-budget-dashboard.component').then((m) => m.RiskBudgetDashboardComponent),
|
||||
},
|
||||
{
|
||||
path: 'overview',
|
||||
loadComponent: () =>
|
||||
import('./risk-budget-dashboard.component').then((m) => m.RiskBudgetDashboardComponent),
|
||||
},
|
||||
{
|
||||
path: 'risk-budget',
|
||||
loadComponent: () =>
|
||||
import('./risk-budget-dashboard.component').then((m) => m.RiskBudgetDashboardComponent),
|
||||
},
|
||||
{
|
||||
path: 'budget',
|
||||
loadComponent: () =>
|
||||
import('./risk-budget-dashboard.component').then((m) => m.RiskBudgetDashboardComponent),
|
||||
},
|
||||
{
|
||||
path: 'risk-budget/config',
|
||||
loadComponent: () =>
|
||||
import('./risk-budget-config.component').then((m) => m.RiskBudgetConfigComponent),
|
||||
},
|
||||
{
|
||||
path: 'budget/config',
|
||||
loadComponent: () =>
|
||||
|
||||
@@ -18,7 +18,7 @@ import { RouterLink } from '@angular/router';
|
||||
<h2>Policy Baselines</h2>
|
||||
<p>Environment-scoped baseline definitions and lock rules.</p>
|
||||
</a>
|
||||
<a routerLink="/ops/policy/rules" class="card">
|
||||
<a routerLink="/ops/policy/gates" class="card">
|
||||
<h2>Governance Rules</h2>
|
||||
<p>Rule catalog for release control gate enforcement.</p>
|
||||
</a>
|
||||
@@ -26,7 +26,7 @@ import { RouterLink } from '@angular/router';
|
||||
<h2>Policy Simulation</h2>
|
||||
<p>Dry-run policy evaluations before production rollout.</p>
|
||||
</a>
|
||||
<a routerLink="/ops/policy/exceptions" class="card">
|
||||
<a routerLink="/ops/policy/waivers" class="card">
|
||||
<h2>Exception Workflow</h2>
|
||||
<p>Exception requests, approvals, and expiry management.</p>
|
||||
</a>
|
||||
|
||||
@@ -41,6 +41,15 @@ export const SECURITY_RISK_ROUTES: Routes = [
|
||||
(m) => m.FindingDetailPageComponent
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'advisories-vex',
|
||||
title: 'Advisories & VEX',
|
||||
data: { breadcrumb: 'Advisories & VEX' },
|
||||
loadComponent: () =>
|
||||
import('../features/security/security-disposition-page.component').then(
|
||||
(m) => m.SecurityDispositionPageComponent
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'supply-chain-data',
|
||||
title: 'Supply-Chain Data',
|
||||
|
||||
@@ -95,6 +95,7 @@ const canonicalRoutes = [
|
||||
'/security/disposition',
|
||||
'/security/supply-chain-data',
|
||||
'/security/supply-chain-data/graph',
|
||||
'/security/sbom-lake',
|
||||
'/security/reachability',
|
||||
'/security/reports',
|
||||
'/evidence',
|
||||
@@ -177,6 +178,29 @@ const canonicalRoutes = [
|
||||
'/setup/topology/gate-profiles',
|
||||
] as const;
|
||||
|
||||
const strictRouteExpectations: Partial<Record<(typeof canonicalRoutes)[number], { title: RegExp; texts: string[] }>> = {
|
||||
'/security/advisories-vex': {
|
||||
title: /Advisories/i,
|
||||
texts: ['Security / Advisories & VEX', 'Providers'],
|
||||
},
|
||||
'/security/sbom-lake': {
|
||||
title: /SBOM Lake/i,
|
||||
texts: ['SBOM Lake', 'Attestation Coverage Metrics'],
|
||||
},
|
||||
'/ops/policy': {
|
||||
title: /Policy/i,
|
||||
texts: ['Policy Governance', 'Risk Budget Overview'],
|
||||
},
|
||||
'/ops/policy/overview': {
|
||||
title: /Policy/i,
|
||||
texts: ['Policy Governance', 'Risk Budget Overview'],
|
||||
},
|
||||
'/ops/policy/risk-budget': {
|
||||
title: /Policy/i,
|
||||
texts: ['Policy Governance', 'Risk Budget Overview'],
|
||||
},
|
||||
};
|
||||
|
||||
function collectNgErrors(page: Page): string[] {
|
||||
const errors: string[] = [];
|
||||
page.on('console', (msg) => {
|
||||
@@ -1210,6 +1234,13 @@ test.describe('Pre-alpha canonical full route sweep', () => {
|
||||
test(`route works: ${path}`, async ({ page }) => {
|
||||
const errors = collectNgErrors(page);
|
||||
await assertUsableRoute(page, path);
|
||||
const expectation = strictRouteExpectations[path];
|
||||
if (expectation) {
|
||||
await expect(page).toHaveTitle(expectation.title);
|
||||
for (const text of expectation.texts) {
|
||||
await expect(page.locator('#main-content')).toContainText(text);
|
||||
}
|
||||
}
|
||||
expect(errors, `Runtime errors on ${path}: ${errors.join('\n')}`).toEqual([]);
|
||||
});
|
||||
}
|
||||
@@ -1240,6 +1271,16 @@ test.describe('Pre-alpha key end-user interactions', () => {
|
||||
await expect(page.locator('.topbar__primary-action')).toContainText('Export Report');
|
||||
});
|
||||
|
||||
test('mission board SBOM card opens SBOM Lake', async ({ page }) => {
|
||||
await page.goto('/mission-control/board', { waitUntil: 'domcontentloaded' });
|
||||
await page.locator('#main-content a[href="/security/sbom-lake"]').first().click();
|
||||
|
||||
await expect(page).toHaveURL(/\/security\/sbom-lake$/);
|
||||
await expect(page).toHaveTitle(/SBOM Lake/i);
|
||||
await expect(page.locator('#main-content')).toContainText('SBOM Lake');
|
||||
await expect(page.locator('#main-content')).toContainText('Attestation Coverage Metrics');
|
||||
});
|
||||
|
||||
test('sidebar root navigation works for all canonical workspaces', async ({ page }) => {
|
||||
await page.goto('/mission-control/board', { waitUntil: 'domcontentloaded' });
|
||||
await page.locator('aside.sidebar a[href="/releases/overview"]').first().click();
|
||||
|
||||
Reference in New Issue
Block a user