e2e: adopt console-gate fixture across all specs

This commit is contained in:
2026-04-25 02:58:22 +03:00
parent 6c30e8ae09
commit 8488f94f60
16 changed files with 70 additions and 36 deletions
+3 -2
View File
@@ -1,4 +1,5 @@
import { test, expect, type Page } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
import type { Page } from "@playwright/test";
// Angular's breadcrumb trail (audited live on flights.test.aeroflot.ru):
// /schedule → [Главная]
@@ -143,7 +144,7 @@ const cases: { name: string; url: string; expected: { text: string; href: string
test.describe("Breadcrumb parity with Angular", () => {
for (const c of cases) {
test(c.name, async ({ page }) => {
test(c.name, async ({ page, consoleMessages }) => {
await page.goto(c.url);
await expect(page.getByTestId("breadcrumbs")).toBeVisible({ timeout: 15000 });
// Poll on the full items array — the leaf depends on dictionaries
+2 -1
View File
@@ -1,8 +1,9 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
test.describe("Flights Map", () => {
test("/ru/flights-map renders or shows feature-flag disabled message", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/flights-map");
await page.waitForLoadState("domcontentloaded");
+5 -3
View File
@@ -1,8 +1,9 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
test.describe("Cross-feature navigation", () => {
test("locale switching: /ru/onlineboard -> /en/onlineboard shows English content", async ({
page,
consoleMessages,
}) => {
// Start on Russian online board
await page.goto("/ru/onlineboard");
@@ -23,7 +24,7 @@ test.describe("Cross-feature navigation", () => {
expect(page.url()).toMatch(/\/en(-[a-z]+)?\/onlineboard/);
});
test("error page: /error/404 renders 404 content", async ({ page }) => {
test("error page: /error/404 renders 404 content", async ({ page, consoleMessages }) => {
// Navigate to a working page first, then client-side navigate to the error
// page. Direct URL navigation to /error/404 renders blank because the
// error route is outside [lang]/layout.tsx and SSR produces empty output.
@@ -42,6 +43,7 @@ test.describe("Cross-feature navigation", () => {
test("error page: /error/500 renders server error content", async ({
page,
consoleMessages,
}) => {
// Navigate to a working page first, then client-side navigate to the error
// page (same reason as the 404 test above).
@@ -57,7 +59,7 @@ test.describe("Cross-feature navigation", () => {
await expect(page.locator(".error-page__code")).toHaveText("500", { timeout: 10000 });
});
test("unknown route: /ru/nonexistent does not crash", async ({ page }) => {
test("unknown route: /ru/nonexistent does not crash", async ({ page, consoleMessages }) => {
const response = await page.goto("/ru/nonexistent");
await page.waitForLoadState("domcontentloaded");
+17 -8
View File
@@ -1,8 +1,9 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
test.describe("Online Board", () => {
test("/ru/onlineboard renders the start page with search form", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("domcontentloaded");
@@ -18,6 +19,7 @@ test.describe("Online Board", () => {
test("filter has accordion with Flight Number and Route tabs", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("domcontentloaded");
@@ -36,7 +38,7 @@ test.describe("Online Board", () => {
).toBeVisible();
});
test("clicking Flight Number tab switches to flight form", async ({ page }) => {
test("clicking Flight Number tab switches to flight form", async ({ page, consoleMessages }) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("domcontentloaded");
@@ -63,6 +65,7 @@ test.describe("Online Board", () => {
test("search form has route inputs, date picker, and submit button", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("domcontentloaded");
@@ -86,7 +89,7 @@ test.describe("Online Board", () => {
await expect(page.locator('[data-testid="search-submit"]')).toBeVisible();
});
test("flight number clear button clears the input", async ({ page }) => {
test("flight number clear button clears the input", async ({ page, consoleMessages }) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("domcontentloaded");
@@ -107,7 +110,7 @@ test.describe("Online Board", () => {
await expect(page.locator('[data-testid="flight-number-input"]')).toHaveValue("");
});
test("route tab has swap button and time selector", async ({ page }) => {
test("route tab has swap button and time selector", async ({ page, consoleMessages }) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("domcontentloaded");
@@ -125,7 +128,7 @@ test.describe("Online Board", () => {
await expect(page.locator('[data-testid="time-selector"]')).toBeVisible();
});
test("breadcrumbs are visible on start page", async ({ page }) => {
test("breadcrumbs are visible on start page", async ({ page, consoleMessages }) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("domcontentloaded");
@@ -137,7 +140,7 @@ test.describe("Online Board", () => {
});
// FeedbackButton component exists but is not wired into OnlineBoardStartPage yet
test.fixme("feedback button is visible", async ({ page }) => {
test.fixme("feedback button is visible", async ({ page, consoleMessages }) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("domcontentloaded");
@@ -150,6 +153,7 @@ test.describe("Online Board", () => {
test("/ru/onlineboard/flight/SU0100-20260415 renders the flight search page", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/onlineboard/flight/SU0100-20260415");
await page.waitForLoadState("domcontentloaded");
@@ -163,6 +167,7 @@ test.describe("Online Board", () => {
test("/ru/onlineboard/departure/SVO-20260415 renders the departure search page", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/onlineboard/departure/SVO-20260415");
await page.waitForLoadState("domcontentloaded");
@@ -173,6 +178,7 @@ test.describe("Online Board", () => {
test("/ru/onlineboard/route/SVO-LED-20260415 renders the route search page", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/onlineboard/route/SVO-LED-20260415");
await page.waitForLoadState("domcontentloaded");
@@ -183,6 +189,7 @@ test.describe("Online Board", () => {
test("flight details page at /ru/onlineboard/SU0100-20260415 renders", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/onlineboard/SU0100-20260415");
await page.waitForLoadState("domcontentloaded");
@@ -193,7 +200,7 @@ test.describe("Online Board", () => {
// Requires live API (city autocomplete + calendar days).
// Skipped when WAF blocks flights.test.aeroflot.ru.
test.skip("route search via form navigates to correct URL", async ({ page }) => {
test.skip("route search via form navigates to correct URL", async ({ page, consoleMessages }) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("networkidle");
@@ -229,6 +236,7 @@ test.describe("Online Board", () => {
test("route search results page hydrates filter from URL params", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/onlineboard/route/MOW-KUF-20260416");
await page.waitForLoadState("networkidle");
@@ -249,6 +257,7 @@ test.describe("Online Board", () => {
// Skipped when WAF blocks flights.test.aeroflot.ru.
test.skip("route search results page shows calendar strip with day numbers", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/onlineboard/route/MOW-KUF-20260416");
await page.waitForLoadState("networkidle");
@@ -269,7 +278,7 @@ test.describe("Online Board", () => {
// TODO: SeoHead does not currently populate <title> on this route.
// Re-enable once the SeoHead component writes to document.title or uses <Helmet>.
test.fixme("page title is set on /ru/onlineboard", async ({ page }) => {
test.fixme("page title is set on /ru/onlineboard", async ({ page, consoleMessages }) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("domcontentloaded");
+3 -2
View File
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// TIRREDESIGN-8: Onlineboard day-tabs must remain unblocked across the
// full -1/+14 window, and must surface out-of-range dates greyed-out
@@ -12,6 +12,7 @@ import { test, expect } from "@playwright/test";
test.describe("TIRREDESIGN-8 — Onlineboard day-tabs", () => {
test("strip exposes the full -1/+14 range without blocking enabled tabs", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru-ru/onlineboard/route/MOW-LED-20260423");
await expect(page.getByTestId("day-tabs")).toBeVisible({ timeout: 15000 });
@@ -58,7 +59,7 @@ test.describe("TIRREDESIGN-8 — Onlineboard day-tabs", () => {
await expect(page.getByTestId("day-tabs-next")).toBeDisabled();
});
test("clicking enabled tabs does not disable siblings", async ({ page }) => {
test("clicking enabled tabs does not disable siblings", async ({ page, consoleMessages }) => {
await page.goto("/ru-ru/onlineboard/route/MOW-LED-20260423");
await expect(page.getByTestId("day-tabs")).toBeVisible({ timeout: 15000 });
+2 -1
View File
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// TIRREDESIGN-10 — Onlineboard list rows must surface "Купить билет"
// and "Онлайн регистрация" inside the expanded body when the per-flight
@@ -15,6 +15,7 @@ import { test, expect } from "@playwright/test";
test("Onlineboard expanded row shows Купить билет + Онлайн регистрация when applicable", async ({
page,
consoleMessages,
}) => {
// Today in the harness clock.
const today = new Date().toISOString().slice(0, 10).replace(/-/g, "");
+3 -1
View File
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// TIRREDESIGN-11 — "Время рейса" slider must filter results.
//
@@ -13,6 +13,7 @@ const ROUTE_URL = "/ru-ru/onlineboard/route/MOW-LED-20260423";
test.describe("Onlineboard time-range filter (TIRREDESIGN-11)", () => {
test("URL with time-range suffix filters the list (URL → state path)", async ({
page,
consoleMessages,
}) => {
// Baseline: no filter
await page.goto(ROUTE_URL);
@@ -38,6 +39,7 @@ test.describe("Onlineboard time-range filter (TIRREDESIGN-11)", () => {
test("dragging the slider + clicking Найти persists time range to URL", async ({
page,
consoleMessages,
}) => {
await page.goto(ROUTE_URL);
await expect(page.locator(".flight-card").first()).toBeVisible({
+15 -4
View File
@@ -9,7 +9,7 @@
* §4.1.1 ¶12 — Flight-Map filter is independent (no cross-section carry-over)
*/
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// ---------------------------------------------------------------------------
// Helpers
@@ -34,6 +34,7 @@ function daysFromNow(n: number): Date {
test.describe("P1 — 4.1.2-R11: out-of-range dates redirect to start page", () => {
test("Online-Board flight URL with date +30 days (beyond +14 window) redirects to /onlineboard", async ({
page,
consoleMessages,
}) => {
const far = fmt(daysFromNow(30));
await page.goto(`/ru/onlineboard/flight/SU1234-${far}`);
@@ -48,6 +49,7 @@ test.describe("P1 — 4.1.2-R11: out-of-range dates redirect to start page", ()
test("Online-Board route URL with date -5 days (before -1 window) redirects to /onlineboard", async ({
page,
consoleMessages,
}) => {
const past = fmt(daysFromNow(-5));
await page.goto(`/ru/onlineboard/route/MOW-LED-${past}`);
@@ -60,6 +62,7 @@ test.describe("P1 — 4.1.2-R11: out-of-range dates redirect to start page", ()
test("Schedule route URL with date +400 days (beyond +330 window) redirects to /schedule", async ({
page,
consoleMessages,
}) => {
const farFrom = fmt(daysFromNow(400));
const farTo = fmt(daysFromNow(407));
@@ -79,6 +82,7 @@ test.describe("P1 — 4.1.2-R11: out-of-range dates redirect to start page", ()
test.describe("P1 — 4.1.2-R10: unknown URL shows 404", () => {
test("/ru/nonexistent does not crash — shows error or empty body", async ({
page,
consoleMessages,
}) => {
// Matches the existing pattern in navigation.spec.ts: the app handles
// unknown routes gracefully (404 page or redirect, not a JS crash).
@@ -89,6 +93,7 @@ test.describe("P1 — 4.1.2-R10: unknown URL shows 404", () => {
test("/error/404 renders 404 content (client-side navigation)", async ({
page,
consoleMessages,
}) => {
// Direct URL navigate to the error route produces blank SSR output;
// client-side assign is the established pattern (see navigation.spec.ts).
@@ -113,6 +118,7 @@ test.describe("P1 — 4.1.2-R10: unknown URL shows 404", () => {
test.describe("P1 — Table 7: breadcrumbs on start pages (Home only)", () => {
test("Online-Board start page has exactly 1 breadcrumb (Home)", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/onlineboard");
await page.waitForLoadState("domcontentloaded");
@@ -126,6 +132,7 @@ test.describe("P1 — Table 7: breadcrumbs on start pages (Home only)", () => {
test("Schedule start page has exactly 1 breadcrumb (Home)", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/schedule");
await page.waitForLoadState("domcontentloaded");
@@ -139,6 +146,7 @@ test.describe("P1 — Table 7: breadcrumbs on start pages (Home only)", () => {
test("Flight-Map start page has exactly 1 breadcrumb (Home)", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/flights-map");
await page.waitForLoadState("domcontentloaded");
@@ -163,6 +171,7 @@ test.describe("P1 — Table 7: breadcrumbs on start pages (Home only)", () => {
test.describe("P1 — Table 7: breadcrumbs on search pages", () => {
test("Online-Board route search page has 2 breadcrumbs (Home + Section)", async ({
page,
consoleMessages,
}) => {
// Use an in-window date so the guard lets the page through.
const today = fmt(new Date());
@@ -178,6 +187,7 @@ test.describe("P1 — Table 7: breadcrumbs on search pages", () => {
test("Online-Board flight search page has 2 breadcrumbs (Home + Section)", async ({
page,
consoleMessages,
}) => {
const today = fmt(new Date());
await page.goto(`/ru/onlineboard/flight/SU0100-${today}`);
@@ -190,6 +200,7 @@ test.describe("P1 — Table 7: breadcrumbs on search pages", () => {
test("Schedule route search page has 3 breadcrumbs (Home + Section + Route heading)", async ({
page,
consoleMessages,
}) => {
const today = fmt(new Date());
const weekAhead = fmt(daysFromNow(7));
@@ -216,7 +227,7 @@ test.describe("P1 — Table 10: cross-section filter carry-over Board ↔ Schedu
test.fixme(
"navigating from Online-Board results to Schedule preserves departure/arrival cities",
async ({ page }) => {
async ({ page, consoleMessages }) => {
// 1. Search on Online-Board (route tab) → MOW-LED.
// 2. Navigate to /ru/schedule.
// 3. Schedule start page filter should be pre-filled with MOW/LED via
@@ -228,7 +239,7 @@ test.describe("P1 — Table 10: cross-section filter carry-over Board ↔ Schedu
test.fixme(
"navigating from Schedule results to Online-Board preserves departure/arrival cities",
async ({ page }) => {
async ({ page, consoleMessages }) => {
// 1. Search on Schedule (route tab) → MOW-LED.
// 2. Navigate to /ru/onlineboard.
// 3. Online-Board start page filter should be pre-filled with MOW/LED.
@@ -250,7 +261,7 @@ test.describe("P1 — §4.1.1 ¶12: Flight-Map filter is independent (no carry-o
test.fixme(
"Online-Board search does not affect Flight-Map departure city",
async ({ page }) => {
async ({ page, consoleMessages }) => {
// 1. Search on Online-Board → MOW-LED.
// 2. Navigate to /ru/flights-map.
// 3. Flight-Map departure input should NOT show MOW (store is separate).
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// TIRREDESIGN-12 — when both schedule cities are filled, the date-picker
// must grey out the days the route does NOT operate. The fix in
@@ -10,6 +10,7 @@ import { test, expect } from "@playwright/test";
test("Schedule calendar greys out non-operating days for the route", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru-ru/schedule/route/MOW-MMK-20260427-20260503");
await expect(page.locator(".day-grouped-flight-list").first()).toBeVisible({
+4 -2
View File
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// Schedule date picker — Angular parity (TZ §4.1.9.4):
// • Single click on any day commits the **whole Mon-Sun week** that
@@ -15,6 +15,7 @@ import { test, expect } from "@playwright/test";
test.describe("Schedule date-range picker (week-snap)", () => {
test("single click snaps to Mon-Sun, closes panel, fills input", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru-ru/schedule");
await expect(page.getByTestId("date-range-input")).toBeVisible({
@@ -39,6 +40,7 @@ test.describe("Schedule date-range picker (week-snap)", () => {
test("clicking a next-month bleed-in day (3 May) snaps to 4-10 May", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru-ru/schedule");
await expect(page.getByTestId("date-range-input")).toBeVisible({
@@ -60,7 +62,7 @@ test.describe("Schedule date-range picker (week-snap)", () => {
);
});
test("input renders as range placeholder when empty", async ({ page }) => {
test("input renders as range placeholder when empty", async ({ page, consoleMessages }) => {
await page.goto("/ru-ru/schedule");
const input = page.locator("#schedule-date-from");
await expect(input).toHaveAttribute(
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// When the user clicks a connecting itinerary in the Schedule list, the
// resulting flight-details URL must include EVERY leg, not just the
@@ -10,6 +10,7 @@ import { test, expect } from "@playwright/test";
test("connecting itinerary navigates to a multi-segment URL with both legs rendered", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru-ru/schedule/route/MOW-MMK-20260427-20260503");
await expect(page.locator(".flight-card").first()).toBeVisible({
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// Schedule Details "Питание на борту" must render meal-class sub-icons
// (Эконом класс / Комфорт класс / Бизнес класс) ONLY when the API
@@ -16,6 +16,7 @@ const URL =
test("Питание sub-icons appear only for legs whose API meal[] contains them", async ({
page,
consoleMessages,
}) => {
await page.goto(URL);
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// On the schedule details page the left mini-list renders a SINGLE
// card for the currently-open flight — matching Angular's
@@ -14,7 +14,7 @@ import { test, expect } from "@playwright/test";
const URL =
"/ru-ru/schedule/VKO/SU6188-20260426/LED/SU6341-20260427/MMK?request=schedule-route-MOW-MMK-20260427-20260503";
test("mini-list — one combined card for the open SU 6188+SU 6341 itinerary", async ({ page }) => {
test("mini-list — one combined card for the open SU 6188+SU 6341 itinerary", async ({ page, consoleMessages }) => {
await page.goto(URL);
const miniList = page.locator(".schedule-mini-list");
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// Schedule details page must render Angular's `<schedule-details-header>`
// summary block between the day-tabs strip and the per-leg cards:
@@ -15,7 +15,7 @@ import { test, expect } from "@playwright/test";
const URL =
"/ru-ru/schedule/VKO/SU6188-20260426/LED/SU6341-20260427/MMK?request=schedule-route-MOW-MMK-20260427-20260503";
test("summary header — both badges + last-update + formatted full-route timeline", async ({ page }) => {
test("summary header — both badges + last-update + formatted full-route timeline", async ({ page, consoleMessages }) => {
await page.goto(URL);
const summary = page.locator(".schedule-details__summary");
+3 -2
View File
@@ -1,7 +1,7 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
test.describe("Schedule", () => {
test("/ru/schedule renders the start page", async ({ page }) => {
test("/ru/schedule renders the start page", async ({ page, consoleMessages }) => {
await page.goto("/ru/schedule");
await page.waitForLoadState("domcontentloaded");
@@ -18,6 +18,7 @@ test.describe("Schedule", () => {
test("/ru/schedule/route/SVO-LED-20260415 renders the search page", async ({
page,
consoleMessages,
}) => {
await page.goto("/ru/schedule/route/SVO-LED-20260415");
await page.waitForLoadState("domcontentloaded");
+3 -3
View File
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures/console-gate";
// TIRREDESIGN-5: the search-history sidebar header must read
// "Ранее искали" (not "Вы искали"). The block exists on Schedule
@@ -30,7 +30,7 @@ async function seedHistory(page: import("@playwright/test").Page) {
}
test.describe("Search-history label is 'Ранее искали' (TIRREDESIGN-5)", () => {
test("Schedule start page", async ({ page }) => {
test("Schedule start page", async ({ page, consoleMessages }) => {
await seedHistory(page);
await page.goto("/ru-ru/schedule");
const block = page.getByTestId("search-history");
@@ -39,7 +39,7 @@ test.describe("Search-history label is 'Ранее искали' (TIRREDESIGN-5)
await expect(block).not.toContainText("Вы искали");
});
test("Online-Board start page", async ({ page }) => {
test("Online-Board start page", async ({ page, consoleMessages }) => {
await seedHistory(page);
await page.goto("/ru-ru/onlineboard");
const block = page.getByTestId("search-history");