/** * Unit tests for dutyList (dutyTimelineCardHtml, dutyItemHtml, contact rendering). */ import { describe, it, expect, beforeAll, vi, afterEach } from "vitest"; import * as dateUtils from "./dateUtils.js"; import { dutyTimelineCardHtml, dutyItemHtml } from "./dutyList.js"; describe("dutyList", () => { beforeAll(() => { document.body.innerHTML = '
' + '
' + '
' + ''; }); describe("dutyTimelineCardHtml", () => { it("renders duty with full_name and time range (no flip when no contacts)", () => { const d = { event_type: "duty", full_name: "Иванов", start_at: "2025-02-25T09:00:00", end_at: "2025-02-25T18:00:00", }; const html = dutyTimelineCardHtml(d, false); expect(html).toContain("Иванов"); expect(html).toContain("duty-item"); expect(html).toContain("duty-timeline-card"); expect(html).not.toContain("duty-flip-card"); expect(html).not.toContain("duty-flip-btn"); }); it("uses flip-card wrapper with front and back when phone or username present", () => { const d = { event_type: "duty", full_name: "Alice", start_at: "2025-03-01T09:00:00", end_at: "2025-03-01T17:00:00", phone: "+79991234567", username: "alice_dev", }; const html = dutyTimelineCardHtml(d, false); expect(html).toContain("Alice"); expect(html).toContain("duty-flip-card"); expect(html).toContain("duty-flip-inner"); expect(html).toContain("duty-flip-front"); expect(html).toContain("duty-flip-back"); expect(html).toContain("duty-flip-btn"); expect(html).toContain('data-flipped="false"'); expect(html).toContain("duty-contact-row"); expect(html).toContain('href="tel:'); expect(html).toContain("+79991234567"); expect(html).toContain("https://t.me/"); expect(html).toContain("alice_dev"); }); it("front face contains name and time; back face contains contact links", () => { const d = { event_type: "duty", full_name: "Bob", start_at: "2025-03-02T08:00:00", end_at: "2025-03-02T16:00:00", phone: "+79001112233", }; const html = dutyTimelineCardHtml(d, false); const frontStart = html.indexOf("duty-flip-front"); const backStart = html.indexOf("duty-flip-back"); const frontSection = html.slice(frontStart, backStart); const backSection = html.slice(backStart); expect(frontSection).toContain("Bob"); expect(frontSection).toContain("time"); expect(frontSection).not.toContain("duty-contact-row"); expect(backSection).toContain("Bob"); expect(backSection).toContain("duty-contact-row"); expect(backSection).toContain("tel:"); }); it("omits flip wrapper and button when phone and username are missing", () => { const d = { event_type: "duty", full_name: "Bob", start_at: "2025-03-02T08:00:00", end_at: "2025-03-02T16:00:00", }; const html = dutyTimelineCardHtml(d, false); expect(html).toContain("Bob"); expect(html).not.toContain("duty-flip-card"); expect(html).not.toContain("duty-flip-btn"); expect(html).not.toContain("duty-contact-row"); }); }); describe("dutyItemHtml", () => { afterEach(() => { vi.restoreAllMocks(); }); it("escapes timeOrRange so HTML special chars are not rendered raw", () => { vi.spyOn(dateUtils, "formatHHMM").mockReturnValue("12:00 & 13:00"); vi.spyOn(dateUtils, "formatDateKey").mockReturnValue("01.02.2025"); const d = { event_type: "duty", full_name: "Test", start_at: "2025-03-01T12:00:00", end_at: "2025-03-01T13:00:00", }; const html = dutyItemHtml(d, null, false); expect(html).toContain("&"); expect(html).not.toContain('
12:00 & 13:00'); }); it("uses typeLabelOverride when provided", () => { const d = { event_type: "duty", full_name: "Alice", start_at: "2025-03-01T09:00:00", end_at: "2025-03-01T17:00:00", }; const html = dutyItemHtml(d, "On duty now", false); expect(html).toContain("On duty now"); expect(html).toContain("Alice"); }); it("shows duty.until when showUntilEnd is true for duty", () => { const d = { event_type: "duty", full_name: "Bob", start_at: "2025-03-01T09:00:00", end_at: "2025-03-01T17:00:00", }; const html = dutyItemHtml(d, null, true); expect(html).toMatch(/until|до/); expect(html).toMatch(/\d{2}:\d{2}/); }); it("renders vacation with date range", () => { vi.spyOn(dateUtils, "formatDateKey") .mockReturnValueOnce("01.03") .mockReturnValueOnce("05.03"); const d = { event_type: "vacation", full_name: "Charlie", start_at: "2025-03-01T00:00:00", end_at: "2025-03-05T23:59:59", }; const html = dutyItemHtml(d); expect(html).toContain("01.03 – 05.03"); expect(html).toContain("duty-item--vacation"); }); it("applies extraClass to container", () => { const d = { event_type: "duty", full_name: "Dana", start_at: "2025-03-01T09:00:00", end_at: "2025-03-01T17:00:00", }; const html = dutyItemHtml(d, null, false, "duty-item--current"); expect(html).toContain("duty-item--current"); expect(html).toContain("Dana"); }); }); });