375bcfb0fa
Copies Playwright e2e tests (58 specs, 300+ tests) designed for cross-app testing. Adapts API mocks to match real Aeroflot dictionary format (title objects with multilingual keys), adds board/schedule/days endpoint mocks, and provides Angular-specific Playwright config on port 4203.
455 lines
17 KiB
TypeScript
455 lines
17 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
||
import type { Page } from '@playwright/test';
|
||
import {
|
||
buildOnlineBoardPath,
|
||
buildRouteParam,
|
||
searchFlightByNumber,
|
||
verifyFlightCard,
|
||
generateFlight,
|
||
generateFlights,
|
||
getToday,
|
||
getTomorrow,
|
||
CITIES,
|
||
} from '../support/test-utilities';
|
||
|
||
const today = getToday();
|
||
const tomorrow = getTomorrow();
|
||
const dateParam = buildRouteParam('MOW', today);
|
||
const tomorrowParam = buildRouteParam('MOW', tomorrow);
|
||
|
||
// ============================================================================
|
||
// Online Board - Flight Tests
|
||
// ============================================================================
|
||
|
||
test.describe('Online Board - Flight', () => {
|
||
test.describe('Page Navigation', () => {
|
||
test('should navigate to flight details page', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
await expect(page).toHaveURL(new RegExp(`/${slug}`));
|
||
await expect(page).toHaveTitle(new RegExp(flight.flightNumber));
|
||
});
|
||
|
||
test('should navigate to flight details for arrival flight', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'arrival', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
await expect(page).toHaveURL(new RegExp(`/${slug}`));
|
||
});
|
||
|
||
test('should navigate to flight details for different date', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW', date: tomorrow });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${tomorrow.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
await expect(page).toHaveURL(new RegExp(`/${slug}`));
|
||
});
|
||
});
|
||
|
||
test.describe('Flight Information', () => {
|
||
test('should display flight number', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const flightNumber = page.locator('[data-testid="flight-number"]');
|
||
await expect(flightNumber).toBeVisible();
|
||
await expect(flightNumber).toContainText(flight.flightNumber);
|
||
});
|
||
|
||
test('should display airline name', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const airlineName = page.locator('[data-testid="airline-name"]');
|
||
await expect(airlineName).toBeVisible();
|
||
await expect(airlineName).toContainText(flight.airlineName);
|
||
});
|
||
|
||
test('should display aircraft type', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const aircraftType = page.locator('[data-testid="aircraft-type"]');
|
||
await expect(aircraftType).toBeVisible();
|
||
await expect(aircraftType).toContainText(flight.aircraftType || '');
|
||
});
|
||
|
||
test('should display departure city', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const departureCity = page.locator('[data-testid="departure-city"]');
|
||
await expect(departureCity).toBeVisible();
|
||
await expect(departureCity).toContainText(flight.departure.cityName);
|
||
});
|
||
|
||
test('should display arrival city', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const arrivalCity = page.locator('[data-testid="arrival-city"]');
|
||
await expect(arrivalCity).toBeVisible();
|
||
await expect(arrivalCity).toContainText(flight.arrival.cityName);
|
||
});
|
||
|
||
test('should display scheduled departure time', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const depTime = page.locator('[data-testid="scheduled-departure-time"]');
|
||
await expect(depTime).toBeVisible();
|
||
|
||
const depTimeText = flight.departure.time.scheduled.slice(11, 16);
|
||
await expect(depTime).toContainText(depTimeText);
|
||
});
|
||
|
||
test('should display scheduled arrival time', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const arrTime = page.locator('[data-testid="scheduled-arrival-time"]');
|
||
await expect(arrTime).toBeVisible();
|
||
|
||
const arrTimeText = flight.arrival.time.scheduled.slice(11, 16);
|
||
await expect(arrTime).toContainText(arrTimeText);
|
||
});
|
||
|
||
test('should display flight duration', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const duration = page.locator('[data-testid="flight-duration"]');
|
||
await expect(duration).toBeVisible();
|
||
});
|
||
|
||
test('should display flight status', async ({ page }) => {
|
||
const flight = generateFlight({
|
||
direction: 'departure',
|
||
cityCode: 'MOW',
|
||
status: 'scheduled',
|
||
});
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const status = page.locator('[data-testid="flight-status"]');
|
||
await expect(status).toBeVisible();
|
||
await expect(status).toContainText(flight.status);
|
||
});
|
||
});
|
||
|
||
test.describe('Flight Details', () => {
|
||
test('should display terminal information', async ({ page }) => {
|
||
const flight = generateFlight({
|
||
direction: 'departure',
|
||
cityCode: 'MOW',
|
||
departure: { terminal: 'B' },
|
||
});
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const terminal = page.locator('[data-testid="terminal"]');
|
||
await expect(terminal).toBeVisible();
|
||
await expect(terminal).toContainText('B');
|
||
});
|
||
|
||
test('should display boarding gate information', async ({ page }) => {
|
||
const flight = generateFlight({
|
||
direction: 'departure',
|
||
cityCode: 'MOW',
|
||
status: 'boarding',
|
||
boarding: { gate: '11', status: 'Идёт посадка' },
|
||
});
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const boardingGate = page.locator('[data-testid="boarding-gate"]');
|
||
await expect(boardingGate).toBeVisible();
|
||
await expect(boardingGate).toContainText('11');
|
||
});
|
||
|
||
test('should display baggage belt information', async ({ page }) => {
|
||
const flight = generateFlight({
|
||
direction: 'arrival',
|
||
cityCode: 'MOW',
|
||
status: 'arrived',
|
||
arrivalInfo: { baggageBelt: '5', transfer: 'Тран' },
|
||
});
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const baggageBelt = page.locator('[data-testid="baggage-belt"]');
|
||
await expect(baggageBelt).toBeVisible();
|
||
await expect(baggageBelt).toContainText('5');
|
||
});
|
||
|
||
test('should display check-in information', async ({ page }) => {
|
||
const flight = generateFlight({
|
||
direction: 'departure',
|
||
cityCode: 'MOW',
|
||
status: 'checkin',
|
||
checkin: { status: 'В процессе' },
|
||
});
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const checkinInfo = page.locator('[data-testid="checkin-info"]');
|
||
await expect(checkinInfo).toBeVisible();
|
||
});
|
||
|
||
test('should display deplaning information', async ({ page }) => {
|
||
const flight = generateFlight({
|
||
direction: 'arrival',
|
||
cityCode: 'MOW',
|
||
status: 'arrived',
|
||
deplaning: { status: 'В процессе', transfer: 'Трап' },
|
||
});
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const deplaningInfo = page.locator('[data-testid="deplaning-info"]');
|
||
await expect(deplaningInfo).toBeVisible();
|
||
});
|
||
});
|
||
|
||
test.describe('Aircraft Information', () => {
|
||
test('should display aircraft type', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const aircraftType = page.locator('[data-testid="aircraft-type"]');
|
||
await expect(aircraftType).toBeVisible();
|
||
});
|
||
|
||
test('should display aircraft name', async ({ page }) => {
|
||
const flight = generateFlight({
|
||
direction: 'departure',
|
||
cityCode: 'MOW',
|
||
aircraft: { type: 'Airbus A320', name: 'В. Высоцкий' },
|
||
});
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const aircraftName = page.locator('[data-testid="aircraft-name"]');
|
||
await expect(aircraftName).toBeVisible();
|
||
await expect(aircraftName).toContainText('В. Высоцкий');
|
||
});
|
||
|
||
test('should display seat configuration', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const seatInfo = page.locator('[data-testid="seat-info"]');
|
||
await expect(seatInfo).toBeVisible();
|
||
});
|
||
});
|
||
|
||
test.describe('Schedule Information', () => {
|
||
test('should display scheduled departure', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const scheduledDep = page.locator('[data-testid="scheduled-departure"]');
|
||
await expect(scheduledDep).toBeVisible();
|
||
});
|
||
|
||
test('should display scheduled arrival', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const scheduledArr = page.locator('[data-testid="scheduled-arrival"]');
|
||
await expect(scheduledArr).toBeVisible();
|
||
});
|
||
|
||
test('should display actual departure when available', async ({ page }) => {
|
||
const flight = generateFlight({
|
||
direction: 'departure',
|
||
cityCode: 'MOW',
|
||
status: 'departed',
|
||
});
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const actualDep = page.locator('[data-testid="actual-departure"]');
|
||
await expect(actualDep).toBeVisible();
|
||
});
|
||
|
||
test('should display actual arrival when available', async ({ page }) => {
|
||
const flight = generateFlight({
|
||
direction: 'arrival',
|
||
cityCode: 'MOW',
|
||
status: 'arrived',
|
||
});
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const actualArr = page.locator('[data-testid="actual-arrival"]');
|
||
await expect(actualArr).toBeVisible();
|
||
});
|
||
|
||
test('should display expected arrival when delayed', async ({ page }) => {
|
||
const flight = generateFlight({
|
||
direction: 'arrival',
|
||
cityCode: 'MOW',
|
||
status: 'delayed',
|
||
});
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const expectedArr = page.locator('[data-testid="expected-arrival"]');
|
||
await expect(expectedArr).toBeVisible();
|
||
});
|
||
});
|
||
|
||
test.describe('Error Handling', () => {
|
||
test('should handle invalid flight number', async ({ page }) => {
|
||
await page.goto(`/ru-ru/SU9999-${today.replace(/-/g, '')}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const errorState = page.locator('[data-testid="error-state"]');
|
||
await expect(errorState).toBeVisible();
|
||
});
|
||
|
||
test('should handle flight not found', async ({ page }) => {
|
||
await page.goto(`/ru-ru/SU9999-20260406`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const notFound = page.locator('[data-testid="not-found"]');
|
||
await expect(notFound).toBeVisible();
|
||
});
|
||
|
||
test('should handle network error', async ({ page }) => {
|
||
await page.route('**/api/flights/**', (route) => {
|
||
return route.abort('internetdisconnected');
|
||
});
|
||
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const networkError = page.locator('[data-testid="network-error"]');
|
||
await expect(networkError).toBeVisible();
|
||
});
|
||
});
|
||
|
||
test.describe('Navigation', () => {
|
||
test('should navigate back to board', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const backLink = page.locator('[data-testid="back-link"]');
|
||
await backLink.click();
|
||
|
||
await expect(page).toHaveURL(/onlineboard\/departure\/MOW-\d{8}/);
|
||
});
|
||
|
||
test('should navigate to related flights', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const relatedFlights = page.locator('[data-testid="related-flights"]');
|
||
await expect(relatedFlights).toBeVisible();
|
||
});
|
||
});
|
||
|
||
test.describe('Accessibility', () => {
|
||
test('should have proper ARIA labels', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const pageContent = page.locator('[data-testid="page-content"]');
|
||
await expect(pageContent).toHaveAttribute('role', 'main');
|
||
});
|
||
|
||
test('should be keyboard navigable', async ({ page }) => {
|
||
const flight = generateFlight({ direction: 'departure', cityCode: 'MOW' });
|
||
const slug = `${flight.flightNumber.replace(/\s+/g, '')}-${today.replace(/-/g, '')}`;
|
||
|
||
await page.goto(`/ru-ru/${slug}`);
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
await page.keyboard.press('Tab');
|
||
await page.keyboard.press('Tab');
|
||
await page.keyboard.press('Tab');
|
||
|
||
const focusedElement = page.locator(':focus');
|
||
await expect(focusedElement).toBeVisible();
|
||
});
|
||
});
|
||
});
|