Lock in execution-days algorithm per TZ 4.1.16.8 (assertion tests)
Add targeted assertions covering every rendering rule in §4.1.16.8: - DaysOfWeekStrip: Mon/Wed/Fri, weekend-only, single-day patterns; explicit bit-index contract (0=Mon … 6=Sun) - weekDateRange: Mon–Sun ISO week, 6-day span invariant, leading-zero dd.MM.yyyy format, Sat input resolves to same week as Mon - FlightSchedule: daysOfWeek.flight (not .current) drives active days; accordion collapses on click; week note anchored to dep-local date
This commit is contained in:
@@ -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(<DaysOfWeekStrip flightBitString="1010100" />);
|
||||
// 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(<DaysOfWeekStrip flightBitString="0000011" />);
|
||||
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(<DaysOfWeekStrip flightBitString="0100000" />);
|
||||
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/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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(<FlightSchedule flight={flight} />);
|
||||
// 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(<FlightSchedule flight={flight} />);
|
||||
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(<FlightSchedule flight={flight} />);
|
||||
const note = screen.getByTestId("flight-schedule-note");
|
||||
expect(note.textContent).toContain("20.04.2026");
|
||||
expect(note.textContent).toContain("26.04.2026");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user