Files
openworker/surfaces/gui/e2e/family-gate.spec.ts
T
Rohit C Prasad 3d13c7d699 coworker picker: setup chips above composer, folder pick at send (UX-029)
Per-session coworker+folder chips replace the sidebar split-button picker; code family gets a send-time folder dialog with git-ready temp dirs and Save as project.
Builtins ship enabled; user-facing noun is Coworker; personas flag now defaults on.
2026-08-10 21:43:02 -07:00

94 lines
4.4 KiB
TypeScript

import { test, expect } from "./fixtures";
// UX-029: the persona FAMILY still decides workspace behavior, but code-family
// enforcement moved from a modal gate at session start to the SEND moment:
// code → send with no folder → "Where should … work?" dialog (recents / native
// picker / "Start in a temporary folder", git-init'd, created only now)
// knowledge → starts orphan on a transparent temporary dir — never gated
// The coworker pick lives in the setup chip row above the composer, only before the
// first message of a new session; afterwards the row leaves and the facts move to the
// session header.
async function newDraftAs(page: import("@playwright/test").Page, coworker: RegExp) {
await page.getByText("New session").first().click();
await page.getByTestId("coworker-chip").click();
await page.locator(".setup-menu").getByRole("button", { name: coworker }).click();
}
test("knowledge coworker: new session starts instantly, no gate, no dialog", async ({ page }) => {
await page.goto("/");
await newDraftAs(page, /Ops Coworker/);
await expect(page.locator(".gate-overlay")).toHaveCount(0);
const box = page.getByPlaceholder(/Ask the coworker/);
await box.fill("hello there");
await page.getByRole("button", { name: "Send" }).click();
await expect(page.getByText(/Echo: hello there/)).toBeVisible();
await expect(page.getByTestId("send-folder-dialog")).toHaveCount(0);
});
test("code coworker: send with no folder asks where to work; temp folder sends the message", async ({
page,
}) => {
await page.goto("/");
await newDraftAs(page, /Code Coworker/);
// No modal gate up front — the composer is live and the draft is composable.
await expect(page.locator(".gate-overlay")).toHaveCount(0);
await page.getByPlaceholder(/Ask the coder/).fill("fix the tests");
await page.getByRole("button", { name: "Send" }).click();
const dlg = page.getByTestId("send-folder-dialog");
await expect(dlg).toBeVisible();
await expect(dlg.getByText("Where should Code Coworker work?")).toBeVisible();
await dlg.getByTestId("start-temp-folder").click();
// The message flies as soon as the choice lands — no second send click, and the local
// echo isn't duplicated by turn_start (the notice sits between them).
await expect(page.getByText(/Echo: fix the tests/)).toBeVisible();
await expect(page.locator(".main-scroll").getByText("fix the tests", { exact: true })).toHaveCount(1);
await expect(page.getByText("Temporary folder created · git initialized")).toBeVisible();
// The raw temp path never shows: header says "Temporary folder" + Save as project….
const sub = page.getByTestId("session-subtitle");
await expect(sub).toContainText("Code Coworker");
await expect(sub).toContainText("Temporary folder");
await expect(sub).not.toContainText("ow-temp");
await expect(page.getByTestId("save-as-project")).toBeVisible();
// One-time pick: the setup row left with the first message.
await expect(page.getByTestId("setup-row")).toHaveCount(0);
// A NEW session never inherits the temporary dir — the folder chip starts fresh.
await page.getByText("New session").first().click();
await expect(page.getByTestId("folder-chip")).toContainText("Choose folder");
});
test("code coworker: Choose a folder… binds the picked project and sends", async ({ page }) => {
await page.goto("/");
await newDraftAs(page, /Code Coworker/);
await page.getByPlaceholder(/Ask the coder/).fill("hello repo");
await page.getByRole("button", { name: "Send" }).click();
// Native pick is mocked server-side → /tmp/picked-folder.
await page.getByTestId("send-folder-dialog").getByRole("button", { name: "Choose a folder…" }).click();
await expect(page.getByText(/Echo: hello repo/)).toBeVisible();
await expect(page.getByTestId("session-subtitle")).toContainText("picked-folder");
await expect(page.getByTestId("save-as-project")).toHaveCount(0);
});
test("escape restores the draft instead of losing it", async ({ page }) => {
await page.goto("/");
await newDraftAs(page, /Code Coworker/);
const box = page.getByPlaceholder(/Ask the coder/);
await box.fill("precious draft");
await page.getByRole("button", { name: "Send" }).click();
await expect(page.getByTestId("send-folder-dialog")).toBeVisible();
await page.keyboard.press("Escape");
await expect(page.getByTestId("send-folder-dialog")).toHaveCount(0);
await expect(box).toHaveValue("precious draft");
});