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:
Dave
2026-02-19 15:33:45 +00:00
parent 644644d5b3
commit 2c3003d721
13 changed files with 606 additions and 17 deletions

View File

@@ -35,6 +35,7 @@ vi.mock("../api/workflow", () => {
ensureAcceptance: vi.fn(),
recordCoverage: vi.fn(),
collectCoverage: vi.fn(),
getStoryTodos: vi.fn(),
},
};
});
@@ -54,6 +55,7 @@ const mockedWorkflow = {
getReviewQueue: vi.mocked(workflowApi.getReviewQueue),
getReviewQueueAll: vi.mocked(workflowApi.getReviewQueueAll),
ensureAcceptance: vi.mocked(workflowApi.ensureAcceptance),
getStoryTodos: vi.mocked(workflowApi.getStoryTodos),
};
describe("Chat review panel", () => {
@@ -75,6 +77,7 @@ describe("Chat review panel", () => {
});
mockedWorkflow.getReviewQueueAll.mockResolvedValue({ stories: [] });
mockedWorkflow.ensureAcceptance.mockResolvedValue(true);
mockedWorkflow.getStoryTodos.mockResolvedValue({ stories: [] });
});
it("shows an empty review queue state", async () => {
@@ -510,4 +513,57 @@ describe("Chat review panel", () => {
expect(await screen.findByText(/Coverage: 85\.0%/)).toBeInTheDocument();
});
it("shows story TODOs when unchecked criteria exist", async () => {
mockedWorkflow.getStoryTodos.mockResolvedValueOnce({
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.",
],
},
],
});
render(<Chat projectPath="/tmp/project" onCloseProject={vi.fn()} />);
expect(
await screen.findByText("The UI lists unchecked acceptance criteria."),
).toBeInTheDocument();
expect(
await screen.findByText("Each TODO is displayed as its full text."),
).toBeInTheDocument();
expect(await screen.findByText("2 remaining")).toBeInTheDocument();
});
it("shows completion message when all criteria are checked", async () => {
mockedWorkflow.getStoryTodos.mockResolvedValueOnce({
stories: [
{
story_id: "28_ui_show_test_todos",
story_name: "Show Remaining Test TODOs in the UI",
todos: [],
},
],
});
render(<Chat projectPath="/tmp/project" onCloseProject={vi.fn()} />);
expect(
await screen.findByText("All acceptance criteria complete."),
).toBeInTheDocument();
});
it("shows TODO error when endpoint fails", async () => {
mockedWorkflow.getStoryTodos.mockRejectedValueOnce(
new Error("Cannot read stories"),
);
render(<Chat projectPath="/tmp/project" onCloseProject={vi.fn()} />);
expect(await screen.findByText("Cannot read stories")).toBeInTheDocument();
});
});