import { expect, test } from "@playwright/test"; import type { AcceptanceResponse, ReviewListResponse, TodoListResponse, } from "../../src/api/workflow"; function mockChatApis( page: import("@playwright/test").Page, overrides: { acceptance?: AcceptanceResponse; reviewQueue?: ReviewListResponse; todos?: TodoListResponse; } = {}, ) { const acceptance: AcceptanceResponse = overrides.acceptance ?? { can_accept: false, reasons: ["No test results recorded for the story."], warning: null, summary: { total: 0, passed: 0, failed: 0 }, missing_categories: ["unit", "integration"], }; const reviewQueue: ReviewListResponse = overrides.reviewQueue ?? { stories: [], }; const todos: TodoListResponse = overrides.todos ?? { stories: [], }; return Promise.all([ page.route("**/api/projects", (route) => route.fulfill({ json: ["/tmp/test-project"] }), ), page.route("**/api/io/fs/home", (route) => route.fulfill({ json: "/tmp" }), ), page.route("**/api/project", (route) => { if (route.request().method() === "POST") { return route.fulfill({ json: "/tmp/test-project" }); } if (route.request().method() === "DELETE") { return route.fulfill({ json: true }); } return route.fulfill({ json: null }); }), page.route("**/api/ollama/models**", (route) => route.fulfill({ json: ["llama3.1"] }), ), page.route("**/api/anthropic/key/exists", (route) => route.fulfill({ json: false }), ), page.route("**/api/anthropic/models", (route) => route.fulfill({ json: [] }), ), page.route("**/api/model", (route) => { if (route.request().method() === "POST") { return route.fulfill({ json: true }); } return route.fulfill({ json: null }); }), page.route("**/api/workflow/acceptance", (route) => { if (route.request().url().includes("/ensure")) return route.fallback(); return route.fulfill({ json: acceptance }); }), page.route("**/api/workflow/review/all", (route) => route.fulfill({ json: reviewQueue }), ), page.route("**/api/workflow/acceptance/ensure", (route) => route.fulfill({ json: true }), ), page.route("**/api/io/fs/list/absolute**", (route) => route.fulfill({ json: [] }), ), page.route("**/api/workflow/todos", (route) => route.fulfill({ json: todos }), ), ]); } async function openProject(page: import("@playwright/test").Page) { await page.goto("/"); await page.getByPlaceholder("/path/to/project").fill("/tmp/test-project"); await page.getByRole("button", { name: "Open Project" }).click(); await expect(page.getByText("Story TODOs", { exact: true })).toBeVisible(); } test.describe("Story TODOs panel", () => { test("shows unchecked acceptance criteria", async ({ page }) => { await mockChatApis(page, { todos: { stories: [ { story_id: "28_ui_show_test_todos", story_name: "Show Remaining Test TODOs in the UI", todos: [ "The UI lists unchecked acceptance criteria.", "Each TODO is displayed as its full text.", ], error: null, }, ], }, }); await openProject(page); await expect( page.getByText("The UI lists unchecked acceptance criteria."), ).toBeVisible(); await expect( page.getByText("Each TODO is displayed as its full text."), ).toBeVisible(); await expect(page.getByText("2 remaining")).toBeVisible(); }); test("shows completion message when all criteria are checked", async ({ page, }) => { await mockChatApis(page, { todos: { stories: [ { story_id: "28_ui_show_test_todos", story_name: "Show Remaining Test TODOs in the UI", todos: [], error: null, }, ], }, }); await openProject(page); await expect( page.getByText("All acceptance criteria complete."), ).toBeVisible(); await expect(page.getByText("0 remaining")).toBeVisible(); }); test("shows per-story front matter error", async ({ page }) => { await mockChatApis(page, { todos: { stories: [ { story_id: "28_ui_show_test_todos", story_name: null, todos: [], error: "Missing front matter", }, ], }, }); await openProject(page); await expect(page.getByText("Missing front matter")).toBeVisible(); await expect(page.getByText("28_ui_show_test_todos")).toBeVisible(); }); test("shows TODO items from multiple stories", async ({ page }) => { await mockChatApis(page, { todos: { stories: [ { story_id: "28_ui_show_test_todos", story_name: "Show TODOs", todos: ["First criterion."], error: null, }, { story_id: "29_another_story", story_name: "Another Story", todos: ["Second criterion."], error: null, }, ], }, }); await openProject(page); await expect(page.getByText("First criterion.")).toBeVisible(); await expect(page.getByText("Second criterion.")).toBeVisible(); await expect(page.getByText("2 remaining")).toBeVisible(); await expect(page.getByText("Show TODOs")).toBeVisible(); await expect(page.getByText("Another Story")).toBeVisible(); }); });