diff --git a/src/features/online-board/components/FlightSchedule/DaysOfWeekStrip.test.tsx b/src/features/online-board/components/FlightSchedule/DaysOfWeekStrip.test.tsx index 91d43641..7240686a 100644 --- a/src/features/online-board/components/FlightSchedule/DaysOfWeekStrip.test.tsx +++ b/src/features/online-board/components/FlightSchedule/DaysOfWeekStrip.test.tsx @@ -42,4 +42,38 @@ describe("DaysOfWeekStrip", () => { expect(screen.getByTestId("day-of-week-0").textContent).toContain("DAYS.1"); expect(screen.getByTestId("day-of-week-6").textContent).toContain("DAYS.7"); }); + + // TZ §4.1.16.8: position 0 = Monday (first ISO weekday), position 6 = Sunday + // Verify the Mon/Wed/Fri pattern used by typical schedule flights ("Пн Ср Пт" = "1010100") + it("Mon/Wed/Fri pattern: positions 0,2,4 active; 1,3,5,6 inactive", () => { + render(); + // Active: Mon(0), Wed(2), Fri(4) + expect(screen.getByTestId("day-of-week-0").className).not.toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-2").className).not.toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-4").className).not.toMatch(/--inactive/); + // Inactive: Tue(1), Thu(3), Sat(5), Sun(6) + expect(screen.getByTestId("day-of-week-1").className).toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-3").className).toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-5").className).toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-6").className).toMatch(/--inactive/); + }); + + // TZ §4.1.16.8: Sat/Sun only ("0000011") + it("weekend-only pattern: positions 5,6 active; 0-4 inactive", () => { + render(); + for (let i = 0; i < 5; i++) { + expect(screen.getByTestId(`day-of-week-${i}`).className).toMatch(/--inactive/); + } + expect(screen.getByTestId("day-of-week-5").className).not.toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-6").className).not.toMatch(/--inactive/); + }); + + // TZ §4.1.16.8: single-day operation (Tuesday only = "0100000") + it("Tuesday-only pattern: position 1 active; all others inactive", () => { + render(); + expect(screen.getByTestId("day-of-week-1").className).not.toMatch(/--inactive/); + for (const i of [0, 2, 3, 4, 5, 6]) { + expect(screen.getByTestId(`day-of-week-${i}`).className).toMatch(/--inactive/); + } + }); }); diff --git a/src/features/online-board/components/FlightSchedule/FlightSchedule.test.tsx b/src/features/online-board/components/FlightSchedule/FlightSchedule.test.tsx index 30377ff5..db547156 100644 --- a/src/features/online-board/components/FlightSchedule/FlightSchedule.test.tsx +++ b/src/features/online-board/components/FlightSchedule/FlightSchedule.test.tsx @@ -1,6 +1,6 @@ // @vitest-environment jsdom import { describe, it, expect, vi } from "vitest"; -import { render, screen } from "@testing-library/react"; +import { render, screen, fireEvent } from "@testing-library/react"; import { FlightSchedule } from "./FlightSchedule.js"; import type { ISimpleFlight } from "../../types.js"; @@ -157,4 +157,50 @@ describe("FlightSchedule", () => { // '2ч. 30мин.' via formatDuration in ru locale. expect(screen.getByText("2ч. 30мин.")).toBeTruthy(); }); + + // TZ §4.1.16.8: uses daysOfWeek.flight (schedule days), not .current (today's active day) + it("uses daysOfWeek.flight bitstring for active days, not .current", () => { + // current = daily, flight = Mon/Wed/Fri only ("1010100") + const flight = makeFlight({ + daysOfWeek: { current: "1111111", flight: "1010100" }, + }); + render(); + // Mon(0), Wed(2), Fri(4) should be active + expect(screen.getByTestId("day-of-week-0").className).not.toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-2").className).not.toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-4").className).not.toMatch(/--inactive/); + // Tue(1), Thu(3), Sat(5), Sun(6) should be inactive + expect(screen.getByTestId("day-of-week-1").className).toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-3").className).toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-5").className).toMatch(/--inactive/); + expect(screen.getByTestId("day-of-week-6").className).toMatch(/--inactive/); + }); + + // TZ §4.1.16.8: collapsible accordion — open by default, toggles + it("accordion is expanded by default and collapses on click", async () => { + const flight = makeFlight({ + daysOfWeek: { current: "1111111", flight: "1111111" }, + }); + render(); + const header = screen.getByRole("button"); + // Days strip is visible initially + expect(screen.getAllByTestId(/^day-of-week-\d$/).length).toBe(7); + // Click to collapse + fireEvent.click(header); + expect(screen.queryAllByTestId(/^day-of-week-\d$/).length).toBe(0); + }); + + // TZ §4.1.16.8: week note uses departure local date, not arrival or UTC + it("week note is derived from first-leg scheduled departure local time", () => { + // dep = 2026-04-20 (Monday) → week is 20.04.2026 – 26.04.2026 + const flight = makeFlight({ + daysOfWeek: { current: "1111111", flight: "1111111" }, + depLocal: "2026-04-20T08:00:00", + arrLocal: "2026-04-20T11:00:00", + }); + render(); + const note = screen.getByTestId("flight-schedule-note"); + expect(note.textContent).toContain("20.04.2026"); + expect(note.textContent).toContain("26.04.2026"); + }); }); diff --git a/src/features/online-board/components/FlightSchedule/weekDateRange.test.ts b/src/features/online-board/components/FlightSchedule/weekDateRange.test.ts index 920bcde1..0e1112bb 100644 --- a/src/features/online-board/components/FlightSchedule/weekDateRange.test.ts +++ b/src/features/online-board/components/FlightSchedule/weekDateRange.test.ts @@ -28,4 +28,40 @@ describe("getWeekDateRange", () => { it("returns empty strings for undefined input", () => { expect(getWeekDateRange(undefined)).toEqual({ start: "", end: "" }); }); + + // TZ §4.1.16.8: week range must be calendar week Mon–Sun (ISO week) + // Verify that the range spans exactly 6 days (Sun - Mon = 6 day delta) + it("end date is always exactly 6 days after start date", () => { + const cases = [ + "2026-04-15T10:00:00", // Wednesday + "2026-04-13T00:00:00", // Monday + "2026-04-19T23:59:59", // Sunday + "2026-01-01T12:00:00", // New Year's Day + ]; + for (const iso of cases) { + const { start, end } = getWeekDateRange(iso); + const [sd, sm, sy] = start.split(".").map(Number); + const [ed, em, ey] = end.split(".").map(Number); + const startMs = new Date(sy!, sm! - 1, sd!).getTime(); + const endMs = new Date(ey!, em! - 1, ed!).getTime(); + const daysDiff = Math.round((endMs - startMs) / 86400000); + expect(daysDiff).toBe(6); + } + }); + + // TZ §4.1.16.8: "dd.MM.yyyy" format (leading zeros required) + it("formats dates with leading zeros", () => { + // 2026-01-05 is a Monday + const { start, end } = getWeekDateRange("2026-01-05T10:00:00"); + expect(start).toBe("05.01.2026"); + expect(end).toBe("11.01.2026"); + }); + + // TZ §4.1.16.8: week starts on Monday (ISO standard) + it("Saturday input yields same Mon–Sun week as adjacent Monday", () => { + const sat = getWeekDateRange("2026-04-18T10:00:00"); // Saturday + const mon = getWeekDateRange("2026-04-13T10:00:00"); // Monday of same week + expect(sat.start).toBe(mon.start); + expect(sat.end).toBe(mon.end); + }); });