Files
gnezim 375bcfb0fa Add e2e test suite from flights-front with Angular API mocks
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.
2026-04-15 23:07:44 +03:00

800 lines
25 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { expect, type Page, type Locator } from '@playwright/test';
import type { Flight, FlightStatus, FlightDirection } from '../../src/entities/flight/types';
import type { ScheduleEntry, ScheduleSearchParams } from '../../src/entities/schedule/types';
import type { Airport } from '../../src/entities/airport/types';
import type { Destination } from '../../src/entities/destination/types';
// ============================================================================
// Test Data Generators
// ============================================================================
export const CITIES = [
{ code: 'MOW', name: 'Moscow', nameRu: 'Москва' },
{ code: 'LED', name: 'Saint Petersburg', nameRu: 'Санкт-Петербург' },
{ code: 'AER', name: 'Sochi', nameRu: 'Сочи' },
{ code: 'OVB', name: 'Novosibirsk', nameRu: 'Новосибирск' },
{ code: 'KRR', name: 'Krasnodar', nameRu: 'Краснодар' },
{ code: 'SVX', name: 'Yekaterinburg', nameRu: 'Екатеринбург' },
{ code: 'KJA', name: 'Krasnoyarsk', nameRu: 'Красноярск' },
{ code: 'GOJ', name: 'Nizhny Novgorod', nameRu: 'Нижний Новгород' },
{ code: 'KUF', name: 'Samara', nameRu: 'Самара' },
{ code: 'UFA', name: 'Ufa', nameRu: 'Уфа' },
{ code: 'KZN', name: 'Kazan', nameRu: 'Казань' },
{ code: 'ROV', name: 'Rostov-on-Don', nameRu: 'Ростов-на-Дону' },
{ code: 'VVO', name: 'Vladivostok', nameRu: 'Владивосток' },
{ code: 'KHV', name: 'Khabarovsk', nameRu: 'Хабаровск' },
{ code: 'IKT', name: 'Irkutsk', nameRu: 'Иркутск' },
{ code: 'OMS', name: 'Omsk', nameRu: 'Омск' },
{ code: 'KGD', name: 'Kaliningrad', nameRu: 'Калининград' },
{ code: 'MRV', name: 'Mineralnye Vody', nameRu: 'Минеральные Воды' },
{ code: 'MCX', name: 'Makhachkala', nameRu: 'Махачкала' },
{ code: 'AAQ', name: 'Anapa', nameRu: 'Анапа' },
] as const;
export const AIRPORTS = [
{ code: 'SVO', name: 'Sheremetyevo', cityCode: 'MOW', cityName: 'Moscow', countryCode: 'RU' },
{ code: 'DME', name: 'Domodedovo', cityCode: 'MOW', cityName: 'Moscow', countryCode: 'RU' },
{ code: 'VKO', name: 'Vnukovo', cityCode: 'MOW', cityName: 'Moscow', countryCode: 'RU' },
{
code: 'LED',
name: 'Pulkovo',
cityCode: 'LED',
cityName: 'Saint Petersburg',
countryCode: 'RU',
},
{ code: 'AER', name: 'Adler', cityCode: 'AER', cityName: 'Sochi', countryCode: 'RU' },
{ code: 'OVB', name: 'Tolmachevo', cityCode: 'OVB', cityName: 'Novosibirsk', countryCode: 'RU' },
{ code: 'KRR', name: 'Pashkovsky', cityCode: 'KRR', cityName: 'Krasnodar', countryCode: 'RU' },
{ code: 'SVX', name: 'Koltsovo', cityCode: 'SVX', cityName: 'Yekaterinburg', countryCode: 'RU' },
{ code: 'KJA', name: 'Emelyanovo', cityCode: 'KJA', cityName: 'Krasnoyarsk', countryCode: 'RU' },
{
code: 'GOJ',
name: 'Strigino',
cityCode: 'GOJ',
cityName: 'Nizhny Novgorod',
countryCode: 'RU',
},
] as const;
export const FLIGHT_NUMBERS = [
'SU 1124',
'SU 1076',
'SU 6170',
'SU 1208',
'SU 1108',
'SU 6245',
'SU 1455',
'SU 1483',
'SU 1759',
'SU 6268',
'SU 6132',
'SU 1525',
'SU 1400',
'SU 1510',
'SU 1190',
'SU 1130',
'SU 1234',
'SU 6310',
'SU 1350',
'SU 1720',
] as const;
export const AIRLINE_CODES = ['SU', 'FV'] as const;
export const AIRLINE_NAMES = {
SU: 'Aeroflot',
FV: 'Rossiya',
} as const;
export const AIRCRAFT_TYPES = [
'Airbus A320',
'Airbus A321',
'Airbus A321neo',
'Boeing 737-800',
'Boeing 777-300',
'Boeing 777-300ER',
'Sukhoi SuperJet 100',
] as const;
export const STATUS_TYPES: FlightStatus[] = [
'scheduled',
'checkin',
'boarding',
'departed',
'inFlight',
'landed',
'arrived',
'delayed',
'cancelled',
'gateChanged',
];
// ============================================================================
// Flight Data Generators
// ============================================================================
export function generateFlightId(): string {
return `fl-${Math.random().toString(36).substring(2, 10)}`;
}
export function generateFlightNumber(): string {
const num = Math.floor(Math.random() * 9000) + 1000;
return `SU ${num}`;
}
export function generateFlight({
direction = 'departure',
date = new Date().toISOString().split('T')[0],
cityCode = 'MOW',
status = 'scheduled',
flightNumber = generateFlightNumber(),
airlineCode = 'SU',
aircraftType = 'Airbus A320',
}: {
direction?: FlightDirection;
date?: string;
cityCode?: string;
status?: FlightStatus;
flightNumber?: string;
airlineCode?: (typeof AIRLINE_CODES)[number];
aircraftType?: (typeof AIRCRAFT_TYPES)[number];
} = {}): Flight {
const depCity = CITIES.find((c) => c.code === cityCode) || CITIES[0];
const arrCity = CITIES.find((c) => c.code !== cityCode) || CITIES[1];
const depAirport = AIRPORTS.find((a) => a.cityCode === cityCode) || AIRPORTS[0];
const arrAirport =
AIRPORTS.find((a) => a.cityCode === arrCity.code && a.code !== depAirport.code) || AIRPORTS[1];
const depTime = `${Math.floor(Math.random() * 23)}:${String(Math.floor(Math.random() * 60)).padStart(2, '0')}`;
const arrTime = `${Math.floor(Math.random() * 23)}:${String(Math.floor(Math.random() * 60)).padStart(2, '0')}`;
const flightId = generateFlightId();
return {
id: flightId,
flightNumber,
airlineCode,
airlineName: AIRLINE_NAMES[airlineCode as keyof typeof AIRLINE_NAMES],
aircraftType,
direction,
status,
date,
departure: {
airportCode: depAirport.code,
airportName: depAirport.name,
cityCode: depCity.code,
cityName: depCity.name,
terminal:
Math.random() > 0.5 ? undefined : ['A', 'B', 'C', 'D'][Math.floor(Math.random() * 4)],
time: {
scheduled: `${date}T${depTime}:00+03:00`,
actual:
status === 'departed' || status === 'inFlight' || status === 'arrived'
? `${date}T${depTime}:00+03:00`
: undefined,
},
},
arrival: {
airportCode: arrAirport.code,
airportName: arrAirport.name,
cityCode: arrCity.code,
cityName: arrCity.name,
terminal: Math.random() > 0.5 ? undefined : ['1', '2', '3'][Math.floor(Math.random() * 3)],
time: {
scheduled: `${date}T${arrTime}:00+03:00`,
actual: status === 'arrived' ? `${date}T${arrTime}:00+03:00` : undefined,
expected: status === 'delayed' ? `${date}T${arrTime}:00+03:00` : undefined,
},
},
boarding:
status === 'boarding' || status === 'departed' || status === 'inFlight'
? {
gate: `${Math.floor(Math.random() * 50) + 1}`,
status: status === 'boarding' ? 'Идёт посадка' : 'Закончена',
startTime: `${date}T${depTime}:00+03:00`,
endTime: `${date}T${depTime}:00+03:00`,
}
: undefined,
arrivalInfo:
status === 'arrived' || status === 'landed'
? {
baggageBelt: `${Math.floor(Math.random() * 10) + 1}`,
transfer: Math.random() > 0.5 ? 'Тран' : undefined,
}
: undefined,
checkin:
status === 'checkin' || status === 'boarding' || status === 'departed'
? {
status: status === 'checkin' ? 'В процессе' : 'Закончена',
startTime: `${date}T${depTime}:00+03:00`,
endTime: `${date}T${depTime}:00+03:00`,
}
: undefined,
deplaning:
status === 'arrived' || status === 'landed'
? {
status: 'В процессе',
startTime: `${date}T${arrTime}:00+03:00`,
endTime: `${date}T${arrTime}:00+03:00`,
transfer: Math.random() > 0.5 ? 'Трап' : undefined,
gate: `${Math.floor(Math.random() * 50) + 1}`,
baggageBelt: `${Math.floor(Math.random() * 10) + 1}`,
}
: undefined,
aircraft: {
type: aircraftType,
name: Math.random() > 0.5 ? `${aircraftType} ${Math.floor(Math.random() * 100)}` : undefined,
totalSeats: Math.floor(Math.random() * 300) + 100,
economySeats: Math.floor(Math.random() * 250) + 100,
businessSeats: Math.floor(Math.random() * 20) + 5,
previousFlight: Math.random() > 0.5 ? generateFlightNumber() : undefined,
},
catering:
Math.random() > 0.5
? {
economy: true,
business: true,
}
: undefined,
schedule: {
scheduledDeparture: `${date}T${depTime}:00+03:00`,
scheduledArrival: `${date}T${arrTime}:00+03:00`,
duration: `${Math.floor(Math.random() * 5) + 1}ч. ${Math.floor(Math.random() * 59)}мин.`,
utcOffset: 'UTC+03:00',
operatingDays: [1, 2, 3, 4, 5, 6, 7],
weekRange: `* Расписание на неделю ${date}`,
},
lastUpdated:
status === 'departed' || status === 'arrived'
? `${depTime} ${date.replace(/-/g, '.')}`
: undefined,
};
}
export function generateFlights(
count: number = 20,
options: Partial<Parameters<typeof generateFlight>[0]> = {},
): Flight[] {
return Array.from({ length: count }, () => generateFlight(options));
}
// ============================================================================
// Schedule Data Generators
// ============================================================================
export function generateScheduleEntry({
from = 'MOW',
to = 'AER',
dateFrom = new Date().toISOString().split('T')[0],
dateTo = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
direct = true,
}: {
from?: string;
to?: string;
dateFrom?: string;
dateTo?: string;
direct?: boolean;
} = {}): ScheduleEntry {
const depCity = CITIES.find((c) => c.code === from) || CITIES[0];
const arrCity = CITIES.find((c) => c.code === to) || CITIES[1];
const depAirport = AIRPORTS.find((a) => a.cityCode === from) || AIRPORTS[0];
const arrAirport = AIRPORTS.find((a) => a.cityCode === to) || AIRPORTS[1];
const depTime = `${Math.floor(Math.random() * 23)}:${String(Math.floor(Math.random() * 60)).padStart(2, '0')}`;
const arrTime = `${Math.floor(Math.random() * 23)}:${String(Math.floor(Math.random() * 60)).padStart(2, '0')}`;
return {
id: generateFlightId(),
flightNumber: generateFlightNumber(),
airlineCode: 'SU',
airlineName: 'Aeroflot',
aircraftType: AIRCRAFT_TYPES[Math.floor(Math.random() * AIRCRAFT_TYPES.length)],
departureCity: depCity.name,
departureCityCode: depCity.code,
departureAirport: depAirport.name,
departureTime: depTime,
arrivalCity: arrCity.name,
arrivalCityCode: arrCity.code,
arrivalAirport: arrAirport.name,
arrivalTime: arrTime,
daysOfWeek: [1, 2, 3, 4, 5, 6, 7],
effectiveFrom: dateFrom,
effectiveTo: dateTo,
direct,
};
}
export function generateScheduleEntries(
count: number = 50,
options: Partial<Parameters<typeof generateScheduleEntry>[0]> = {},
): ScheduleEntry[] {
return Array.from({ length: count }, () => generateScheduleEntry(options));
}
// ============================================================================
// Destination Data Generators
// ============================================================================
export function generateDestination({
departureCity = 'MOW',
arrivalCity = 'AER',
flightCount = Math.floor(Math.random() * 100) + 1,
dates = [new Date().toISOString().split('T')[0]],
}: {
departureCity?: string;
arrivalCity?: string;
flightCount?: number;
dates?: string[];
} = {}): Destination {
const depCity = CITIES.find((c) => c.code === departureCity) || CITIES[0];
const arrCity = CITIES.find((c) => c.code === arrivalCity) || CITIES[1];
return {
id: `dest-${departureCity}-${arrivalCity}`,
departureCity: depCity.name,
departureCityCode: depCity.code,
arrivalCity: arrCity.name,
arrivalCityCode: arrCity.code,
flightCount,
dates,
};
}
export function generateDestinations(count: number = 20): Destination[] {
return Array.from({ length: count }, () => generateDestination());
}
// ============================================================================
// URL Pattern Helpers
// ============================================================================
export function buildRouteParam(cityCode: string, date: string): string {
return `${cityCode}-${date.replace(/-/g, '')}`;
}
export function buildLocalePath(locale: string, path: string): string {
return `/${locale}${path}`;
}
export function buildOnlineBoardPath(
direction: 'departure' | 'arrival',
cityCode: string,
date: string,
): string {
return `/onlineboard/${direction}/${buildRouteParam(cityCode, date)}`;
}
export function buildSchedulePath(): string {
return '/schedule';
}
export function buildFlightsMapPath(): string {
return '/flights-map';
}
export function buildFlightDetailsPath(flightNumber: string, date: string): string {
const slug = `${flightNumber.replace(/\s+/g, '')}-${date.replace(/-/g, '')}`;
return `/${slug}`;
}
// ============================================================================
// Test Assertion Helpers
// ============================================================================
export async function expectUrlToMatch(page: Page, pattern: RegExp | string): Promise<void> {
const url = page.url();
if (typeof pattern === 'string') {
expect(url).toContain(pattern);
} else {
expect(url).toMatch(pattern);
}
}
export async function expectElementToBeVisible(locator: Locator, message?: string): Promise<void> {
await expect(locator).toBeVisible({ timeout: 10000 });
}
export async function expectElementToBeHidden(locator: Locator, message?: string): Promise<void> {
await expect(locator).toBeHidden({ timeout: 10000 });
}
export async function expectElementToHaveText(
locator: Locator,
text: string | RegExp,
message?: string,
): Promise<void> {
await expect(locator).toHaveText(text, { timeout: 10000 });
}
export async function expectElementToContainText(
locator: Locator,
text: string,
message?: string,
): Promise<void> {
await expect(locator).toContainText(text, { timeout: 10000 });
}
export async function expectElementToHaveAttribute(
locator: Locator,
attribute: string,
value: string,
message?: string,
): Promise<void> {
await expect(locator).toHaveAttribute(attribute, value, { timeout: 10000 });
}
export async function expectElementToHaveClass(
locator: Locator,
className: string,
message?: string,
): Promise<void> {
await expect(locator).toHaveClass(new RegExp(className), { timeout: 10000 });
}
export async function expectElementToBeEnabled(locator: Locator, message?: string): Promise<void> {
await expect(locator).toBeEnabled({ timeout: 10000 });
}
export async function expectElementToBeDisabled(locator: Locator, message?: string): Promise<void> {
await expect(locator).toBeDisabled({ timeout: 10000 });
}
export async function expectElementToBeChecked(locator: Locator, message?: string): Promise<void> {
await expect(locator).toBeChecked({ timeout: 10000 });
}
export async function expectElementToBeUnchecked(
locator: Locator,
message?: string,
): Promise<void> {
await expect(locator).not.toBeChecked({ timeout: 10000 });
}
export async function expectElementToHaveValue(
locator: Locator,
value: string,
message?: string,
): Promise<void> {
await expect(locator).toHaveValue(value, { timeout: 10000 });
}
export async function expectElementToHaveCount(
locator: Locator,
count: number,
message?: string,
): Promise<void> {
await expect(locator).toHaveCount(count, { timeout: 10000 });
}
export async function expectElementToBeFocused(locator: Locator, message?: string): Promise<void> {
await expect(locator).toBeFocused({ timeout: 10000 });
}
export async function expectElementNotToBeFocused(
locator: Locator,
message?: string,
): Promise<void> {
await expect(locator).not.toBeFocused({ timeout: 10000 });
}
// ============================================================================
// Flight Search Helpers
// ============================================================================
export async function searchFlightByNumber(
page: Page,
flightNumber: string,
date?: string,
): Promise<void> {
const dateParam = date || new Date().toISOString().split('T')[0];
await page.goto(`/ru-ru/onlineboard/departure/MOW-${buildRouteParam('MOW', dateParam)}`);
await page.waitForLoadState('networkidle');
const searchInput = page.locator('[data-testid="flight-search-input"]');
await searchInput.fill(flightNumber);
await searchInput.press('Enter');
await page.waitForLoadState('networkidle');
}
export async function searchFlightByRoute(
page: Page,
departureCity: string,
arrivalCity: string,
date?: string,
): Promise<void> {
const dateParam = date || new Date().toISOString().split('T')[0];
await page.goto(`/ru-ru/onlineboard/departure/MOW-${buildRouteParam('MOW', dateParam)}`);
await page.waitForLoadState('networkidle');
const routeTab = page.locator('[data-testid="route-search-tab"]');
await routeTab.click();
await page.waitForTimeout(500);
const departureInput = page.locator('[data-testid="departure-city-input"]');
const arrivalInput = page.locator('[data-testid="arrival-city-input"]');
await departureInput.fill(departureCity);
await page.waitForTimeout(500);
await departureInput.press('Enter');
await page.waitForTimeout(500);
await arrivalInput.fill(arrivalCity);
await page.waitForTimeout(500);
await arrivalInput.press('Enter');
await page.waitForLoadState('networkidle');
}
export async function searchFlightByDate(page: Page, date: string): Promise<void> {
await page.goto(`/ru-ru/onlineboard/departure/MOW-${buildRouteParam('MOW', date)}`);
await page.waitForLoadState('networkidle');
}
export async function openFlightDetails(page: Page, flightIndex: number = 0): Promise<void> {
const flightCards = page.locator('[data-testid="flight-card"]');
await expect(flightCards).toHaveCount(flightIndex + 1, { timeout: 10000 });
await flightCards.nth(flightIndex).click();
await page.waitForLoadState('networkidle');
}
export async function verifyFlightCard(
page: Page,
flight: Flight,
index: number = 0,
): Promise<void> {
const flightCards = page.locator('[data-testid="flight-card"]');
const count = await flightCards.count();
await expect(flightCards).toHaveCount(count);
const card = flightCards.nth(index);
await expect(card.getByText(flight.flightNumber)).toBeVisible();
await expect(card.getByText(flight.airlineName)).toBeVisible();
await expect(card.getByText(flight.departure.cityName)).toBeVisible();
await expect(card.getByText(flight.arrival.cityName)).toBeVisible();
const depTime = flight.departure.time.scheduled.slice(11, 16);
await expect(card.getByText(depTime)).toBeVisible();
const arrTime = flight.arrival.time.scheduled.slice(11, 16);
await expect(card.getByText(arrTime)).toBeVisible();
}
export async function verifyFlightDetails(page: Page, flight: Flight): Promise<void> {
await expect(page.getByText(flight.flightNumber)).toBeVisible();
await expect(page.getByText(flight.airlineName)).toBeVisible();
await expect(page.getByText(flight.departure.cityName)).toBeVisible();
await expect(page.getByText(flight.arrival.cityName)).toBeVisible();
if (flight.aircraft?.type) {
await expect(page.getByText(flight.aircraft.type)).toBeVisible();
}
if (flight.schedule?.duration) {
await expect(page.getByText(flight.schedule.duration)).toBeVisible();
}
}
// ============================================================================
// Date Helpers
// ============================================================================
export function formatDateForUrl(date: string | Date): string {
const d = typeof date === 'string' ? date : date.toISOString().split('T')[0];
return d.replace(/-/g, '');
}
export function formatDateForDisplay(date: string | Date, locale: string = 'ru'): string {
const d = typeof date === 'string' ? date : date.toISOString().split('T')[0];
const dateObj = new Date(d);
const options: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'short' };
return new Date(d).toLocaleDateString(locale, options);
}
export function getToday(): string {
return new Date().toISOString().split('T')[0];
}
export function getTomorrow(): string {
const d = new Date();
d.setDate(d.getDate() + 1);
return d.toISOString().split('T')[0];
}
export function getYesterday(): string {
const d = new Date();
d.setDate(d.getDate() - 1);
return d.toISOString().split('T')[0];
}
export function getFutureDate(days: number): string {
const d = new Date();
d.setDate(d.getDate() + days);
return d.toISOString().split('T')[0];
}
export function getPastDate(days: number): string {
const d = new Date();
d.setDate(d.getDate() - days);
return d.toISOString().split('T')[0];
}
// ============================================================================
// Error Response Generators
// ============================================================================
export function generateNotFoundError(): {
status: number;
body: { error: string; message: string };
} {
return {
status: 404,
body: {
error: 'Not Found',
message: 'The requested resource was not found',
},
};
}
export function generateBadRequestError(): {
status: number;
body: { error: string; message: string };
} {
return {
status: 400,
body: {
error: 'Bad Request',
message: 'Invalid request parameters',
},
};
}
export function generateUnauthorizedError(): {
status: number;
body: { error: string; message: string };
} {
return {
status: 401,
body: {
error: 'Unauthorized',
message: 'Authentication required',
},
};
}
export function generateForbiddenError(): {
status: number;
body: { error: string; message: string };
} {
return {
status: 403,
body: {
error: 'Forbidden',
message: 'Access denied',
},
};
}
export function generateServerError(): {
status: number;
body: { error: string; message: string };
} {
return {
status: 500,
body: {
error: 'Internal Server Error',
message: 'An unexpected error occurred',
},
};
}
export function generateTimeoutError(): {
status: number;
body: { error: string; message: string };
} {
return {
status: 504,
body: {
error: 'Gateway Timeout',
message: 'The request took too long to process',
},
};
}
// ============================================================================
// Async Wait Utilities (for complex async operations)
// ============================================================================
/**
* Wait for an element with an extended timeout for slow async operations.
* Used for map initialization, calendar loading, etc.
*/
export async function waitForElementExtended(
page: Page,
selector: string,
timeoutMs: number = 20000,
): Promise<void> {
try {
await page.locator(selector).first().waitFor({ timeout: timeoutMs });
} catch (error) {
// If element doesn't appear, log but don't fail - test will fail naturally if needed
console.log(`Extended wait for "${selector}" timed out after ${timeoutMs}ms`);
}
}
/**
* Wait for a locator with extended timeout.
*/
export async function waitForLocatorExtended(
locator: Locator,
timeoutMs: number = 20000,
): Promise<void> {
try {
await locator.first().waitFor({ timeout: timeoutMs, state: 'visible' });
} catch (error) {
// If element doesn't appear, log but don't fail
console.log(`Extended wait for locator timed out after ${timeoutMs}ms`);
}
}
/**
* Retry an async operation with backoff.
* Useful for flaky async operations or race conditions.
*/
export async function retryAsync<T>(
fn: () => Promise<T>,
maxRetries: number = 3,
delayMs: number = 500,
): Promise<T> {
let lastError: Error | undefined;
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (i < maxRetries - 1) {
await new Promise((resolve) => setTimeout(resolve, delayMs * (i + 1)));
}
}
}
throw lastError || new Error('Retry exhausted');
}
// ============================================================================
// Test Data Fixtures
// ============================================================================
export const FIXTURES = {
flights: {
departures: generateFlights(20, { direction: 'departure', cityCode: 'MOW' }),
arrivals: generateFlights(20, { direction: 'arrival', cityCode: 'MOW' }),
scheduled: generateFlights(20, { status: 'scheduled' }),
departed: generateFlights(20, { status: 'departed' }),
delayed: generateFlights(20, { status: 'delayed' }),
cancelled: generateFlights(20, { status: 'cancelled' }),
},
schedule: {
entries: generateScheduleEntries(50),
},
destinations: {
entries: generateDestinations(20),
},
airports: {
entries: AIRPORTS,
},
cities: {
entries: CITIES,
},
errors: {
notFound: generateNotFoundError(),
badRequest: generateBadRequestError(),
unauthorized: generateUnauthorizedError(),
forbidden: generateForbiddenError(),
serverError: generateServerError(),
timeout: generateTimeoutError(),
},
};
export default FIXTURES;