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(); }); }); });