feat(FN-2441): singularize one-step progress labels

- Update TaskCard step toggle label to render "step" when the unified total is 1
- Update TaskDetailModal completion label to use singular/plural based on total step count
- Add regression tests in TaskCard and TaskDetailModal suites to verify singular labels and reject incorrect plural forms
- Tighten existing completion-count assertions to cover singular/plural text expectations
This commit is contained in:
Fusion
2026-04-24 04:12:36 -07:00
committed by gsxdsm
parent d74295f7dc
commit d0c82714e6
5 changed files with 56 additions and 2 deletions

View File

@@ -113,6 +113,21 @@ describe("TaskCard", () => {
expect(screen.getByText("5 steps")).toBeDefined();
});
it("uses singular step label when unified progress total is one", () => {
render(
<TaskCard
task={makeTask({
steps: [{ name: "Step 0", status: "done" }],
})}
onOpenDetail={noop}
addToast={noop}
/>,
);
expect(screen.getByText("1 step")).toBeDefined();
expect(screen.queryByText("1 steps")).toBeNull();
});
it("renders workflow checks after normal steps with mapped statuses", () => {
const { container } = render(
<TaskCard

View File

@@ -995,7 +995,7 @@ function TaskCardComponent({
aria-expanded={showSteps}
aria-label={showSteps ? "Hide steps" : "Show steps"}
>
<span>{unifiedProgress.total} steps</span>
<span>{unifiedProgress.total} step{unifiedProgress.total === 1 ? "" : "s"}</span>
<ChevronDown
size={14}
className={`card-steps-toggle-icon${showSteps ? " expanded" : ""}`}

View File

@@ -1415,7 +1415,7 @@ export function TaskDetailModal({
))}
</div>
<span className="step-progress-label">
{workingTask.steps.filter(s => s.status === "done").length}/{workingTask.steps.length} steps
{workingTask.steps.filter(s => s.status === "done").length}/{workingTask.steps.length} step{workingTask.steps.length === 1 ? "" : "s"}
</span>
</div>
) : (

View File

@@ -1563,6 +1563,25 @@ describe("TaskCard steps toggle", () => {
expect(toggle.textContent).toContain("2 steps");
});
it("shows singular step label for one-step tasks", () => {
const task = makeTask({
column: "todo",
steps: [{ name: "Only step", status: "pending" }],
});
render(
<TaskCard
task={task}
onOpenDetail={vi.fn()}
addToast={noopToast}
/>
);
const toggle = screen.getByRole("button", { name: /Show steps/i });
expect(toggle.textContent).toContain("1 step");
expect(toggle.textContent).not.toContain("1 steps");
});
it("clicking toggle expands and shows step list", () => {
// Use 'todo' column to test default collapsed behavior
const task = makeTask({

View File

@@ -2220,6 +2220,25 @@ describe("TaskDetailModal", () => {
expect((segments[3] as HTMLElement).style.backgroundColor).toBe("var(--text-dim, #484f58)");
});
it("displays singular completion label for one-step tasks", () => {
render(
<TaskDetailModal
task={makeTask({
steps: [{ name: "Step 1", status: "done" }],
})}
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
addToast={noop}
/>,
);
expect(screen.getByText("1/1 step")).toBeTruthy();
expect(screen.queryByText("1/1 steps")).toBeNull();
});
it("displays correct completion count", () => {
render(
<TaskDetailModal
@@ -2241,6 +2260,7 @@ describe("TaskDetailModal", () => {
);
expect(screen.getByText("2/4 steps")).toBeTruthy();
expect(screen.queryByText("2/4 step")).toBeNull();
});
it("has data-tooltip attribute with step name and status on each segment", () => {