import { describe, expect, it } from "vitest"; import settings from "../board/__fixtures__/settings.json"; import { groupLinks, isOverdue, todayISO } from "./card.ts"; const types = settings.link_types as Record; const closed = new Set(["Готово", "Снято"]); const out = (k: string, t: string, s: string | null = `Тема ${k}`, st: string | null = "В работе") => ({ target_key: k, link_type: t, summary: s, status: st }); const inn = (k: string, t: string, s: string | null = `Тема ${k}`, st: string | null = "В работе") => ({ source_key: k, link_type: t, summary: s, status: st }); describe("groupLinks", () => { it("блокеры по направлению, связанные без повторов, прочие с направлением; порядок групп и ключей", () => { const g = groupLinks({ links_out: [out("A-10", "ASUPR_Блокер_Завершения"), out("A-2", "ASUPR_Блокер_Начала"), out("A-5", "ASUPR_Связанные"), out("A-7", "Duplicate")], links_in: [inn("A-3", "ASUPR_Блокер_Начала"), inn("A-5", "ASUPR_Связанные"), inn("A-8", "Duplicate", null, null)], }, types); expect(g.map(([id, title]) => [id, title])).toEqual([["depends", "Зависит от"], ["blocks", "Блокирует"], ["related", "Связана с"], ["other", "Прочие"]]); expect(g[0][2]).toEqual([{ key: "A-3", summary: "Тема A-3", status: "В работе", tag: "начало" }]); expect(g[1][2].map((l) => [l.key, l.tag])).toEqual([["A-2", "начало"], ["A-10", "завершение"]]); // ключи как числа expect(g[2][2].map((l) => l.key)).toEqual(["A-5"]); expect(g[3][2]).toEqual([{ key: "A-7", summary: "Тема A-7", status: "В работе", tag: "→ Duplicate" }, { key: "A-8", summary: null, status: null, tag: "← Duplicate" }]); }); it("пустые группы не возвращаются; без link_types всё — «Прочие» с именем Jira", () => { expect(groupLinks({ links_out: [], links_in: [] }, types)).toEqual([]); const g = groupLinks({ links_out: [out("A-2", "ASUPR_Блокер_Начала")], links_in: [] }, {}); expect(g).toEqual([["other", "Прочие", [{ key: "A-2", summary: "Тема A-2", status: "В работе", tag: "→ ASUPR_Блокер_Начала" }]]]); }); it("логический тип из настроек вне трёх известных — «Прочие» с логическим именем", () => { const g = groupLinks({ links_out: [out("A-2", "X_Дубль")], links_in: [] }, { ...types, "дублирует": "X_Дубль" }); expect(g[0][2][0].tag).toBe("→ дублирует"); }); }); describe("просрочка", () => { it("срок прошёл, статус Jira не закрытый — просрочено", () => { expect(isOverdue("2026-10-01", "В работе", closed, "2026-10-17")).toBe(true); expect(isOverdue("2026-10-01", null, closed, "2026-10-17")).toBe(true); }); it("закрытая задача или срок не прошёл — не просрочено", () => { expect(isOverdue("2026-10-01", "Готово", closed, "2026-10-17")).toBe(false); expect(isOverdue("2026-10-01", "Снято", closed, "2026-10-17")).toBe(false); expect(isOverdue("2026-10-17", "В работе", closed, "2026-10-17")).toBe(false); expect(isOverdue("", "В работе", closed, "2026-10-17")).toBe(false); }); it("todayISO — местная дата YYYY-MM-DD", () => { expect(todayISO(new Date(2026, 0, 5, 23, 30))).toBe("2026-01-05"); }); });