import { writeFile } from "node:fs/promises"; import path from "node:path"; import { expect, test } from "@playwright/test"; import { openFixture } from "../__tests__/fixture.js"; test("Git → Pi provider → semantic browser review → persisted feedback → Pi follow-up", async ({ page, }, testInfo) => { const fixture = await openFixture(); try { await fixture.run(); await page.goto(fixture.url); await expect(page.locator("#status")).toHaveText("Review ready"); await expect(page.locator(".hunk")).toHaveCount(2); await expect(page.locator("#view-summary")).toHaveText( "Bound retries before using them.", ); await page.getByRole("button", { name: "Review contract.ts" }).click(); await expect(page.locator("#explanation")).toHaveText( "Explanation for contract.ts.", ); await page.getByRole("tab", { name: "Ask", exact: true }).click(); await page .locator("#question") .fill("Does this include the first attempt?"); await page.getByRole("button", { name: "Send", exact: true }).click(); await expect(page.locator("#chat")).toContainText( "Three attempts include the first call.", ); await page .getByRole("button", { name: "Comment on new line 1 in contract.ts", exact: true, }) .click(); await page.locator("#comment-text").fill("Document the retry bound."); await page .getByRole("button", { name: "Add comment", exact: true }) .click(); await expect(page.locator(".finding")).toContainText( "Document the retry bound.", ); await page .getByRole("button", { name: "Mark reviewed", exact: true }) .click(); await expect(page.locator("#progress-label")).toContainText("1 / 2"); await page .locator("#notes") .fill("Add a regression for exhausted attempts."); await expect .poll(() => page.evaluate(async () => { const response = await fetch("/api/state"); return (await response.json()).review.draft.notes; }), ) .toBe("Add a regression for exhausted attempts."); await page.reload(); await page.getByRole("tab", { name: "Feedback" }).click(); await expect(page.locator("#notes")).toHaveValue( "Add a regression for exhausted attempts.", ); await expect(page.locator(".finding")).toContainText( "Document the retry bound.", ); await page.screenshot({ path: testInfo.outputPath("strata-pi-workspace.png"), fullPage: true, }); await writeFile( path.join(fixture.root, "contract.ts"), "export const attempts = 4;\n", ); page.once("dialog", (dialog) => dialog.accept()); await page.getByRole("button", { name: "Send feedback to Pi" }).click(); await expect(page.locator("#status")).toContainText("snapshot is stale"); expect( fixture.prompts.some((prompt) => prompt.startsWith("Strata review feedback"), ), ).toBe(false); page.once("dialog", (dialog) => dialog.accept()); await page.getByRole("button", { name: "Refresh", exact: true }).click(); await expect(page.locator("#status")).toContainText( "Stale snapshot replaced", ); await expect(page.locator("#notes")).toHaveValue(""); await expect(page.locator(".finding")).toHaveCount(0); await page.locator("#notes").fill("Confirm four attempts are intentional."); page.once("dialog", (dialog) => dialog.accept()); await page.getByRole("button", { name: "Send feedback to Pi" }).click(); await expect(page.locator("#status")).toHaveText("Feedback sent to Pi"); await expect .poll(() => fixture.prompts.some((prompt) => prompt.startsWith("Strata review feedback"), ), ) .toBe(true); const feedback = fixture.prompts.find((prompt) => prompt.startsWith("Strata review feedback"), ); expect(feedback).toContain("Confirm four attempts are intentional."); expect(feedback).toMatch(/Snapshot: [0-9a-f]{64}/); await expect .poll(() => fixture.t.events.messages.some( (message) => message.role === "user" && JSON.stringify(message).includes("Confirm four attempts"), ), ) .toBe(true); await fixture.t.session.agent.waitForIdle(); page.once("dialog", (dialog) => dialog.accept()); await page.getByRole("button", { name: "Close", exact: true }).click(); await expect(page.locator("#status")).toContainText("Review closed"); const calls = fixture.calls; await fixture.run(); await page.goto(fixture.url); await expect(page.locator("#status")).toHaveText("Review ready"); expect(fixture.calls).toBe(calls); await expect(page.locator("#notes")).toHaveValue(""); } finally { await fixture.close(); } });