import { describe, expect, it } from "vitest"; import { formatTaskProgress } from "./taskProgress"; import type { TaskListItem } from "./types"; function makeTask( overrides: Partial & Pick, ): TaskListItem { return { id: "task-a", goal: null, workflow_definition_id: null, assigned_device_id: null, assigned_host_id: null, attempt_count: 0, failure_reason: null, target_host_id: null, target_device_id: null, created_at: "2026-07-14T00:00:00+00:00", progress_step_index: null, progress_step_status: null, progress_summary: null, progress_updated_at: null, ...overrides, }; } describe("formatTaskProgress", () => { it("renders step index, status, and summary for an in-progress dispatched task", () => { const task = makeTask({ status: "dispatched", progress_step_index: 2, progress_step_status: "running", progress_summary: "tapping button", }); expect(formatTaskProgress(task)).toBe("step 2 \u2014 running: tapping button"); }); it("renders step index and status when summary is missing", () => { const task = makeTask({ status: "assigned", progress_step_index: 0, progress_step_status: "starting", progress_summary: null, }); expect(formatTaskProgress(task)).toBe("step 0 \u2014 starting"); }); it("returns null when the task is dispatched but no progress has been reported", () => { const task = makeTask({ status: "dispatched", progress_step_index: null, progress_step_status: null, progress_summary: null, }); expect(formatTaskProgress(task)).toBeNull(); }); it("returns null for a dispatched task with undefined progress fields", () => { const task = makeTask({ status: "dispatched" }); // Fields default to null via makeTask; simulate the undefined case. delete (task as Partial).progress_step_index; delete (task as Partial).progress_step_status; expect(formatTaskProgress(task)).toBeNull(); }); it("hides stale progress when the task has reached a terminal status (done)", () => { const task = makeTask({ status: "done", progress_step_index: 5, progress_step_status: "running", progress_summary: "stale snapshot before terminal", }); expect(formatTaskProgress(task)).toBeNull(); }); it("hides stale progress when the task has reached a terminal status (failed)", () => { const task = makeTask({ status: "failed", progress_step_index: 3, progress_step_status: "running", progress_summary: "stale snapshot before failure", }); expect(formatTaskProgress(task)).toBeNull(); }); it("hides progress for a queued task that has not started executing", () => { const task = makeTask({ status: "queued", progress_step_index: 1, progress_step_status: "running", progress_summary: "should not show while queued", }); expect(formatTaskProgress(task)).toBeNull(); }); it("ignores a missing step status even when step index is present", () => { const task = makeTask({ status: "dispatched", progress_step_index: 1, progress_step_status: null, progress_summary: "partial snapshot", }); expect(formatTaskProgress(task)).toBeNull(); }); it("truncates a long summary with an ellipsis", () => { const longSummary = "x".repeat(200); const task = makeTask({ status: "dispatched", progress_step_index: 1, progress_step_status: "running", progress_summary: longSummary, }); const formatted = formatTaskProgress(task); expect(formatted).not.toBeNull(); const summaryPart = formatted!.split(": ").slice(1).join(": "); expect(summaryPart.length).toBeLessThan(longSummary.length); expect(summaryPart.endsWith("\u2026")).toBe(true); }); it("trims surrounding whitespace before truncating or rendering", () => { const task = makeTask({ status: "dispatched", progress_step_index: 1, progress_step_status: "running", progress_summary: " hello world ", }); expect(formatTaskProgress(task)).toBe("step 1 \u2014 running: hello world"); }); });