Story 31: View Upcoming Stories

Add GET /workflow/upcoming endpoint that reads .story_kit/stories/upcoming/
and returns story IDs with names parsed from frontmatter. Add UpcomingPanel
component wired into Chat view with loading, error, empty, and list states.

12 new tests (3 backend, 9 frontend) all passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dave
2026-02-19 15:51:12 +00:00
parent 644644d5b3
commit 939387104b
12 changed files with 505 additions and 18 deletions

View File

@@ -9,8 +9,11 @@ const baseProps = {
gateStatusColor: "#aaa",
isGateLoading: false,
gateError: null,
coverageError: null,
lastGateRefresh: null,
onRefresh: vi.fn(),
onCollectCoverage: vi.fn(),
isCollectingCoverage: false,
};
describe("GatePanel", () => {
@@ -21,9 +24,7 @@ describe("GatePanel", () => {
it("shows loading message when isGateLoading is true", () => {
render(<GatePanel {...baseProps} isGateLoading={true} />);
expect(
screen.getByText("Loading workflow gates..."),
).toBeInTheDocument();
expect(screen.getByText("Loading workflow gates...")).toBeInTheDocument();
});
it("shows error with retry button", async () => {
@@ -64,13 +65,12 @@ describe("GatePanel", () => {
warning: null,
summary: { total: 5, passed: 5, failed: 0 },
missingCategories: [],
coverageReport: null,
}}
gateStatusLabel="Ready to accept"
/>,
);
expect(
screen.getByText(/5\/5 passing, 0 failing/),
).toBeInTheDocument();
expect(screen.getByText(/5\/5 passing, 0 failing/)).toBeInTheDocument();
});
it("shows missing categories", () => {
@@ -83,12 +83,11 @@ describe("GatePanel", () => {
warning: null,
summary: { total: 0, passed: 0, failed: 0 },
missingCategories: ["unit", "integration"],
coverageReport: null,
}}
/>,
);
expect(
screen.getByText("Missing: unit, integration"),
).toBeInTheDocument();
expect(screen.getByText("Missing: unit, integration")).toBeInTheDocument();
});
it("shows warning text", () => {
@@ -101,6 +100,7 @@ describe("GatePanel", () => {
warning: "Multiple tests failing — fix one at a time.",
summary: { total: 4, passed: 2, failed: 2 },
missingCategories: [],
coverageReport: null,
}}
/>,
);
@@ -119,12 +119,11 @@ describe("GatePanel", () => {
warning: null,
summary: { total: 2, passed: 1, failed: 1 },
missingCategories: [],
coverageReport: null,
}}
/>,
);
expect(
screen.getByText("No approved test plan."),
).toBeInTheDocument();
expect(screen.getByText("No approved test plan.")).toBeInTheDocument();
expect(screen.getByText("Tests are failing.")).toBeInTheDocument();
});
@@ -132,9 +131,7 @@ describe("GatePanel", () => {
const onRefresh = vi.fn();
render(<GatePanel {...baseProps} onRefresh={onRefresh} />);
await userEvent.click(
screen.getByRole("button", { name: "Refresh" }),
);
await userEvent.click(screen.getByRole("button", { name: "Refresh" }));
expect(onRefresh).toHaveBeenCalledOnce();
});