mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-04 07:20:10 +00:00
162 lines
7.8 KiB
TypeScript
162 lines
7.8 KiB
TypeScript
// Agent teams: the board in the session UI — the rail section (grouped by state,
|
|
// blocked on top, active work only) and the expanded overlay: a quiet list over
|
|
// the store's RAW states (In progress / Awaiting review / Queued — no computed
|
|
// interpretation layer, no row buttons) plus a detail pane with the item's merged
|
|
// event timeline. Verdicts flow through the pane: Mark done / Request changes….
|
|
// The fake agent files items on "plan the work"; transitions round-trip through
|
|
// the mocked /board endpoints as the user.
|
|
import { expect } from "@playwright/test";
|
|
import { test } from "./fixtures";
|
|
|
|
async function planTheWork(page: import("@playwright/test").Page) {
|
|
await page.goto("/");
|
|
await page.getByPlaceholder(/Ask the coworker/).fill("plan the work");
|
|
await page.getByRole("button", { name: "Send" }).click();
|
|
await expect(page.getByText(/filed 5 work items/)).toBeVisible();
|
|
}
|
|
|
|
// Seventeenth pass: every drawer section starts collapsed — expanding the Board
|
|
// section is now an explicit step wherever a test reads the rail's rows.
|
|
async function openBoardSection(page: import("@playwright/test").Page) {
|
|
await page.getByTestId("rail-toggle-board").click();
|
|
await expect(page.getByTestId("board-rail")).toBeVisible();
|
|
}
|
|
|
|
test("plain sessions carry zero board chrome", async ({ page }) => {
|
|
await page.goto("/");
|
|
await page.getByPlaceholder(/Ask the coworker/).fill("hello");
|
|
await page.getByRole("button", { name: "Send" }).click();
|
|
await expect(page.getByText("Echo: hello")).toBeVisible();
|
|
await expect(page.getByTestId("board-rail")).toHaveCount(0);
|
|
await expect(page.getByTestId("rail-toggle-board")).toHaveCount(0);
|
|
});
|
|
|
|
test("filed items appear grouped in the rail, blocked on top, queued items listed", async ({
|
|
page,
|
|
}) => {
|
|
await planTheWork(page);
|
|
// collapsed by default: the header chip is the maximum signal
|
|
await expect(page.getByTestId("board-rail")).toHaveCount(0);
|
|
await expect(page.getByTestId("rail-toggle-board")).toContainText("1 blocked · 1 review");
|
|
await openBoardSection(page);
|
|
const rail = page.getByTestId("board-rail");
|
|
await expect(rail).toBeVisible();
|
|
const groups = rail.locator(".board-group");
|
|
await expect(groups.first()).toHaveText("Blocked");
|
|
await expect(rail).toContainText("Queued");
|
|
await expect(rail.getByText("Secrets — git history, both repos")).toBeVisible();
|
|
});
|
|
|
|
test("the overlay lists raw-state sections; verdicts flow through the detail pane", async ({
|
|
page,
|
|
}) => {
|
|
await planTheWork(page);
|
|
await page.getByTestId("board-expand").click();
|
|
const overlay = page.getByTestId("board-overlay");
|
|
await expect(overlay).toBeVisible();
|
|
// the owner's sections, nothing computed — and no buttons in the rows
|
|
await expect(overlay).toContainText("In progress");
|
|
await expect(overlay).toContainText("Awaiting review");
|
|
await expect(overlay).toContainText("Queued");
|
|
await expect(overlay.getByRole("button", { name: "Mark done" })).toHaveCount(0);
|
|
// a blocked row carries the blocker as a plain fact under In progress
|
|
await expect(page.getByTestId("board-item-4")).toContainText(
|
|
"cloud-posture · blocked: need tfvars for staging",
|
|
);
|
|
|
|
// review verdict from the pane
|
|
await page.getByTestId("board-item-5").click();
|
|
const detail = page.getByTestId("board-detail");
|
|
await detail.getByRole("button", { name: "Mark done" }).click();
|
|
await expect(page.getByTestId("overlay-finished-toggle")).toHaveText("1 finished · show");
|
|
|
|
// queued items are removed from their pane (maps to canceled underneath)
|
|
await page.getByTestId("board-item-1").click();
|
|
await detail.getByRole("button", { name: "Remove" }).click();
|
|
await expect(page.getByTestId("overlay-finished-toggle")).toHaveText("2 finished · show");
|
|
|
|
await page.keyboard.press("Escape");
|
|
await expect(page.getByTestId("board-overlay")).toHaveCount(0);
|
|
});
|
|
|
|
test("finished items leave the rail; a quiet toggle reveals them", async ({ page }) => {
|
|
await planTheWork(page);
|
|
await openBoardSection(page);
|
|
const rail = page.getByTestId("board-rail");
|
|
await expect(rail.getByText("Report rollup")).toBeVisible(); // review = active
|
|
await page.getByTestId("board-expand").click();
|
|
await page.getByTestId("board-item-5").click();
|
|
await page.getByTestId("board-detail").getByRole("button", { name: "Mark done" }).click();
|
|
await page.keyboard.press("Escape");
|
|
// done vanishes from the rail — a fresh session on an old board starts calm
|
|
await expect(rail.getByText("Report rollup")).toHaveCount(0);
|
|
const toggle = page.getByTestId("board-finished-toggle");
|
|
await expect(toggle).toHaveText("1 finished · show");
|
|
await toggle.click();
|
|
await expect(rail.getByText("Report rollup")).toBeVisible();
|
|
await toggle.click();
|
|
await expect(rail.getByText("Report rollup")).toHaveCount(0);
|
|
});
|
|
|
|
test("item detail: timeline with attachment, worker link, request changes", async ({
|
|
page,
|
|
}) => {
|
|
await planTheWork(page);
|
|
await openBoardSection(page);
|
|
// a rail row deep-opens the overlay on that item's detail
|
|
await page.getByTestId("board-rail").getByText("Report rollup").click();
|
|
const detail = page.getByTestId("board-detail");
|
|
await expect(detail).toBeVisible();
|
|
await expect(detail).toContainText("#5");
|
|
await expect(detail).toContainText("Report rollup");
|
|
await expect(detail).toContainText("In review");
|
|
await expect(detail).toContainText("Done when");
|
|
// the merged timeline tells the item's whole story
|
|
await expect(detail).toContainText("security started");
|
|
await expect(detail).toContainText("balances reconcile against the seeded rows");
|
|
await expect(detail).toContainText("moved to in review");
|
|
// the attachment image actually loads (real bytes from the fixture)
|
|
await expect(detail.getByTestId("board-attachment")).toBeVisible();
|
|
// the assignee links to that coworker's session
|
|
await expect(detail.getByTestId("board-open-worker")).toHaveText("security ↗");
|
|
// Request changes… discloses a comment box; sending returns the item to work
|
|
await detail.getByRole("button", { name: "Request changes…" }).click();
|
|
await detail.getByPlaceholder("What needs to change?").fill("totals drift on Tom");
|
|
await detail.getByRole("button", { name: "Request changes", exact: true }).click();
|
|
await expect(detail).toContainText("In progress");
|
|
// switching rows switches the pane
|
|
await page.getByTestId("board-item-3").click();
|
|
await expect(detail).toContainText("Dependency audit — lockfiles");
|
|
});
|
|
|
|
test("Add a note is a pure append — it lands in the timeline, state untouched", async ({
|
|
page,
|
|
}) => {
|
|
await planTheWork(page);
|
|
await openBoardSection(page);
|
|
await page.getByTestId("board-rail").getByText("Report rollup").click();
|
|
const detail = page.getByTestId("board-detail");
|
|
await expect(detail).toContainText("In review");
|
|
await detail.getByTestId("board-note-input").fill("prefer the v2 endpoint for totals");
|
|
await detail.getByTestId("board-note-input").press("Enter");
|
|
// the note appears as a timeline event…
|
|
await expect(detail).toContainText("user commented");
|
|
await expect(detail).toContainText("prefer the v2 endpoint for totals");
|
|
// …and the state did NOT change (notes never transition)
|
|
await expect(detail).toContainText("In review");
|
|
await expect(detail.getByRole("button", { name: "Mark done" })).toBeVisible();
|
|
});
|
|
|
|
test("journal section lists cases once a board exists", async ({ page }) => {
|
|
await planTheWork(page);
|
|
// Journal is not a primary section — it sits behind the quiet More row.
|
|
await expect(page.getByTestId("rail-toggle-journal")).toHaveCount(0);
|
|
await page.getByTestId("rail-toggle-journal").click();
|
|
const journal = page.getByTestId("journal-list");
|
|
await expect(journal).toBeVisible();
|
|
await expect(journal).toContainText("findings");
|
|
await expect(journal).toContainText("12 entries");
|
|
// Access folds with it — the drawer keeps three primary sections.
|
|
await expect(page.getByTestId("access-section")).toBeVisible();
|
|
});
|