fix(e2e): sweep the panels the manifest claims to cover, and own the shared provider

Three holes in the route manifest, one class: a gate that reports green on a
surface it never mounts.

- FLOW_DETAIL_TABS listed 8 of the flow detail's 10 desktop panels. Terminal is
  the default and gets scanned anyway, but Automation was mounted by nothing —
  no axe scan, no palette scan — so a regression confined to it stayed invisible.
- /dashboard declared no tabs at all although it is a two-tab Radix Tabs, and
  Radix unmounts the inactive panel: Overview, four Cards and two usage tables,
  was never scanned. The same regression in Analytics would have been caught.
- Its `ready` was a ChartCard heading rendered above the component's own loading
  branch, which is exactly what the tabs contract forbids: the sweep could start
  against spinners. Gate on a data row instead.
- templates-provider.tsx was owned by /templates alone while flow-form.tsx
  consumes it from /flows/5, so affected-routes scoped a provider change away
  from the route that renders the consumer.

All four panels pass axe and palette in both themes as-is. The ownership case is
pinned against the real manifest, not the fake one, and fails before the fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Sergey Kozyrenko
2026-07-25 23:56:04 +07:00
co-authored by Claude Opus 4.8
parent b56833c2ea
commit 17198bf073
2 changed files with 14 additions and 1 deletions
@@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest';
import type { RouteSource } from './affected-routes.ts';
import { affectedRoutes } from './affected-routes.ts';
import { ROUTE_MANIFEST } from './routes.ts';
const MANIFEST: RouteSource[] = [
{
@@ -58,4 +59,12 @@ describe('affectedRoutes', () => {
expect(result.map((route) => route.path)).toEqual(['/flows']);
});
// The fake manifest above cannot catch ownership drift in the real one: a shared provider claimed
// by a single route silently scopes a run away from the other routes that mount its consumers.
it('scopes a shared provider to every route that mounts a consumer', () => {
const result = affectedRoutes(['frontend/src/providers/templates-provider.tsx'], ROUTE_MANIFEST);
expect(result.map((route) => route.path).sort()).toEqual(['/flows/5', '/templates']);
});
});
+5 -1
View File
@@ -43,6 +43,7 @@ export interface RouteTab {
export const FLOW_DETAIL_TABS: RouteTab[] = [
{ name: 'Dashboard', ready: (page) => page.getByText('Usage by Model & Provider') },
{ name: 'Assistant', ready: (page) => page.getByText('New assistant', { exact: true }) },
{ name: 'Automation', ready: (page) => page.getByText('No active tasks') },
{ name: 'Tasks', ready: (page) => page.getByText('E2E Task Alpha') },
{ name: 'Agents', ready: (page) => page.getByText('E2E agent reconnaissance') },
{ name: 'Searches', ready: (page) => page.getByText('E2E search for the CVE') },
@@ -97,6 +98,8 @@ export const ROUTE_MANIFEST: RouteManifestEntry[] = [
'src/components/shared/file-manager',
'src/components/dashboard',
'src/features/resources',
// Assistant-tab FlowForm reads useTemplates, so a provider change reaches this route too.
'src/providers/templates-provider.tsx',
],
tabs: FLOW_DETAIL_TABS,
},
@@ -130,8 +133,9 @@ export const ROUTE_MANIFEST: RouteManifestEntry[] = [
a11yWaivers: [{ rule: 'aria-valid-attr-value', target: /radix-.*-trigger-/ }],
cassette: dashboardCassette,
path: routes.dashboard,
ready: (page) => page.getByRole('heading', { name: 'Flows Activity Over Time' }),
ready: (page) => page.getByText('E2E Alpha'),
sources: ['src/pages/dashboard', 'src/components/dashboard'],
tabs: [{ name: 'Overview', ready: (page) => page.getByRole('cell', { exact: true, name: 'e2e-provider' }) }],
},
{
cassette: settingsPromptsCassette,