Files
duty-teller/webapp/js/dateUtils.test.js
Nikolay Tatarinov 2fb553567f
Some checks failed
CI / lint-and-test (push) Failing after 45s
feat: enhance CI workflow and update webapp styles
- Added Node.js setup and webapp testing steps to the CI workflow for improved integration.
- Updated HTML to link multiple CSS files for better modularity and organization of styles.
- Removed deprecated `style.css` and introduced new CSS files for base styles, calendar, day detail, hints, markers, states, and duty list to enhance maintainability and readability.
- Implemented new styles for improved presentation of duty information and user interactions.
- Added unit tests for new API functions and contact link rendering to ensure functionality and reliability.
2026-03-02 17:20:33 +03:00

231 lines
6.6 KiB
JavaScript

/**
* Unit tests for dateUtils: localDateString, dutyOverlapsLocalDay,
* dutyOverlapsLocalRange, getMonday, formatHHMM.
*/
import { describe, it, expect } from "vitest";
import {
localDateString,
dutyOverlapsLocalDay,
dutyOverlapsLocalRange,
getMonday,
formatHHMM,
firstDayOfMonth,
lastDayOfMonth,
formatDateKey,
dateKeyToDDMM,
} from "./dateUtils.js";
describe("localDateString", () => {
it("formats date as YYYY-MM-DD", () => {
const d = new Date(2025, 0, 15);
expect(localDateString(d)).toBe("2025-01-15");
});
it("pads month and day with zero", () => {
expect(localDateString(new Date(2025, 0, 5))).toBe("2025-01-05");
expect(localDateString(new Date(2025, 8, 9))).toBe("2025-09-09");
});
it("handles December and year boundary", () => {
expect(localDateString(new Date(2024, 11, 31))).toBe("2024-12-31");
});
});
describe("dutyOverlapsLocalDay", () => {
it("returns true when duty spans the whole day", () => {
const d = {
start_at: "2025-02-25T00:00:00Z",
end_at: "2025-02-25T23:59:59Z",
};
expect(dutyOverlapsLocalDay(d, "2025-02-25")).toBe(true);
});
it("returns true when duty overlaps part of the day", () => {
const d = {
start_at: "2025-02-25T09:00:00Z",
end_at: "2025-02-25T14:00:00Z",
};
expect(dutyOverlapsLocalDay(d, "2025-02-25")).toBe(true);
});
it("returns true when duty continues from previous day", () => {
const d = {
start_at: "2025-02-24T22:00:00Z",
end_at: "2025-02-25T06:00:00Z",
};
expect(dutyOverlapsLocalDay(d, "2025-02-25")).toBe(true);
});
it("returns false when duty ends before the day", () => {
const d = {
start_at: "2025-02-24T09:00:00Z",
end_at: "2025-02-24T18:00:00Z",
};
expect(dutyOverlapsLocalDay(d, "2025-02-25")).toBe(false);
});
it("returns false when duty starts after the day", () => {
const d = {
start_at: "2025-02-26T09:00:00Z",
end_at: "2025-02-26T18:00:00Z",
};
expect(dutyOverlapsLocalDay(d, "2025-02-25")).toBe(false);
});
});
describe("dutyOverlapsLocalRange", () => {
it("returns true when duty overlaps the range", () => {
const d = {
start_at: "2025-02-24T12:00:00Z",
end_at: "2025-02-26T12:00:00Z",
};
expect(dutyOverlapsLocalRange(d, "2025-02-25", "2025-02-28")).toBe(true);
});
it("returns true when duty is entirely inside the range", () => {
const d = {
start_at: "2025-02-26T09:00:00Z",
end_at: "2025-02-26T18:00:00Z",
};
expect(dutyOverlapsLocalRange(d, "2025-02-25", "2025-02-28")).toBe(true);
});
it("returns false when duty ends before range start", () => {
const d = {
start_at: "2025-02-20T09:00:00Z",
end_at: "2025-02-22T18:00:00Z",
};
expect(dutyOverlapsLocalRange(d, "2025-02-25", "2025-02-28")).toBe(false);
});
it("returns false when duty starts after range end", () => {
const d = {
start_at: "2025-03-01T09:00:00Z",
end_at: "2025-03-01T18:00:00Z",
};
expect(dutyOverlapsLocalRange(d, "2025-02-25", "2025-02-28")).toBe(false);
});
});
describe("getMonday", () => {
it("returns same day when date is Monday", () => {
const monday = new Date(2025, 0, 6); // 6 Jan 2025 is Monday
const result = getMonday(monday);
expect(result.getFullYear()).toBe(2025);
expect(result.getMonth()).toBe(0);
expect(result.getDate()).toBe(6);
expect(result.getDay()).toBe(1);
});
it("returns previous Monday for Wednesday", () => {
const wed = new Date(2025, 0, 8);
const result = getMonday(wed);
expect(result.getDay()).toBe(1);
expect(result.getDate()).toBe(6);
});
it("returns Monday of same week for Sunday", () => {
const sun = new Date(2025, 0, 12);
const result = getMonday(sun);
expect(result.getDay()).toBe(1);
expect(result.getDate()).toBe(6);
});
});
describe("formatHHMM", () => {
it("formats ISO string as HH:MM in local time", () => {
const s = "2025-02-25T14:30:00Z";
const result = formatHHMM(s);
expect(result).toMatch(/^\d{2}:\d{2}$/);
const d = new Date(s);
const expected = (d.getHours() < 10 ? "0" : "") + d.getHours() + ":" + (d.getMinutes() < 10 ? "0" : "") + d.getMinutes();
expect(result).toBe(expected);
});
it("returns empty string for null", () => {
expect(formatHHMM(null)).toBe("");
});
it("returns empty string for undefined", () => {
expect(formatHHMM(undefined)).toBe("");
});
it("returns empty string for empty string", () => {
expect(formatHHMM("")).toBe("");
});
it("pads hours and minutes with zero", () => {
const s = "2025-02-25T09:05:00Z";
const result = formatHHMM(s);
expect(result).toMatch(/^\d{2}:\d{2}$/);
});
});
describe("firstDayOfMonth", () => {
it("returns first day of month", () => {
const d = new Date(2025, 5, 15);
const result = firstDayOfMonth(d);
expect(result.getFullYear()).toBe(2025);
expect(result.getMonth()).toBe(5);
expect(result.getDate()).toBe(1);
});
it("handles January", () => {
const d = new Date(2025, 0, 31);
const result = firstDayOfMonth(d);
expect(result.getDate()).toBe(1);
expect(result.getMonth()).toBe(0);
});
});
describe("lastDayOfMonth", () => {
it("returns last day of month", () => {
const d = new Date(2025, 0, 15);
const result = lastDayOfMonth(d);
expect(result.getFullYear()).toBe(2025);
expect(result.getMonth()).toBe(0);
expect(result.getDate()).toBe(31);
});
it("returns 28 for non-leap February", () => {
const d = new Date(2023, 1, 1);
const result = lastDayOfMonth(d);
expect(result.getDate()).toBe(28);
expect(result.getMonth()).toBe(1);
});
it("returns 29 for leap February", () => {
const d = new Date(2024, 1, 1);
const result = lastDayOfMonth(d);
expect(result.getDate()).toBe(29);
});
});
describe("formatDateKey", () => {
it("formats ISO date string as DD.MM (local time)", () => {
const result = formatDateKey("2025-02-25T00:00:00Z");
expect(result).toMatch(/^\d{2}\.\d{2}$/);
const [day, month] = result.split(".");
expect(Number(day)).toBeGreaterThanOrEqual(1);
expect(Number(day)).toBeLessThanOrEqual(31);
expect(Number(month)).toBeGreaterThanOrEqual(1);
expect(Number(month)).toBeLessThanOrEqual(12);
});
it("returns DD.MM format with zero-padding", () => {
const result = formatDateKey("2025-01-05T12:00:00Z");
expect(result).toMatch(/^\d{2}\.\d{2}$/);
});
});
describe("dateKeyToDDMM", () => {
it("converts YYYY-MM-DD to DD.MM", () => {
expect(dateKeyToDDMM("2025-02-25")).toBe("25.02");
});
it("handles single-digit day and month", () => {
expect(dateKeyToDDMM("2025-01-09")).toBe("09.01");
});
});