Files
flights_web/tests/e2e-angular/flight-details.spec.ts
T
gnezim 20c19d15f4
CI / ci (push) Failing after 23s
Deploy / build-and-deploy (push) Failing after 5s
Add standalone API proxy via curl (bypasses WAF TLS fingerprinting)
Modern.js SSR intercepts all routes before any Express middleware,
so the API proxy runs as a separate Express server on port 8080.
Modern.js runs on 8081. The proxy uses curl subprocesses which go
through the system HTTPS proxy (GOST) with a proper TLS fingerprint
that the Aeroflot WAF accepts.

Usage: node scripts/dev-server.mjs (replaces pnpm dev for full-stack)

Also: remove stray e2e-angular test directory, fix env default to
same-origin /api.
2026-04-15 23:04:24 +03:00

1082 lines
41 KiB
TypeScript

import { test, expect } from '@playwright/test';
const BASE_URL = process.env.BASE_URL || 'http://localhost:5173';
// Test flight IDs - using realistic SU (Aeroflot) flight numbers
const TEST_FLIGHTS = {
su1402: 'SU1402', // Moscow to New York
su200: 'SU200', // Moscow to Paris
su500: 'SU500', // Moscow to Tokyo
};
test.describe('Flight Details Page (US-47 to US-49)', () => {
test.describe('US-47: Flight Details Page', () => {
test('should load flight details page with proper layout', async ({ page }) => {
// Navigate to flight details page
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
// Wait for page to load
await page.waitForLoadState('networkidle');
// Check for main page elements
const mainContent = page.locator('main');
await expect(mainContent).toBeVisible();
});
test('should display loading state initially', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
// Check for content after load
await page.waitForLoadState('networkidle');
const content = page.locator('body');
await expect(content).toBeVisible();
});
test('should handle responsive layout on mobile viewport', async ({ page }) => {
// Set mobile viewport
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Check for responsive container
const container = page.locator('[class*="basicInfo"], [class*="statusDisplay"]');
await expect(container.first()).toBeVisible();
});
test('should handle tablet viewport', async ({ page }) => {
// Set tablet viewport
await page.setViewportSize({ width: 768, height: 1024 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const container = page.locator('[class*="basicInfo"], [class*="statusDisplay"]');
await expect(container.first()).toBeVisible();
});
test('should display all layout sections in correct order', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Check for flight number in header
const flightNumber = page.locator('span[class*="flightNumber"]');
await expect(flightNumber).toBeVisible();
});
test('should be accessible with keyboard navigation', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Tab to first interactive element
await page.keyboard.press('Tab');
// Check if focus is on interactive element
const focusedElement = await page.evaluate(() => document.activeElement?.tagName);
expect(['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA']).toContain(focusedElement);
});
test('should render without console errors', async ({ page }) => {
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Filter out expected third-party errors if needed
const appErrors = errors.filter((e) => !e.includes('third-party'));
expect(appErrors.length).toBe(0);
});
});
test.describe('US-48: Basic Flight Information', () => {
test('should display flight number with proper formatting', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Flight number should be visible
const flightNumber = page.locator('span[class*="flightNumber"]');
await expect(flightNumber).toBeVisible();
const text = await flightNumber.textContent();
// Should contain flight number (with or without space: "SU 1402" or "SU1402")
expect(text).toMatch(/SU\s?1402/);
});
test('should display departure time and city', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for departure section
const departureCity = page.locator('[class*="segment"] [class*="city"]').first();
await expect(departureCity).toBeVisible();
const city = await departureCity.textContent();
expect(city).toBeTruthy(); // Should contain city name
});
test('should display arrival time and city', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for arrival section (second city)
const arrivalCity = page.locator('[class*="segment"] [class*="city"]').last();
await expect(arrivalCity).toBeVisible();
const city = await arrivalCity.textContent();
expect(city).toBeTruthy(); // Should contain city name
});
test('should display aircraft type when available', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for aircraft section
const aircraft = page.locator('[class*="aircraft"] [class*="value"]');
if ((await aircraft.count()) > 0) {
const aircraftText = await aircraft.textContent();
expect(aircraftText).toBeTruthy();
}
});
test('should display flight duration', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for duration text
const duration = page.locator('[class*="durationText"]');
if ((await duration.count()) > 0) {
await expect(duration).toBeVisible();
const text = await duration.textContent();
expect(text).toMatch(/\d+h\s*\d+m/);
}
});
test('should format times correctly in locale', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for time elements (HH:MM format)
const timeElements = page.locator('[class*="time"]');
if ((await timeElements.count()) > 0) {
const firstTime = await timeElements.first().textContent();
expect(firstTime).toMatch(/\d{2}:\d{2}/);
}
});
test('should handle flights without aircraft type', async ({ page }) => {
// This test ensures component doesn't crash without aircraft info
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su500}`);
await page.waitForLoadState('networkidle');
// Page should still render
const container = page.locator('[class*="basicInfo"]');
await expect(container).toBeVisible();
});
test('should be responsive on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Check that content is visible and not overlapping
const basicInfo = page.locator('[class*="basicInfo"]');
await expect(basicInfo).toBeVisible();
const boundingBox = await basicInfo.boundingBox();
expect(boundingBox?.width).toBeLessThanOrEqual(375);
});
});
test.describe('US-49: Status and Status Details', () => {
test('should display operational status', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for status display
const statusIndicator = page.locator('[class*="statusIndicator"]');
if ((await statusIndicator.count()) > 0) {
await expect(statusIndicator.first()).toBeVisible();
}
});
test('should display status remarks when available', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su200}`);
await page.waitForLoadState('networkidle');
// Look for remarks section
const remarks = page.locator('[class*="remarks"]');
if ((await remarks.count()) > 0) {
await expect(remarks.first()).toBeVisible();
}
});
test('should display last update timestamp', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for last update section
const lastUpdate = page.locator('[class*="lastUpdate"]');
if ((await lastUpdate.count()) > 0) {
await expect(lastUpdate.first()).toBeVisible();
const text = await lastUpdate.first().textContent();
expect(text).toBeTruthy();
}
});
test('should apply correct status color coding for scheduled status', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Status display should be present
const statusDisplay = page.locator('[class*="statusDisplay"]');
if ((await statusDisplay.count()) > 0) {
await expect(statusDisplay.first()).toBeVisible();
}
});
test('should handle different status types', async ({ page }) => {
// Test with different flight
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su500}`);
await page.waitForLoadState('networkidle');
const statusDisplay = page.locator('[class*="statusDisplay"]');
if ((await statusDisplay.count()) > 0) {
await expect(statusDisplay.first()).toBeVisible();
}
});
test('should display status message in correct locale (Russian)', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Check that page content is in Russian
const html = await page.content();
expect(html).toContain('ru-ru');
});
test('should display status message in correct locale (English)', async ({ page }) => {
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Check that page content is in English
const html = await page.content();
expect(html).toContain('en-us');
});
test('should update status display when status changes', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Get initial status
const statusDisplay = page.locator('[class*="statusDisplay"]');
if ((await statusDisplay.count()) > 0) {
await expect(statusDisplay.first()).toBeVisible();
}
});
test('should format status timestamp in locale-aware manner', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const lastUpdate = page.locator('[class*="lastUpdate"]');
if ((await lastUpdate.count()) > 0) {
const text = await lastUpdate.first().textContent();
// Should contain time in HH:MM format
expect(text).toMatch(/\d{2}:\d{2}/);
}
});
test('should be responsive on mobile viewport', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const statusDisplay = page.locator('[class*="statusDisplay"]');
if ((await statusDisplay.count()) > 0) {
await expect(statusDisplay.first()).toBeVisible();
}
});
});
test.describe('Integration: Full Flight Details Page', () => {
test('should render all components together', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Check for both basic info and status display
const basicInfo = page.locator('[class*="basicInfo"], span[class*="flightNumber"]');
const statusDisplay = page.locator('[class*="statusDisplay"], [class*="statusIndicator"]');
expect(await basicInfo.count()).toBeGreaterThan(0);
});
test('should display information without layout issues', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Check that main content area has reasonable width
const mainContent = page.locator('main, [role="main"]');
const box = await mainContent.first().boundingBox();
expect(box?.width).toBeGreaterThan(0);
});
test('should support page refresh without errors', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Refresh page
await page.reload();
await page.waitForLoadState('networkidle');
// Check that content is still visible
const flightNumber = page.locator('span[class*="flightNumber"]');
await expect(flightNumber).toBeVisible();
});
test('should support switching between locales', async ({ page }) => {
// Start with Russian
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const ruContent = await page.content();
// Switch to English
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const enContent = await page.content();
// Both should have flight details visible
expect(ruContent).toContain('SU1402');
expect(enContent).toContain('SU1402');
});
test('should handle rapid navigation between flights', async ({ page }) => {
// Navigate to first flight
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Navigate to second flight
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su200}`);
await page.waitForLoadState('networkidle');
// Navigate to third flight
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su500}`);
await page.waitForLoadState('networkidle');
// Page should render correctly
const flightNumber = page.locator('span[class*="flightNumber"]');
await expect(flightNumber).toBeVisible();
});
});
test.describe('US-50: Aircraft Information', () => {
test('should display aircraft type', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for aircraft type display
const aircraftType = page.locator('[class*="Aircraft"]');
if ((await aircraftType.count()) > 0) {
await expect(aircraftType.first()).toBeVisible();
}
});
test('should display aircraft registration if available', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for registration section
const registration = page.locator('[class*="registration"]');
if ((await registration.count()) > 0) {
await expect(registration.first()).toBeVisible();
}
});
test('should display seat configuration', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for seats section
const seats = page.locator('[class*="seats"]');
if ((await seats.count()) > 0) {
await expect(seats.first()).toBeVisible();
}
});
test('should be responsive on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const aircraft = page.locator('[class*="Aircraft"]');
if ((await aircraft.count()) > 0) {
const box = await aircraft.first().boundingBox();
expect(box?.width).toBeLessThanOrEqual(375);
}
});
test('should handle missing aircraft data', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su500}`);
await page.waitForLoadState('networkidle');
// Page should still render without errors
const mainContent = page.locator('main');
await expect(mainContent).toBeVisible();
});
});
test.describe('US-51: Airline Information', () => {
test('should display airline name', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for airline name
const airlineName = page.locator('[class*="Airline"]');
if ((await airlineName.count()) > 0) {
const text = await airlineName.first().textContent();
expect(text).toBeTruthy();
}
});
test('should display airline logo', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for airline logo image
const logo = page.locator('[class*="logo"]');
if ((await logo.count()) > 0) {
await expect(logo.first()).toBeVisible();
}
});
test('should display airline IATA code', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for airline code
const airlineCode = page.locator('[class*="code"]');
if ((await airlineCode.count()) > 0) {
const text = await airlineCode.first().textContent();
expect(text).toMatch(/^[A-Z]{2}$/);
}
});
test('should be responsive on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const airline = page.locator('[class*="Airline"]');
if ((await airline.count()) > 0) {
const box = await airline.first().boundingBox();
expect(box?.width).toBeLessThanOrEqual(375);
}
});
test('should support both locales', async ({ page }) => {
// Russian
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const ruAirline = page.locator('[class*="Airline"]');
expect(await ruAirline.count()).toBeGreaterThanOrEqual(0);
// English
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const enAirline = page.locator('[class*="Airline"]');
expect(await enAirline.count()).toBeGreaterThanOrEqual(0);
});
});
test.describe('US-52: Airport Information', () => {
test('should display departure airport code', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for airport codes
const codes = page.locator('[class*="code"]');
const codeCount = await codes.count();
expect(codeCount).toBeGreaterThanOrEqual(1);
});
test('should display departure airport name', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for airport names
const names = page.locator('[class*="name"]');
const nameCount = await names.count();
expect(nameCount).toBeGreaterThanOrEqual(1);
});
test('should display city name', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for city information
const cities = page.locator('[class*="city"]');
const cityCount = await cities.count();
expect(cityCount).toBeGreaterThanOrEqual(1);
});
test('should display terminal information if available', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for terminal section
const terminal = page.locator('[class*="terminal"]');
if ((await terminal.count()) > 0) {
await expect(terminal.first()).toBeVisible();
}
});
test('should be responsive on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const airport = page.locator('[class*="Airport"]');
if ((await airport.count()) > 0) {
const box = await airport.first().boundingBox();
expect(box?.width).toBeLessThanOrEqual(375);
}
});
});
test.describe('US-53: Days of Operation', () => {
test('should display operating days indicator', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for days of operation
const daysSection = page.locator('[class*="Days"]');
if ((await daysSection.count()) > 0) {
await expect(daysSection.first()).toBeVisible();
}
});
test('should display week grid', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for day indicators
const dayItems = page.locator('[class*="dayItem"]');
const dayCount = await dayItems.count();
// Should have 7 days or display days in some form
if (dayCount > 0) {
expect(dayCount).toBeGreaterThanOrEqual(1);
}
});
test('should show operating days highlighted', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for operating day indicators
const operating = page.locator('[class*="operating"]');
if ((await operating.count()) > 0) {
await expect(operating.first()).toBeVisible();
}
});
test('should be responsive on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const days = page.locator('[class*="Days"]');
if ((await days.count()) > 0) {
const box = await days.first().boundingBox();
expect(box?.width).toBeLessThanOrEqual(375);
}
});
});
test.describe('US-54: Flight Actions', () => {
test('should display buy ticket button', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const buyButton = page.locator(
'button:has-text("Buy"), button:has-text("Ticket"), [class*="buyTicket"]',
);
if ((await buyButton.count()) > 0) {
await expect(buyButton.first()).toBeVisible();
}
});
test('should display print button', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const printButton = page.locator('button:has-text("Print"), [class*="printItinerary"]');
if ((await printButton.count()) > 0) {
await expect(printButton.first()).toBeVisible();
}
});
test('should display share button', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const shareButton = page.locator('button:has-text("Share"), [class*="shareFlight"]');
if ((await shareButton.count()) > 0) {
await expect(shareButton.first()).toBeVisible();
}
});
test('should have responsive button layout', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const buttons = page.locator('button');
const count = await buttons.count();
expect(count).toBeGreaterThan(0);
});
test('should work on tablet size', async ({ page }) => {
await page.setViewportSize({ width: 768, height: 1024 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const buttons = page.locator('button');
const count = await buttons.count();
expect(count).toBeGreaterThan(0);
});
});
test.describe('US-55: Route Timeline', () => {
test('should display flight route timeline', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for route/timeline elements
const timeline = page.locator('[class*="timeline"]');
if ((await timeline.count()) > 0) {
await expect(timeline.first()).toBeVisible();
}
});
test('should display departure point', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const departureCodes = page.locator('[class*="code"]');
const count = await departureCodes.count();
expect(count).toBeGreaterThanOrEqual(1);
});
test('should display arrival point', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const arrivalCodes = page.locator('[class*="code"]');
const count = await arrivalCodes.count();
// Should have at least 2 codes (departure and arrival)
expect(count).toBeGreaterThanOrEqual(1);
});
test('should display times on route', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const times = page.locator('[class*="time"]');
const count = await times.count();
expect(count).toBeGreaterThanOrEqual(1);
});
test('should be responsive on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const timeline = page.locator('[class*="timeline"]');
if ((await timeline.count()) > 0) {
const box = await timeline.first().boundingBox();
expect(box?.width).toBeLessThanOrEqual(375);
}
});
});
test.describe('US-56: Transfer Information', () => {
test('should display transfer information section', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for transfer section
const transfer = page.locator('[class*="Transfer"]');
if ((await transfer.count()) > 0) {
await expect(transfer.first()).toBeVisible();
}
});
test('should display layover time if available', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const layover = page.locator('[class*="layover"]');
if ((await layover.count()) > 0) {
await expect(layover.first()).toBeVisible();
}
});
test('should display baggage information if available', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const baggage = page.locator('[class*="baggage"]');
if ((await baggage.count()) > 0) {
await expect(baggage.first()).toBeVisible();
}
});
test('should display visa requirements if needed', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const visa = page.locator('[class*="visa"]');
if ((await visa.count()) > 0) {
await expect(visa.first()).toBeVisible();
}
});
test('should be responsive on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const transfer = page.locator('[class*="Transfer"]');
if ((await transfer.count()) > 0) {
const box = await transfer.first().boundingBox();
expect(box?.width).toBeLessThanOrEqual(375);
}
});
test('should support both locales', async ({ page }) => {
// Russian
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
expect(await page.content()).toContain('ru-ru');
// English
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
expect(await page.content()).toContain('en-us');
});
});
test.describe('US-40: Cabin Services', () => {
test('should display cabin services section when available', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Services may or may not be available depending on flight data
const pageContent = await page.content();
expect(pageContent).toBeDefined();
});
test('should display meal services when included', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su200}`);
await page.waitForLoadState('networkidle');
// Check for meal-related content
const pageContent = await page.content();
expect(pageContent).toBeDefined();
});
test('should show amenities with proper styling', async ({ page }) => {
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su500}`);
await page.waitForLoadState('networkidle');
const pageContent = await page.content();
expect(pageContent).toBeDefined();
});
test('should handle missing services gracefully', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Page should load without errors even if no services
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
expect(errors.length).toBe(0);
});
});
test.describe('US-41: Flight Schedule and Time Information', () => {
test('should display departure and arrival times', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Flight details should contain time information
const pageContent = await page.content();
expect(pageContent).toContain(':'); // Time format includes colons
});
test('should display flight duration', async ({ page }) => {
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su200}`);
await page.waitForLoadState('networkidle');
const pageContent = await page.content();
expect(pageContent).toBeDefined();
});
test('should show timezone information', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su500}`);
await page.waitForLoadState('networkidle');
const pageContent = await page.content();
expect(pageContent).toBeDefined();
});
test('should display operating days information', async ({ page }) => {
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const pageContent = await page.content();
expect(pageContent).toBeDefined();
});
test('should be responsive on mobile viewport', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su200}`);
await page.waitForLoadState('networkidle');
// Schedule info should be visible and properly sized
const viewport = await page.evaluate(() => ({
width: window.innerWidth,
height: window.innerHeight,
}));
expect(viewport.width).toBeLessThanOrEqual(375);
});
});
test.describe('US-62: Share Flight Information', () => {
test('should display share button on flight details page', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
const shareButton = page.locator('button[aria-label*="Share"], button:has-text("Share")');
// Share button might be in FlightActions
const pageContent = await page.content();
expect(pageContent).toBeDefined();
});
test('should handle share button click on desktop', async ({ page }) => {
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su200}`);
await page.waitForLoadState('networkidle');
// Check that page loads without errors
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
expect(errors.length).toBe(0);
});
test('should show mobile-friendly share UI on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su500}`);
await page.waitForLoadState('networkidle');
const viewport = await page.evaluate(() => window.innerWidth);
expect(viewport).toBeLessThanOrEqual(375);
});
test('should support share across different locales', async ({ page }) => {
// Russian
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
let pageContent = await page.content();
expect(pageContent).toBeDefined();
// English
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
pageContent = await page.content();
expect(pageContent).toBeDefined();
});
test('should not break page when share is clicked', async ({ page }) => {
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su200}`);
await page.waitForLoadState('networkidle');
// Try to find and click share button if it exists
const shareBtnLocator = page.locator('button[aria-label*="Share"], button:has-text("Share")');
const buttonCount = await shareBtnLocator.count();
if (buttonCount > 0) {
await shareBtnLocator.first().click();
// Wait for potential feedback
await page.waitForTimeout(500);
}
// Page should still be functional
const main = page.locator('main');
await expect(main).toBeVisible();
});
});
test.describe('US-63: No Errors on Flight Details Page', () => {
test('should have no console errors on page load', async ({ page }) => {
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
expect(errors).toHaveLength(0);
});
test('should have no console warnings on page load', async ({ page }) => {
const warnings: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'warning') {
warnings.push(msg.text());
}
});
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su200}`);
await page.waitForLoadState('networkidle');
// Should have minimal warnings (may have some third-party)
const appWarnings = warnings.filter((w) => !w.includes('third-party'));
expect(appWarnings.length).toBeLessThanOrEqual(0);
});
test('should handle missing flight data without crashing', async ({ page }) => {
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/INVALID`);
await page.waitForLoadState('networkidle');
// May show error message but should not have JS errors
const appErrors = errors.filter((e) => !e.includes('404') && !e.includes('not found'));
expect(appErrors.length).toBeLessThanOrEqual(5);
});
test('should not have rendering errors across viewports', async ({ page }) => {
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
// Mobile
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su500}`);
await page.waitForLoadState('networkidle');
expect(errors).toHaveLength(0);
});
});
test.describe('US-64: Data Integrity', () => {
test('should display all required fields when available', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Check for flight number (always present)
const flightNumberRegex = /[A-Z]{2}\d+/;
const pageContent = await page.content();
expect(pageContent).toMatch(flightNumberRegex);
});
test('should handle missing optional fields gracefully', async ({ page }) => {
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su200}`);
await page.waitForLoadState('networkidle');
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
expect(errors).toHaveLength(0);
});
test('should validate data types are displayed correctly', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su500}`);
await page.waitForLoadState('networkidle');
const pageContent = await page.content();
// Flight details should contain proper data structures
expect(pageContent).toBeDefined();
expect(pageContent.length).toBeGreaterThan(100);
});
test('should not display invalid or malformed data', async ({ page }) => {
await page.goto(`${BASE_URL}/en-us/onlineboard/flights/${TEST_FLIGHTS.su1402}`);
await page.waitForLoadState('networkidle');
// Look for obviously malformed patterns (multiple consecutive special chars, etc)
const pageContent = await page.content();
// eslint-disable-next-line no-useless-escape
const malformedPattern = /[^\w\s\-:,.()\/\\]{3,}/g;
const matches = pageContent.match(malformedPattern) || [];
// Should have minimal malformed data (exclude data URIs, etc)
const actualMalformed = matches.filter((m) => !m.includes('data:'));
expect(actualMalformed.length).toBeLessThanOrEqual(10);
});
test('should handle edge cases with null and empty values', async ({ page }) => {
await page.goto(`${BASE_URL}/ru-ru/onlineboard/flights/${TEST_FLIGHTS.su200}`);
await page.waitForLoadState('networkidle');
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
// Interact with page to trigger any error handling
await page.locator('main').scrollIntoViewIfNeeded();
expect(errors).toHaveLength(0);
});
});
});