// frontend/tests/tasks/tree.test.ts import { describe, expect, it } from "vitest"; import { buildIndex, dueLabel, editTree, findPath, isSoon, pct, stat, } from "@/lib/tasks/tree"; import type { Folder, Task } from "@/lib/types"; function t(id: string, status: Task["status"], children: Task[] = [], due: string | null = null): Task { return { id, project_id: "biz-report", parent_id: null, title: id, status, assignee_id: "jiwoo", due, prio: "보통", notes: "", est: "", delegated: false, sort_order: 0, created_at: "", updated_at: "", comments: [], children, }; } describe("tree utils", () => { it("findPath 무한 중첩 경로", () => { const tree = [t("k1", "doing", [t("kx7", "doing", [t("kx9", "todo")])])]; const p = findPath(tree, "kx9"); expect(p?.map((n) => n.id)).toEqual(["k1", "kx7", "kx9"]); }); it("editTree 깊은 노드 갱신 (형제 불변)", () => { const tree = [t("k1", "doing", [t("a", "todo"), t("b", "todo")])]; const next = editTree(tree, "b", (n) => ({ ...n, status: "done" })); expect(next[0].children[0].status).toBe("todo"); // a 불변 expect(next[0].children[1].status).toBe("done"); // b 변경 }); it("stat/pct 진행률", () => { const node = t("k", "doing", [t("a", "done"), t("b", "done"), t("c", "todo")]); expect(stat(node)).toEqual({ total: 3, done: 2 }); expect(pct(node)).toBe(67); }); it("dueLabel ISO → 6/8", () => { expect(dueLabel("2026-06-08")).toBe("6/8"); expect(dueLabel(null)).toBe("—"); }); it("isSoon: due<=8 && !done", () => { expect(isSoon(t("a", "todo", [], "2026-06-08"))).toBe(true); expect(isSoon(t("a", "done", [], "2026-06-08"))).toBe(false); expect(isSoon(t("a", "todo", [], "2026-06-20"))).toBe(false); }); it("buildIndex isDescendant 필터", () => { const folders: Folder[] = [ { id: "work", name: "업무", tone: "ink", icon: "folder", sort_order: 0, is_system: true, projects: [ { id: "biz", folder_id: "work", parent_id: null, name: "경영", tone: "coral", sort_order: 0, pinned: false, task_count: 0, children: [ { id: "biz-report", folder_id: "work", parent_id: "biz", name: "리포트", tone: "coral", sort_order: 0, pinned: false, task_count: 0, children: [], }, ], }, ], }, ]; const ix = buildIndex(folders); expect(ix.isDescendant("biz-report", "work")).toBe(true); expect(ix.isDescendant("biz-report", "biz")).toBe(true); expect(ix.isDescendant("biz", "biz-report")).toBe(false); expect(ix.pathOf("biz-report").map((r) => r.id)).toEqual(["work", "biz", "biz-report"]); }); });