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);
+ });
});