feat: enhance CI workflow and update webapp styles
Some checks failed
CI / lint-and-test (push) Failing after 45s

- 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.
This commit is contained in:
2026-03-02 17:20:33 +03:00
parent e3240d0981
commit 2fb553567f
29 changed files with 2212 additions and 1375 deletions

View File

@@ -1,9 +1,10 @@
/**
* Unit tests for dutyList (dutyTimelineCardHtml, contact rendering).
* Unit tests for dutyList (dutyTimelineCardHtml, dutyItemHtml, contact rendering).
*/
import { describe, it, expect, beforeAll } from "vitest";
import { dutyTimelineCardHtml } from "./dutyList.js";
import { describe, it, expect, beforeAll, vi, afterEach } from "vitest";
import * as dateUtils from "./dateUtils.js";
import { dutyTimelineCardHtml, dutyItemHtml } from "./dutyList.js";
describe("dutyList", () => {
beforeAll(() => {
@@ -89,4 +90,75 @@ describe("dutyList", () => {
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('<div class="time">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");
});
});
});