191 lines
5.0 KiB
TypeScript
191 lines
5.0 KiB
TypeScript
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();
|
|
});
|
|
});
|