Story 28: Show remaining test TODOs in the UI
Add TodoPanel that displays unchecked acceptance criteria from current story files. Backend parses `- [ ]` lines from markdown, frontend shows them in a panel with refresh. Includes 4 Rust unit tests, 3 Vitest tests, 3 Playwright E2E tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
166
frontend/tests/e2e/story-todos.spec.ts
Normal file
166
frontend/tests/e2e/story-todos.spec.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
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.",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
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: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await openProject(page);
|
||||
|
||||
await expect(
|
||||
page.getByText("All acceptance criteria complete."),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText("0 remaining")).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."],
|
||||
},
|
||||
{
|
||||
story_id: "29_another_story",
|
||||
story_name: "Another Story",
|
||||
todos: ["Second criterion."],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -81,6 +81,9 @@ function mockChatApis(
|
||||
page.route("**/api/io/fs/list/absolute**", (route) =>
|
||||
route.fulfill({ json: [] }),
|
||||
),
|
||||
page.route("**/api/workflow/todos", (route) =>
|
||||
route.fulfill({ json: { stories: [] } }),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,9 @@ function mockChatApis(
|
||||
page.route("**/api/io/fs/list/absolute**", (route) =>
|
||||
route.fulfill({ json: [] }),
|
||||
),
|
||||
page.route("**/api/workflow/todos", (route) =>
|
||||
route.fulfill({ json: { stories: [] } }),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user