Respect schedule flight operating days
This commit is contained in:
@@ -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: () => <div data-testid="day-tabs" />,
|
||||
DayTabs: ({ availableDates }: { availableDates: string[] }) => (
|
||||
<div data-testid="day-tabs" data-available-dates={availableDates.join(",")} />
|
||||
),
|
||||
}));
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -95,7 +95,7 @@ export const ScheduleDetailsPage: FC<ScheduleDetailsPageProps> = ({
|
||||
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<ScheduleDetailsPageProps> = ({
|
||||
// TZ §4.1.16.3 R22-R28: day tabs (Schedule window: [-1, +330] from today)
|
||||
<DayTabs
|
||||
selectedDate={selectedDate}
|
||||
availableDates={[]}
|
||||
availableDates={daysOfFlight}
|
||||
daysBefore={scheduleSearchFrom}
|
||||
daysAfter={scheduleSearchTo}
|
||||
locale={locale}
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface UseScheduleDetailsParams {
|
||||
|
||||
export interface UseScheduleDetailsResult {
|
||||
flights: ISimpleFlight[];
|
||||
daysOfFlight: string[];
|
||||
loading: boolean;
|
||||
error: ApiError | null;
|
||||
}
|
||||
@@ -34,6 +35,7 @@ export function useScheduleDetails(
|
||||
): UseScheduleDetailsResult {
|
||||
const client = useApiClient();
|
||||
const [flights, setFlights] = useState<ISimpleFlight[]>([]);
|
||||
const [daysOfFlight, setDaysOfFlight] = useState<string[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<ApiError | null>(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 };
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user