test(FN-4316): complete Step 4 — add todo aging column coverage

Fusion-Task-Id: FN-4316
Fusion-Task-Lineage: 6b2b78b4-effa-479c-9a22-5a53d0f74894
This commit is contained in:
Fusion
2026-05-13 17:57:38 -07:00
committed by gsxdsm
parent c12c462be2
commit e64345393e

View File

@@ -438,7 +438,7 @@ describe("Column same-column drop", () => {
render(<Column {...defaultProps} column="todo" tasks={tasks} onMoveTask={onMoveTask} addToast={addToast} />);
const columnEl = screen.getByText("1").closest(".column") as HTMLElement;
const columnEl = screen.getByRole("heading", { name: "Todo" }).closest(".column") as HTMLElement;
const dataTransfer = {
getData: vi.fn().mockReturnValue("FN-001"),
dropEffect: "move",
@@ -456,7 +456,7 @@ describe("Column same-column drop", () => {
render(<Column {...defaultProps} column="todo" tasks={tasks} onMoveTask={onMoveTask} />);
const columnEl = screen.getByText("1").closest(".column") as HTMLElement;
const columnEl = screen.getByRole("heading", { name: "Todo" }).closest(".column") as HTMLElement;
const dataTransfer = {
getData: vi.fn().mockReturnValue("FN-001"),
dropEffect: "move",
@@ -536,6 +536,90 @@ describe("Column same-column drop", () => {
});
});
describe("Column todo aging summary", () => {
const now = new Date("2026-04-04T12:00:00Z").getTime();
const makeTodoTask = (id: string, ageDays: number): Task => ({
...makeTask(id),
column: "todo",
columnMovedAt: new Date(now - ageDays * 24 * 60 * 60 * 1000).toISOString(),
createdAt: new Date(now - ageDays * 24 * 60 * 60 * 1000).toISOString(),
updatedAt: new Date(now - ageDays * 24 * 60 * 60 * 1000).toISOString(),
});
it.each(["triage", "in-progress", "in-review", "done", "archived"] as const)(
"renders aging summary only on todo column (not %s)",
(column) => {
render(<Column {...defaultProps} column={column} tasks={[{ ...makeTask("FN-001"), column }]} lastFetchTimeMs={now} />);
expect(screen.queryByTestId("todo-aging-summary")).toBeNull();
},
);
it("renders aging summary counts for todo bucket totals", () => {
render(
<Column
{...defaultProps}
column="todo"
tasks={[makeTodoTask("FN-001", 2), makeTodoTask("FN-002", 10), makeTodoTask("FN-003", 45)]}
lastFetchTimeMs={now}
/>,
);
expect(screen.getByTestId("todo-aging-chip-fresh")).toHaveTextContent("07d1");
expect(screen.getByTestId("todo-aging-chip-aging")).toHaveTextContent("830d1");
expect(screen.getByTestId("todo-aging-chip-stale")).toHaveTextContent("31+d1");
});
it("filters todo tasks by selected bucket and toggles off on second click", async () => {
const user = userEvent.setup();
render(
<Column
{...defaultProps}
column="todo"
tasks={[makeTodoTask("FN-001", 2), makeTodoTask("FN-002", 10), makeTodoTask("FN-003", 45)]}
lastFetchTimeMs={now}
/>,
);
await user.click(screen.getByTestId("todo-aging-chip-stale"));
expect(screen.queryByTestId("task-FN-001")).toBeNull();
expect(screen.queryByTestId("task-FN-002")).toBeNull();
expect(screen.getByTestId("task-FN-003")).toBeTruthy();
const columnCount = screen.getByText((_content, node) => {
if (!node || !node.classList.contains("column-count")) {
return false;
}
return node.textContent?.includes("1 / 3") ?? false;
});
expect(columnCount).toBeTruthy();
await user.click(screen.getByTestId("todo-aging-chip-stale"));
expect(screen.getByTestId("task-FN-001")).toBeTruthy();
expect(screen.getByTestId("task-FN-002")).toBeTruthy();
expect(screen.getByTestId("task-FN-003")).toBeTruthy();
});
it("renders empty bucket hint and clear filter action when selected bucket has no tasks", async () => {
const user = userEvent.setup();
render(
<Column
{...defaultProps}
column="todo"
tasks={[makeTodoTask("FN-001", 2), makeTodoTask("FN-002", 8)]}
lastFetchTimeMs={now}
/>,
);
await user.click(screen.getByTestId("todo-aging-chip-stale"));
expect(screen.getByText("No tasks in this bucket")).toBeTruthy();
await user.click(screen.getByRole("button", { name: "Clear filter" }));
expect(screen.queryByText("No tasks in this bucket")).toBeNull();
expect(screen.getByTestId("task-FN-001")).toBeTruthy();
expect(screen.getByTestId("task-FN-002")).toBeTruthy();
});
});
describe("Column PluginSlot integration", () => {
it("renders PluginSlot for board-column-footer", () => {
mockUsePluginUiSlots.mockReturnValue({