/** * Unit tests for getDutyMarkerRows and buildDutyItemTimePrefix logic. * Covers: sorting order preservation, idx=0 with total>1 and startSameDay. * Also tests dismissHint helper. */ import { describe, it, expect, beforeAll, beforeEach, afterEach, vi } from "vitest"; import { getDutyMarkerRows, dismissHint } from "./hints.js"; const FROM = "from"; const TO = "until"; const SEP = "\u00a0"; describe("getDutyMarkerRows", () => { beforeAll(() => { document.body.innerHTML = '
'; }); it("preserves input order (caller must sort by start_at before passing)", () => { const hintDay = "2025-02-25"; const duties = [ { full_name: "Иванов", start_at: "2025-02-25T14:00:00", end_at: "2025-02-25T18:00:00", }, { full_name: "Петров", start_at: "2025-02-25T09:00:00", end_at: "2025-02-25T14:00:00", }, ]; const rows = getDutyMarkerRows(duties, hintDay, SEP, FROM, TO); expect(rows).toHaveLength(2); expect(rows[0].fullName).toBe("Иванов"); expect(rows[1].fullName).toBe("Петров"); }); it("first of multiple with startSameDay shows full range (from HH:MM to HH:MM)", () => { const hintDay = "2025-02-25"; const duties = [ { full_name: "Иванов", start_at: "2025-02-25T09:00:00", end_at: "2025-02-25T14:00:00", }, { full_name: "Петров", start_at: "2025-02-25T14:00:00", end_at: "2025-02-25T18:00:00", }, ].sort((a, b) => new Date(a.start_at) - new Date(b.start_at)); const rows = getDutyMarkerRows(duties, hintDay, SEP, FROM, TO); expect(rows).toHaveLength(2); expect(rows[0].fullName).toBe("Иванов"); expect(rows[0].timePrefix).toContain("09:00"); expect(rows[0].timePrefix).toContain("14:00"); expect(rows[0].timePrefix).toContain(FROM); expect(rows[0].timePrefix).toContain(TO); }); it("first of multiple continuation from previous day shows only end time", () => { const hintDay = "2025-02-25"; const duties = [ { full_name: "Иванов", start_at: "2025-02-24T22:00:00", end_at: "2025-02-25T06:00:00", }, { full_name: "Петров", start_at: "2025-02-25T09:00:00", end_at: "2025-02-25T14:00:00", }, ].sort((a, b) => new Date(a.start_at) - new Date(b.start_at)); const rows = getDutyMarkerRows(duties, hintDay, SEP, FROM, TO); expect(rows).toHaveLength(2); expect(rows[0].fullName).toBe("Иванов"); expect(rows[0].timePrefix).not.toContain(FROM); expect(rows[0].timePrefix).toContain(TO); expect(rows[0].timePrefix).toContain("06:00"); }); it("second duty continuation from previous day shows only end time (to HH:MM)", () => { const hintDay = "2025-02-23"; const duties = [ { full_name: "A", start_at: "2025-02-23T00:00:00", end_at: "2025-02-23T09:00:00", }, { full_name: "B", start_at: "2025-02-22T09:00:00", end_at: "2025-02-23T09:00:00", }, ]; const rows = getDutyMarkerRows(duties, hintDay, SEP, FROM, TO); expect(rows).toHaveLength(2); expect(rows[0].fullName).toBe("A"); expect(rows[0].timePrefix).toContain(FROM); expect(rows[0].timePrefix).toContain("00:00"); expect(rows[0].timePrefix).toContain(TO); expect(rows[0].timePrefix).toContain("09:00"); expect(rows[1].fullName).toBe("B"); expect(rows[1].timePrefix).not.toContain(FROM); expect(rows[1].timePrefix).toContain(TO); expect(rows[1].timePrefix).toContain("09:00"); }); it("multiple duties in one day — correct order when input is pre-sorted", () => { const hintDay = "2025-02-25"; const duties = [ { full_name: "A", start_at: "2025-02-25T09:00:00", end_at: "2025-02-25T12:00:00" }, { full_name: "B", start_at: "2025-02-25T12:00:00", end_at: "2025-02-25T15:00:00" }, { full_name: "C", start_at: "2025-02-25T15:00:00", end_at: "2025-02-25T18:00:00" }, ].sort((a, b) => new Date(a.start_at) - new Date(b.start_at)); const rows = getDutyMarkerRows(duties, hintDay, SEP, FROM, TO); expect(rows.map((r) => r.fullName)).toEqual(["A", "B", "C"]); expect(rows[0].timePrefix).toContain("09:00"); expect(rows[1].timePrefix).toContain("12:00"); expect(rows[2].timePrefix).toContain("15:00"); }); }); describe("dismissHint", () => { beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it("removes visible class immediately and hides element after delay", () => { const el = document.createElement("div"); el.classList.add("calendar-event-hint--visible"); el.hidden = false; el.setAttribute("data-active", "1"); dismissHint(el); expect(el.classList.contains("calendar-event-hint--visible")).toBe(false); expect(el.hidden).toBe(false); vi.advanceTimersByTime(150); expect(el.hidden).toBe(true); expect(el.hasAttribute("data-active")).toBe(false); }); it("returns timeout id usable with clearTimeout", () => { const el = document.createElement("div"); const id = dismissHint(el); expect(id).toBeDefined(); clearTimeout(id); vi.advanceTimersByTime(150); expect(el.hidden).toBe(false); }); it("calls afterHide callback after delay when provided", () => { const el = document.createElement("div"); let called = false; dismissHint(el, { afterHide: () => { called = true; }, }); expect(called).toBe(false); vi.advanceTimersByTime(150); expect(called).toBe(true); }); });