Files
flights_web/tests/e2e/flights-map.spec.ts
gnezim 5c309004f0
ci-deploy / build-deploy-test (push) Successful in 2m12s
Fix first city click on flights map
2026-05-26 11:21:14 +03:00

314 lines
9.5 KiB
TypeScript

import { test, expect } from "./fixtures/console-gate";
import {
routeAppSettingsFixture,
routeDictionaryFixtures,
} from "./helpers/api-fixtures";
async function routeFlightsMapTransferOnlyFixtures(
page: import("@playwright/test").Page,
): Promise<void> {
await routeAppSettingsFixture(page);
await routeDictionaryFixtures(page);
await page.route("**/api/flights/1/*/destinations?**", async (route) => {
const url = new URL(route.request().url());
const departure = url.searchParams.get("departure");
const arrival = url.searchParams.get("arrival");
const connections = url.searchParams.get("connections");
const hasTransferRoute =
departure === "LED" && arrival === "MLE" && connections === "1";
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
data: {
routes: hasTransferRoute
? [{ route: ["LED", "SVO", "MLE"], isDirect: false }]
: [],
},
}),
});
});
await page.route("**/api/flights/v1/*/days/**/flights-map/", (route) =>
route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ days: "1".repeat(200) }),
}),
);
await page.route("**/map/api/tile/**", (route) =>
route.fulfill({
status: 200,
contentType: "image/gif",
body: Buffer.from("R0lGODlhAQABAAAAACw=", "base64"),
}),
);
}
async function routeFlightsMapDirectRouteFixtures(
page: import("@playwright/test").Page,
): Promise<string[]> {
await routeAppSettingsFixture(page);
await routeDictionaryFixtures(page);
const destinationRequests: string[] = [];
await page.route("**/api/flights/1/*/destinations?**", async (route) => {
const url = new URL(route.request().url());
destinationRequests.push(url.toString());
const departure = url.searchParams.get("departure");
const arrival = url.searchParams.get("arrival");
const connections = url.searchParams.get("connections");
const isSelectedRoute = departure === "LED" && arrival === "MLE";
const routes =
isSelectedRoute && connections === "0"
? [{ route: ["LED", "MLE"], isDirect: true }]
: isSelectedRoute && connections === "1"
? [{ route: ["LED", "SVO", "MLE"], isDirect: false }]
: [];
if (isSelectedRoute && connections === "0") {
await new Promise((resolve) => setTimeout(resolve, 250));
}
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ data: { routes } }),
});
});
await page.route("**/api/flights/v1/*/days/**/flights-map/", (route) =>
route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ days: "1".repeat(200) }),
}),
);
await page.route("**/map/api/tile/**", (route) =>
route.fulfill({
status: 200,
contentType: "image/gif",
body: Buffer.from("R0lGODlhAQABAAAAACw=", "base64"),
}),
);
return destinationRequests;
}
async function routeFlightsMapMoscowSpiderFixtures(
page: import("@playwright/test").Page,
): Promise<string[]> {
await routeAppSettingsFixture(page);
await routeDictionaryFixtures(page);
const destinationRequests: string[] = [];
await page.route("**/api/flights/1/*/destinations?**", async (route) => {
const url = new URL(route.request().url());
destinationRequests.push(url.toString());
const departure = url.searchParams.get("departure");
const arrival = url.searchParams.get("arrival");
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
data: {
routes:
departure === "MOW" && !arrival
? [
{ route: ["SVO", "LED"], isDirect: true },
{ route: ["SVO", "MLE"], isDirect: true },
]
: [],
},
}),
});
});
await page.route("**/api/flights/v1/*/days/**/flights-map/", (route) =>
route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ days: "1".repeat(200) }),
}),
);
await page.route("**/map/api/tile/**", (route) =>
route.fulfill({
status: 200,
contentType: "image/gif",
body: Buffer.from("R0lGODlhAQABAAAAACw=", "base64"),
}),
);
return destinationRequests;
}
async function selectCity(
page: import("@playwright/test").Page,
inputTestId: string,
query: string,
optionTestId: string,
): Promise<void> {
const input = page.getByTestId(inputTestId).locator("input");
await input.click();
await input.fill(query);
await expect(page.getByTestId(optionTestId)).toBeVisible({ timeout: 10000 });
await page.getByTestId(optionTestId).click();
}
test.describe("Flights Map", () => {
test("/ru/flights-map renders or shows feature-flag disabled message", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru-ru/flights-map");
await page.waitForLoadState("domcontentloaded");
// Either the map page renders or the feature-flag-disabled fallback shows
const mapStart = page.locator('[data-testid="flights-map-start"]');
const mapDisabled = page.locator('[data-testid="flights-map-disabled"]');
await expect(mapStart.or(mapDisabled)).toBeVisible({ timeout: 10000 });
});
test("transfer-only checkbox can be switched off after auto-fallback", async ({
page,
consoleMessages,
}) => {
await routeFlightsMapTransferOnlyFixtures(page);
await page.goto("/ru-ru/flights-map");
await expect(page.getByTestId("flights-map-start")).toBeVisible();
await selectCity(page, "fm-departure-input", "Санкт", "city-suggestion-LED");
await selectCity(page, "fm-arrival-input", "Мале", "city-suggestion-MLE");
const transferToggle = page.getByTestId("fm-connections-toggle");
const transferLabel = page.getByText("Показать только рейсы с пересадкой", {
exact: true,
});
await expect(transferToggle).toBeEnabled();
await expect(transferToggle).toBeChecked({ timeout: 10000 });
await transferLabel.click();
await expect(transferToggle).not.toBeChecked();
await page.waitForLoadState("networkidle", { timeout: 5000 }).catch(() => {});
await expect(transferToggle).not.toBeChecked();
expect(consoleMessages).toEqual([]);
});
test("first click on a city label selects the city and draws spider routes", async ({
page,
consoleMessages,
}) => {
const destinationRequests = await routeFlightsMapMoscowSpiderFixtures(page);
await page.goto("/ru-ru/flights-map");
await expect(page.getByTestId("flights-map-start")).toBeVisible();
const moscowLabel = page.locator(".leaflet-tooltip.city-label", {
hasText: "Москва",
});
await expect(moscowLabel).toHaveCount(1, { timeout: 10000 });
await moscowLabel.click();
await expect(page.getByTestId("fm-departure-input").locator("input")).toHaveValue(
"Москва",
);
await expect(page.getByTestId("fm-arrival-input").locator("input")).toHaveValue("");
await expect
.poll(() =>
destinationRequests.some(
(url) =>
url.includes("departure=MOW") &&
!url.includes("arrival="),
),
)
.toBe(true);
await expect(page.locator("path.leaflet-interactive")).not.toHaveCount(0);
expect(consoleMessages).toEqual([]);
});
test("first click on a city marker selects the city", async ({
page,
consoleMessages,
}) => {
const destinationRequests = await routeFlightsMapMoscowSpiderFixtures(page);
await page.goto("/ru-ru/flights-map");
await expect(page.getByTestId("flights-map-start")).toBeVisible();
const moscowMarker = page.locator('img.leaflet-marker-icon[title="MOW"]');
await expect(moscowMarker).toHaveCount(1, { timeout: 10000 });
await moscowMarker.click();
await expect(page.getByTestId("fm-departure-input").locator("input")).toHaveValue(
"Москва",
);
await expect
.poll(() =>
destinationRequests.some(
(url) =>
url.includes("departure=MOW") &&
!url.includes("arrival="),
),
)
.toBe(true);
expect(consoleMessages).toEqual([]);
});
test("transfer-only checkbox stays off while a direct route search is still loading", async ({
page,
consoleMessages,
}) => {
const destinationRequests = await routeFlightsMapDirectRouteFixtures(page);
await page.goto("/ru-ru/flights-map");
await expect(page.getByTestId("flights-map-start")).toBeVisible();
await selectCity(page, "fm-departure-input", "Санкт", "city-suggestion-LED");
await selectCity(page, "fm-arrival-input", "Мале", "city-suggestion-MLE");
const transferToggle = page.getByTestId("fm-connections-toggle");
await expect(transferToggle).toBeEnabled();
await expect(transferToggle).not.toBeChecked();
await expect
.poll(() =>
destinationRequests.some(
(url) =>
url.includes("departure=LED") &&
url.includes("arrival=MLE") &&
url.includes("connections=0"),
),
)
.toBe(true);
await expect(transferToggle).not.toBeChecked();
await page.waitForTimeout(500);
expect(
destinationRequests.some(
(url) =>
url.includes("departure=LED") &&
url.includes("arrival=MLE") &&
url.includes("connections=1"),
),
).toBe(false);
expect(consoleMessages).toEqual([]);
});
});