sdlc-hub/web/src/modules/jira/agile/model.test.ts
Andrey Epifancev 815cd3885e feat(web): значки типов по образцу Jira и формат SP (#112, M-6)
- 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>
2026-10-25 20:18:37 +04:00

56 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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");
});
});