diff --git a/src/features/schedule/components/ScheduleDetailsPage.test.tsx b/src/features/schedule/components/ScheduleDetailsPage.test.tsx index 6cc2db81..3b932f70 100644 --- a/src/features/schedule/components/ScheduleDetailsPage.test.tsx +++ b/src/features/schedule/components/ScheduleDetailsPage.test.tsx @@ -60,8 +60,14 @@ vi.mock("@/i18n/resolver.js", () => ({ })); // Mutable for individual test overrides -let mockScheduleDetailsResult: { flights: unknown[]; loading: boolean; error: unknown } = { +let mockScheduleDetailsResult: { + flights: unknown[]; + daysOfFlight: string[]; + loading: boolean; + error: unknown; +} = { flights: [], + daysOfFlight: [], loading: true, error: null, }; @@ -145,7 +151,9 @@ vi.mock("@/features/online-board/components/FlightsMiniList/index.js", () => ({ })); vi.mock("@/features/online-board/components/DayTabs/index.js", () => ({ - DayTabs: () =>
, + DayTabs: ({ availableDates }: { availableDates: string[] }) => ( +
+ ), })); vi.mock("@/ui/flights/FlightCard.js", () => ({ @@ -187,7 +195,7 @@ const flightId: IScheduleFlightId = { describe("ScheduleDetailsPage breadcrumbs", () => { beforeEach(() => { mockSearchParamsGet = () => null; - mockScheduleDetailsResult = { flights: [], loading: true, error: null }; + mockScheduleDetailsResult = { flights: [], daysOfFlight: [], loading: true, error: null }; }); it("shows 1-item trail when no ?request= param (share-link)", () => { @@ -253,7 +261,7 @@ describe("ScheduleDetailsPage structure (§4.1.16.1 + §4.1.16.2 + §4.1.16.3)", beforeEach(() => { mockSearchParamsGet = () => null; // Reset to loading state (breadcrumb tests rely on this) - mockScheduleDetailsResult = { flights: [], loading: true, error: null }; + mockScheduleDetailsResult = { flights: [], daysOfFlight: [], loading: true, error: null }; // The new ScheduleFlightsMiniList scrolls the highlighted row into // view on mount; JSDOM doesn't ship `scrollIntoView`, so stub it. Element.prototype.scrollIntoView = vi.fn(); @@ -288,6 +296,7 @@ describe("ScheduleDetailsPage structure (§4.1.16.1 + §4.1.16.2 + §4.1.16.3)", }, }, ], + daysOfFlight: ["20260515", "20260516"], loading: false, error: null, }; @@ -333,6 +342,7 @@ describe("ScheduleDetailsPage structure (§4.1.16.1 + §4.1.16.2 + §4.1.16.3)", }, }, ], + daysOfFlight: ["20260515", "20260516"], loading: false, error: null, }; @@ -345,6 +355,9 @@ describe("ScheduleDetailsPage structure (§4.1.16.1 + §4.1.16.2 + §4.1.16.3)", ); // Success state renders DayTabs in stickyContent (Schedule window = +330 days) expect(screen.getByTestId("day-tabs")).toBeTruthy(); + expect(screen.getByTestId("day-tabs").getAttribute("data-available-dates")).toBe( + "20260515,20260516", + ); }); it("4.1.16.1-R4: back link navigates to scheduleHref (success state)", () => { @@ -377,6 +390,7 @@ describe("ScheduleDetailsPage structure (§4.1.16.1 + §4.1.16.2 + §4.1.16.3)", }, }, ], + daysOfFlight: ["20260515", "20260516"], loading: false, error: null, }; diff --git a/src/features/schedule/components/ScheduleDetailsPage.tsx b/src/features/schedule/components/ScheduleDetailsPage.tsx index e2fb633b..5548f06b 100644 --- a/src/features/schedule/components/ScheduleDetailsPage.tsx +++ b/src/features/schedule/components/ScheduleDetailsPage.tsx @@ -95,7 +95,7 @@ export const ScheduleDetailsPage: FC = ({ arrival: "", }; - const { flights, loading, error } = useScheduleDetails(detailsParams); + const { flights, daysOfFlight, loading, error } = useScheduleDetails(detailsParams); const scheduleHref = `/${locale}/schedule`; @@ -382,7 +382,7 @@ export const ScheduleDetailsPage: FC = ({ // TZ §4.1.16.3 R22-R28: day tabs (Schedule window: [-1, +330] from today) ([]); + const [daysOfFlight, setDaysOfFlight] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -53,6 +55,7 @@ export function useScheduleDetails( .then((response) => { if (!cancelled) { setFlights(response.data.routes); + setDaysOfFlight(response.data.daysOfFlight ?? []); setLoading(false); } }) @@ -68,5 +71,5 @@ export function useScheduleDetails( }; }, [client, flightsKey, datesKey, params.departure, params.arrival]); - return { flights, loading, error }; + return { flights, daysOfFlight, loading, error }; } diff --git a/tests/e2e/schedule-details-day-tabs-operating-days.spec.ts b/tests/e2e/schedule-details-day-tabs-operating-days.spec.ts new file mode 100644 index 00000000..da07e7ac --- /dev/null +++ b/tests/e2e/schedule-details-day-tabs-operating-days.spec.ts @@ -0,0 +1,61 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { test, expect } from "./fixtures/console-gate"; +import { routeAppSettingsFixture } from "./helpers/api-fixtures"; + +const FIXTURE_DIR = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../fixtures/api", +); + +const URL = + "/ru-ru/schedule/KJA/SU6837-20260519/MJZ?request=schedule-route-KJA-MJZ-20260518-20260524-C0"; + +test("TIRREDESIGN-26: schedule details day tabs disable non-operating flight dates", async ({ + page, + consoleMessages, +}) => { + await routeAppSettingsFixture(page); + await page.route("**/api/flights/v1.1/*/schedule/details?**", async (route) => { + const source = JSON.parse( + fs.readFileSync(path.join(FIXTURE_DIR, "schedule-details-vvo-mjz.json"), "utf8"), + ) as { + data: { + routes: Array<{ + flightId: { carrier: string; flightNumber: string; date: string }; + }>; + partners: string[]; + }; + }; + const su6837 = source.data.routes.find( + (flight) => + flight.flightId.carrier === "SU" && + flight.flightId.flightNumber === "6837" && + flight.flightId.date === "2026-05-19", + ); + expect(su6837).toBeTruthy(); + await route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ + data: { + partners: [], + routes: su6837 ? [su6837] : [], + daysOfFlight: ["20260519", "20260523"], + }, + }), + }); + }); + + await page.goto(URL); + await expect(page.getByTestId("day-tabs")).toBeVisible({ timeout: 15000 }); + await expect(page.getByTestId("day-tab-20260519")).toBeEnabled(); + + await page.getByTestId("day-tabs-next").click(); + const nonOperatingFriday = page.getByTestId("day-tab-20260522"); + await expect(nonOperatingFriday).toBeDisabled(); + await expect(page.getByTestId("day-tab-20260523")).toBeEnabled(); + + await expect(page.getByTestId("schedule-details-not-found")).toHaveCount(0); +});