import { expect, test } from "@playwright/test"; import { enableScopedE2EAuth, scopedPath } from "./helpers/auth"; import { mockActivationFlow } from "./helpers/activation-fixtures"; import { buildObservabilitySummary } from "./helpers/sandbox-fixtures"; import { fulfillJson } from "./helpers/automation-control-plane-fixtures"; test("PR link uses the metadata snapshot instead of the repo picker", async ({ page, }) => { await enableScopedE2EAuth(page); await mockActivationFlow(page); await page.route("**/api/observability/stats*", (route) => fulfillJson(route, buildObservabilitySummary([])) ); // The repo picker has a different repo than the metadata snapshot. await page.route("**/api/repos*", (route) => fulfillJson(route, [{ id: "context-repo", full_name: "Mogplex/mogplex" }]) ); await page.route("**/api/observability/calls*", (route) => fulfillJson(route, { calls: [ { id: "pr-link-call", type: "pr_review", model: "anthropic/claude-sonnet-4", repo_id: "context-repo", status: "success", metadata: { repo_full_name: "webrenew/gtm-supahost", pr_number: 42, }, started_at: new Date().toISOString(), tool_calls: [], tool_calls_count: 0, }, ], total: 1, page: 1, limit: 25, }) ); await page.route("**/api/observability/call-events*", (route) => fulfillJson(route, { events: [] }) ); // The AI calls table is on the Usage tab. await page.goto(scopedPath("observability?view=usage")); // The PR link should use the metadata snapshot, not the repo picker. const prLink = page.locator('a[href*="github.com"]').filter({ hasText: /gtm-supahost#42/, }); await expect(prLink).toBeVisible(); await expect(prLink).toHaveAttribute( "href", "https://github.com/webrenew/gtm-supahost/pull/42" ); }); for (const width of [390, 1280]) { for (const snapshot of ["current", "legacy", "missing"] as const) { test(`Slack run repository is readable at ${width}px with ${snapshot} metadata`, async ({ page, }) => { await page.setViewportSize({ width, height: 844 }); await enableScopedE2EAuth(page); await mockActivationFlow(page); const metadata = { run_origin: "slack", ...(snapshot === "current" ? { repo_full_name: "webrenew/gtm-supahost" } : {}), ...(snapshot === "legacy" ? { repo: "webrenew/gtm-supahost" } : {}), slack: { mode: "repo_agent", attribution_mode: "mapped_profile" }, }; await page.route("**/api/observability/stats*", (route) => fulfillJson(route, buildObservabilitySummary([])) ); // The actual target isn't in the current workspace's repository picker. await page.route("**/api/repos*", (route) => fulfillJson(route, [ { id: "context-repo", full_name: "Mogplex/mogplex" }, ]) ); await page.route("**/api/observability/calls*", (route) => fulfillJson(route, { calls: [ { id: "slack-call", type: "agent", model: "openai/gpt-6-astra-fast", repo_id: "actual-target", status: "streaming", metadata, started_at: new Date().toISOString(), tool_calls: [], tool_calls_count: 0, }, ], total: 1, page: 1, limit: 25, }) ); await page.route("**/api/observability/call-events*", (route) => fulfillJson(route, { events: [] }) ); await page.goto(scopedPath("observability?call_id=slack-call")); // The Where column and expanded row both show the resolved repository. const whereCell = page.getByTestId("run-repository-location"); const details = page.getByTestId("run-repository-details"); const expectedRepo = snapshot === "missing" ? "Not recorded" : "webrenew/gtm-supahost"; await expect(whereCell).toHaveText(expectedRepo); await expect(details).toContainText(expectedRepo); await expect(details).not.toContainText("Mogplex/mogplex"); // No horizontal scroll, hover, raw event JSON, or open workspace needed. // Both surfaces should be within viewport at all tested widths. for (const label of [whereCell, details]) { await expect(label).toBeVisible(); const box = await label.boundingBox(); expect(box).not.toBeNull(); expect(box!.x).toBeGreaterThanOrEqual(0); expect(box!.x + box!.width).toBeLessThanOrEqual(width); } }); } }