- model.ts: fmtSp округляет до двух знаков (M-6); typeKind и isEpic по именам типов Jira без учёта регистра - TypeIcon: квадрат 16 px, вид по typeKind, aria-label — исходное имя типа, без типа — «Тип не указан» - icons.tsx: IconBookmark, IconCircle, IconSquares, IconDot; IconProps.size добавляет 12 - agile.css: фон значка — токены дизайн-системы, символ --color-text-on-primary Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
56 lines
3.3 KiB
TypeScript
56 lines
3.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import type { AgileBoard, AgileResponse } from "../api/types.ts";
|
||
import { boardState, daysLeft, fmtDay, fmtRange, fmtSp, isEpic, jiraDate, tasksWord, typeKind } from "./model.ts";
|
||
|
||
const resp = (over: Partial<AgileBoard>): AgileResponse => ({
|
||
board: { id: 4216, type: "scrum", columns: [{ name: "Готово", statuses: ["Готово"] }], kanban_backlog: null, fetched_at: "2026-10-24T10:00:00+00:00", ...over },
|
||
sprints: [], backlog: [], board_issues: null,
|
||
});
|
||
|
||
describe("даты и числа экранов доски", () => {
|
||
it("время Jira с ±HHMM и ±HH:MM — один день", () => { // Review Focus 1
|
||
expect(fmtDay("2026-10-14T09:00:00.000+0300")).toBe(fmtDay("2026-10-14T09:00:00.000+03:00"));
|
||
expect(fmtDay("2026-10-14T09:00:00.000+0300")).toMatch(/^\d{2}\.\d{2}$/);
|
||
expect(fmtDay(null)).toBe("—"); expect(fmtDay("мусор")).toBe("—");
|
||
});
|
||
it("jiraDate и fmtRange", () => {
|
||
expect(jiraDate("2026-10-14T09:00:00.000+0300")?.getTime()).toBe(Date.parse("2026-10-14T09:00:00.000+03:00"));
|
||
expect(jiraDate("мусор")).toBeNull();
|
||
expect(fmtRange(null, null)).toBe("—–—");
|
||
expect(fmtRange("2026-10-14T09:00:00.000+0300", "2026-10-28T09:00:00.000+0300")).toMatch(/^\d{2}\.\d{2}–\d{2}\.\d{2}$/);
|
||
});
|
||
it("daysLeft — календарные дни до конца", () => {
|
||
const now = new Date(2026, 9, 24, 23, 30);
|
||
expect(daysLeft(new Date(2026, 9, 27, 1, 0).toISOString(), now)).toBe(3);
|
||
expect(daysLeft(new Date(2026, 9, 23, 12, 0).toISOString(), now)).toBe(-1);
|
||
expect(daysLeft(null, now)).toBeNull();
|
||
});
|
||
it("fmtSp, tasksWord", () => {
|
||
expect([fmtSp(null), fmtSp(3), fmtSp(2.5)]).toEqual(["—", "3", "2.5"]);
|
||
expect([1, 2, 5, 11, 21, 73].map(tasksWord)).toEqual(["задача", "задачи", "задач", "задач", "задача", "задачи"]);
|
||
});
|
||
it("fmtSp — до двух знаков без хвостов (M-6)", () => {
|
||
expect([fmtSp(0.1 + 0.2), fmtSp(1 / 3), fmtSp(0)]).toEqual(["0.3", "0.33", "0"]);
|
||
});
|
||
it("typeKind — имена типов Jira, без учёта регистра", () => {
|
||
const kinds = (types: (string | null)[]) => types.map(typeKind);
|
||
expect(kinds(["Эпик", "epic", "Фича"])).toEqual(["epic", "epic", "epic"]);
|
||
expect(kinds(["История", "Story"])).toEqual(["story", "story"]);
|
||
expect(kinds(["Задача", "Task"])).toEqual(["task", "task"]);
|
||
expect(kinds(["Ошибка", "BUG"])).toEqual(["bug", "bug"]);
|
||
expect(kinds(["Подзадача", "Sub-task", "Subtask"])).toEqual(["subtask", "subtask", "subtask"]);
|
||
expect(kinds([null, "", "Риск"])).toEqual(["other", "other", "other"]);
|
||
});
|
||
it("isEpic — по виду типа", () => {
|
||
expect(isEpic({ issue_type: "Фича" })).toBe(true);
|
||
expect(isEpic({ issue_type: null })).toBe(false);
|
||
expect(isEpic({ issue_type: "История" })).toBe(false);
|
||
});
|
||
it("boardState — три состояния (design #23)", () => {
|
||
expect(boardState({ board: null, sprints: [], backlog: null, board_issues: null })).toBe("no-data");
|
||
expect(boardState(resp({ fetched_at: null, columns: null }))).toBe("no-data");
|
||
expect(boardState(resp({ fetched_at: "2026-10-24T10:00:00+00:00", columns: null }))).toBe("no-columns");
|
||
expect(boardState(resp({}))).toBe("ok");
|
||
});
|
||
});
|