evOo-local/apps/web/e2e/autopilot.spec.ts
cynarAI 54658ee689
feat: autopilot progress, composer start and morning summary (#146)
The autopilot's start line counts its steps and folds open to the latest
checkpoints, and its result reads as a card. The composer's plus menu
starts an autopilot with the draft as the order and a choice of until
done, tomorrow morning or a time. Today lists the autopilots that run,
wait or ended within a day, and their step runs no longer show one by
one. Autopilot runs send no completion or failure push; only a pause
notifies.


Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 04:45:49 +02:00

111 lines
5.5 KiB
TypeScript

import { expect, type Page, test } from "@playwright/test";
import { captureScreenshot, completeOnboarding, openUserSettings, signup } from "./helpers";
async function useAppearance(page: Page, appearance: "light" | "dark") {
const settings = await openUserSettings(page);
await settings.getByTestId(`ui-appearance-${appearance}`).click();
await expect(page.locator("html")).toHaveAttribute("data-theme", appearance);
await settings.getByRole("button", { name: "Close user settings" }).click();
await expect(settings).toBeHidden();
}
test("the autopilot works step by step until the order is done", async ({ page }, testInfo) => {
const stamp = Date.now();
await signup(page, `autopilot-${stamp}@rakazo.test`, "password12", "Autopilot");
await completeOnboarding(page);
await page.getByPlaceholder(/Message/).fill("Work through the night on the tickets.");
await page.keyboard.press("Enter");
const start = page.locator('[data-testid="autopilot-line"][data-event="autopilot.started"]');
await expect(start).toBeVisible({ timeout: 60_000 });
await expect(start).toContainText("Tidy up the tickets");
await expect(start).toContainText("Only tidy, change nothing else");
const finished = page.locator('[data-testid="autopilot-line"][data-event="autopilot.finished"]');
await expect(finished).toBeVisible({ timeout: 120_000 });
await expect(finished).toContainText("Tidied 5 tickets; nothing waits on you.");
// Once it is over, the start line has no Stop any more.
await expect(start.getByRole("button", { name: "Stop" })).toHaveCount(0);
await useAppearance(page, "light");
await finished.scrollIntoViewIfNeeded();
await captureScreenshot(page, testInfo, "autopilot-finished-light");
await useAppearance(page, "dark");
await finished.scrollIntoViewIfNeeded();
await captureScreenshot(page, testInfo, "autopilot-finished-dark");
});
test("an autopilot without progress pauses and stops on request", async ({ page }, testInfo) => {
const stamp = Date.now();
await signup(page, `autopilot-idle-${stamp}@rakazo.test`, "password12", "Autopilot Idle");
await completeOnboarding(page);
await page.getByPlaceholder(/Message/).fill("Work through the night, idle check.");
await page.keyboard.press("Enter");
const paused = page.locator('[data-testid="autopilot-line"][data-event="autopilot.paused"]');
await expect(paused).toBeVisible({ timeout: 120_000 });
await expect(paused).toContainText("No progress in 3 runs");
await expect(paused.getByRole("button", { name: "Continue" })).toBeVisible();
await useAppearance(page, "dark");
await paused.scrollIntoViewIfNeeded();
await captureScreenshot(page, testInfo, "autopilot-paused-dark");
await paused.getByRole("button", { name: "Stop" }).click();
const stopped = page.locator('[data-testid="autopilot-line"][data-event="autopilot.stopped"]');
await expect(stopped).toBeVisible({ timeout: 30_000 });
await expect(paused.getByRole("button", { name: "Continue" })).toHaveCount(0);
await useAppearance(page, "light");
await stopped.scrollIntoViewIfNeeded();
await captureScreenshot(page, testInfo, "autopilot-paused-light");
});
test("an autopilot started from the composer works overnight and reports in Today", async ({
page,
}, testInfo) => {
const stamp = Date.now();
await signup(page, `autopilot-night-${stamp}@rakazo.test`, "password12", "Autopilot Night");
await completeOnboarding(page);
await page.getByPlaceholder(/Message/).fill("Tidy the tickets");
await page.getByRole("button", { name: "Add" }).click();
await page.getByRole("menuitem", { name: "Autopilot" }).click();
const dialog = page.getByTestId("autopilot-dialog");
await expect(dialog.getByRole("textbox", { name: "Order" })).toHaveValue("Tidy the tickets");
await dialog.getByRole("radio", { name: "Until tomorrow morning" }).click();
await dialog.getByRole("button", { name: "Start" }).click();
await expect(dialog).toBeHidden();
await expect(page.getByPlaceholder(/Message/)).toHaveValue("");
const start = page.locator('[data-testid="autopilot-line"][data-event="autopilot.started"]');
await expect(start).toContainText("Tidy the tickets", { timeout: 60_000 });
await expect(start).toContainText(/until 8:00/);
const finished = page.locator('[data-testid="autopilot-line"][data-event="autopilot.finished"]');
await expect(finished).toBeVisible({ timeout: 120_000 });
await expect(finished.getByTestId("autopilot-result")).toHaveText(
"Tidied 5 tickets; nothing waits on you.",
);
await start.getByRole("button", { name: "5 steps" }).click();
await expect(start.getByTestId("autopilot-steps").locator("li")).toHaveCount(5);
await expect(start.getByTestId("autopilot-steps")).toContainText("Ticket 5 tidied");
await useAppearance(page, "light");
await start.scrollIntoViewIfNeeded();
await captureScreenshot(page, testInfo, "autopilot-night-light");
await useAppearance(page, "dark");
await start.scrollIntoViewIfNeeded();
await captureScreenshot(page, testInfo, "autopilot-night-dark");
// The morning summary: what the autopilot did, in Today.
await page.getByRole("button", { name: "Today", exact: true }).click();
const row = page.getByTestId("heute-autopilot-row");
await expect(row).toHaveAttribute("data-status", "finished", { timeout: 30_000 });
await expect(row).toContainText("Tidy the tickets");
await expect(row).toContainText("Tidied 5 tickets; nothing waits on you.");
await expect(row).toContainText("5 steps");
await captureScreenshot(page, testInfo, "autopilot-today-dark");
await row.click();
await expect(start).toBeVisible();
});