import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; import type { BoardSnapshot } from "../../api/types.ts"; import { canon } from "../../../../test/canon.ts"; import { dictsFromSettings } from "../dicts.ts"; import settings from "../__fixtures__/settings.json"; import { parseCsv } from "./csv.ts"; import { fromCsv } from "./fromCsv.ts"; import { fromHub } from "./fromHub.ts"; import { fromJson } from "./fromJson.ts"; const fx = (name: string) => readFileSync(join(import.meta.dirname, "..", "__fixtures__", name), "utf8"); const golden = (name: string) => JSON.parse(fx(name)); const { dicts } = dictsFromSettings(settings); describe("модель доски = links.html@3931a88", () => { it("fromHub — снимок с эпиками, соседями, заглушками", () => { const snap = JSON.parse(fx("hub-basic.board.json")) as BoardSnapshot; expect(canon(fromHub(snap, dicts))).toEqual(golden("hub-basic.expected.json").model); }); it("fromHub — пустое зеркало без полного прогона", () => { const snap = JSON.parse(fx("hub-empty.board.json")) as BoardSnapshot; // эталон написан, когда fromHub всегда давал hasSprint: true; без своих спринтов фильтр спринтов не показывается expect(canon(fromHub(snap, dicts))).toEqual({ ...golden("hub-empty.expected.json").model, hasSprint: false }); }); it("T9: fromHub — спринта у своих задач нет (есть только у соседа) → hasSprint false", () => { const snap = JSON.parse(fx("hub-basic.board.json")) as BoardSnapshot; const none = { ...snap, issues: snap.issues.map((i) => ({ ...i, sprint: null })) }; expect(fromHub(none, dicts).hasSprint).toBe(false); const neighborOnly = { ...snap, issues: snap.issues.map((i) => ({ ...i, sprint: i.neighbor ? i.sprint : null })) }; expect(fromHub(neighborOnly, dicts).hasSprint).toBe(false); expect(fromHub(snap, dicts).hasSprint).toBe(true); }); it("hasSprint: спринт только у эпика → false", () => { const snap = JSON.parse(fx("hub-basic.board.json")) as BoardSnapshot; const onlyEpic = { ...snap, issues: snap.issues.map((i) => ({ ...i, sprint: i.key === "AURORA-9100" ? "AWPCA_Спринт 1" : null })) }; expect(fromHub(onlyEpic, dicts).hasSprint).toBe(false); }); it("parseCsv и fromCsv — BOM, CRLF, кавычки, повторные колонки, даты разных локалей", () => { const text = fx("jira-export.csv"); const g = golden("jira-export.expected.json"); expect(parseCsv(text)).toEqual(g.rows); expect(canon(fromCsv(text, "jira-export.csv", dicts))).toEqual(g.model); }); it("fromCsv без колонок связей, спринта и эпика — проблемы файла", () => { expect(canon(fromCsv(fx("jira-no-links.csv"), "jira-no-links.csv", dicts))).toEqual(golden("jira-no-links.expected.json").model); }); it("fromCsv без ключа и темы — та же ошибка", () => { expect(() => fromCsv(fx("jira-bad.csv"), "jira-bad.csv", dicts)).toThrow(golden("jira-bad.expected.json").error); }); it("fromJson — выгрузка старой страницы", () => { expect(canon(fromJson(fx("old-board.json"), "old-board.json", dicts))).toEqual(golden("old-board.expected.json").import); }); it("fromJson — не JSON и JSON без nodes", () => { expect(() => fromJson("{", "x.json", dicts)).toThrow("это не JSON"); expect(() => fromJson("{}", "x.json", dicts)).toThrow("в JSON нет списка nodes"); }); });