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.
188 lines
7.5 KiB
TypeScript
188 lines
7.5 KiB
TypeScript
/**
|
|
* Canonical data-testid selector map for cross-app e2e tests.
|
|
*
|
|
* Both Angular and React apps must implement these testids.
|
|
* Where Angular uses a different testid, add an entry to ANGULAR_OVERRIDES.
|
|
*/
|
|
|
|
export const S = {
|
|
// Navigation & Layout
|
|
NAV_ONLINEBOARD_TAB: 'nav-onlineboard-tab',
|
|
NAV_SCHEDULE_TAB: 'nav-schedule-tab',
|
|
NAV_FLIGHTS_MAP_TAB: 'nav-flights-map-tab',
|
|
LAYOUT_BREADCRUMBS: 'layout-breadcrumbs',
|
|
LAYOUT_FEEDBACK_BUTTON: 'layout-feedback-button',
|
|
LAYOUT_SCROLL_TOP_BUTTON: 'layout-scroll-top-button',
|
|
LAYOUT_LOCALE_SWITCHER: 'layout-locale-switcher',
|
|
LAYOUT_LOCALE_OPTION: 'layout-locale-option',
|
|
|
|
// Online Board - Filter
|
|
FILTER_ACCORDION: 'filter-accordion',
|
|
FILTER_FLIGHT_TAB: 'filter-flight-tab',
|
|
FILTER_ROUTE_TAB: 'filter-route-tab',
|
|
FILTER_FLIGHT_NUMBER_INPUT: 'filter-flight-number-input',
|
|
FILTER_FLIGHT_NUMBER_CLEAR: 'filter-flight-number-clear',
|
|
FILTER_FLIGHT_NUMBER_CALENDAR: 'filter-flight-number-calendar',
|
|
FILTER_FLIGHT_NUMBER_SEARCH: 'filter-flight-number-search',
|
|
FILTER_ROUTE_DEPARTURE_INPUT: 'filter-route-departure-input',
|
|
FILTER_ROUTE_ARRIVAL_INPUT: 'filter-route-arrival-input',
|
|
FILTER_ROUTE_SWAP_BUTTON: 'filter-route-swap-button',
|
|
FILTER_ROUTE_CALENDAR: 'filter-route-calendar',
|
|
FILTER_ROUTE_TIME_SELECTOR: 'filter-route-time-selector',
|
|
FILTER_ROUTE_SEARCH: 'filter-route-search',
|
|
|
|
// Online Board - Results
|
|
BOARD_DAY_TABS: 'board-day-tabs',
|
|
BOARD_DAY_TAB: 'board-day-tab',
|
|
BOARD_TIME_SELECTOR: 'board-time-selector',
|
|
BOARD_SEARCH_RESULT: 'board-search-result',
|
|
BOARD_FLIGHT_RESULT: 'board-flight-result',
|
|
BOARD_FLIGHT_NUMBER: 'board-flight-number',
|
|
BOARD_FLIGHT_STATUS: 'board-flight-status',
|
|
BOARD_FLIGHT_EXPAND: 'board-flight-expand',
|
|
BOARD_LOADER: 'board-loader',
|
|
BOARD_EMPTY_LIST: 'board-empty-list',
|
|
BOARD_CANCEL_BUTTON: 'board-cancel-button',
|
|
|
|
// Flight Details
|
|
DETAILS_FLIGHT_NUMBER: 'details-flight-number',
|
|
DETAILS_DEPARTURE_STATION: 'details-departure-station',
|
|
DETAILS_ARRIVAL_STATION: 'details-arrival-station',
|
|
DETAILS_DEPARTURE_TIME: 'details-departure-time',
|
|
DETAILS_ARRIVAL_TIME: 'details-arrival-time',
|
|
DETAILS_STATUS: 'details-status',
|
|
DETAILS_DURATION: 'details-duration',
|
|
DETAILS_OPERATOR_LOGO: 'details-operator-logo',
|
|
DETAILS_AIRCRAFT_MODEL: 'details-aircraft-model',
|
|
DETAILS_PRINT_BUTTON: 'details-print-button',
|
|
DETAILS_SHARE_BUTTON: 'details-share-button',
|
|
DETAILS_BUY_TICKET_BUTTON: 'details-buy-ticket-button',
|
|
DETAILS_REGISTRATION_BUTTON: 'details-registration-button',
|
|
DETAILS_FLIGHT_STATUS_BUTTON: 'details-flight-status-button',
|
|
DETAILS_TRANSFER_SECTION: 'details-transfer-section',
|
|
DETAILS_FULL_ROUTE: 'details-full-route',
|
|
DETAILS_TERMINAL_LINK: 'details-terminal-link',
|
|
|
|
// Landing Page
|
|
LANDING_SECTION: 'landing-section',
|
|
LANDING_POPULAR_REQUEST: 'landing-popular-request',
|
|
LANDING_SEARCH_HISTORY: 'landing-search-history',
|
|
LANDING_SEARCH_HISTORY_ITEM: 'landing-search-history-item',
|
|
|
|
// Schedule - Filter
|
|
SCHEDULE_DEPARTURE_INPUT: 'schedule-departure-input',
|
|
SCHEDULE_ARRIVAL_INPUT: 'schedule-arrival-input',
|
|
SCHEDULE_SWAP_BUTTON: 'schedule-swap-button',
|
|
SCHEDULE_CALENDAR: 'schedule-calendar',
|
|
SCHEDULE_RETURN_CALENDAR: 'schedule-return-calendar',
|
|
SCHEDULE_TIME_SELECTOR: 'schedule-time-selector',
|
|
SCHEDULE_RETURN_TIME_SELECTOR: 'schedule-return-time-selector',
|
|
SCHEDULE_DIRECT_ONLY_CHECKBOX: 'schedule-direct-only-checkbox',
|
|
SCHEDULE_RETURN_CHECKBOX: 'schedule-return-checkbox',
|
|
SCHEDULE_SEARCH_BUTTON: 'schedule-search-button',
|
|
|
|
// Schedule - Results
|
|
SCHEDULE_WEEK_TABS: 'schedule-week-tabs',
|
|
SCHEDULE_WEEK_TAB: 'schedule-week-tab',
|
|
SCHEDULE_WEEK_PREV: 'schedule-week-prev',
|
|
SCHEDULE_WEEK_NEXT: 'schedule-week-next',
|
|
SCHEDULE_DIRECTION_SWITCH: 'schedule-direction-switch',
|
|
SCHEDULE_SORT_DROPDOWN: 'schedule-sort-dropdown',
|
|
SCHEDULE_FLIGHT_DAY: 'schedule-flight-day',
|
|
SCHEDULE_FLIGHT_ITEM: 'schedule-flight-item',
|
|
SCHEDULE_LOADER: 'schedule-loader',
|
|
|
|
// Schedule - Details
|
|
SCHEDULE_DETAILS_BACK_BUTTON: 'schedule-details-back-button',
|
|
SCHEDULE_DETAILS_DAY_TABS: 'schedule-details-day-tabs',
|
|
SCHEDULE_DETAILS_FLIGHT_MINI: 'schedule-details-flight-mini',
|
|
SCHEDULE_DETAILS_TRANSFER: 'schedule-details-transfer',
|
|
|
|
// Flights Map
|
|
MAP_CONTAINER: 'map-container',
|
|
MAP_DEPARTURE_INPUT: 'map-departure-input',
|
|
MAP_ARRIVAL_INPUT: 'map-arrival-input',
|
|
MAP_SWAP_BUTTON: 'map-swap-button',
|
|
MAP_CALENDAR: 'map-calendar',
|
|
MAP_DOMESTIC_TOGGLE: 'map-domestic-toggle',
|
|
MAP_INTERNATIONAL_TOGGLE: 'map-international-toggle',
|
|
MAP_CONNECTING_TOGGLE: 'map-connecting-toggle',
|
|
MAP_MARKER: 'map-marker',
|
|
MAP_MARKER_CLUSTER: 'map-marker-cluster',
|
|
|
|
// Shared Components
|
|
CITY_AUTOCOMPLETE_INPUT: 'city-autocomplete-input',
|
|
CITY_AUTOCOMPLETE_POPUP: 'city-autocomplete-popup',
|
|
CITY_AUTOCOMPLETE_CLEAR: 'city-autocomplete-clear',
|
|
CITY_AUTOCOMPLETE_OPTION: 'city-autocomplete-option',
|
|
CITY_CODE_DISPLAY: 'city-code-display',
|
|
TIME_SELECTOR_FROM: 'time-selector-from',
|
|
TIME_SELECTOR_TO: 'time-selector-to',
|
|
TIME_SELECTOR_TRACK: 'time-selector-track',
|
|
CALENDAR_INPUT: 'calendar-input',
|
|
CALENDAR_CLEAR: 'calendar-clear',
|
|
|
|
// Error Pages
|
|
ERROR_PAGE_404: 'error-page-404',
|
|
ERROR_PAGE_GENERIC: 'error-page-generic',
|
|
ERROR_PAGE_HOME_LINK: 'error-page-home-link',
|
|
} as const;
|
|
|
|
export type SelectorKey = keyof typeof S;
|
|
|
|
/**
|
|
* Angular app uses different testid names in some places.
|
|
* This map translates canonical names to Angular-specific ones.
|
|
*/
|
|
const ANGULAR_OVERRIDES: Partial<Record<string, string>> = {
|
|
[S.NAV_ONLINEBOARD_TAB]: 'onlineboard-tab',
|
|
[S.NAV_SCHEDULE_TAB]: 'schedule-tab',
|
|
[S.NAV_FLIGHTS_MAP_TAB]: 'flights-map-tab',
|
|
[S.FILTER_FLIGHT_TAB]: 'flight-filter',
|
|
[S.FILTER_ROUTE_TAB]: 'route-filter',
|
|
[S.FILTER_FLIGHT_NUMBER_INPUT]: 'flight-number-input',
|
|
[S.FILTER_FLIGHT_NUMBER_CLEAR]: 'flight-number-clear-button',
|
|
[S.FILTER_FLIGHT_NUMBER_CALENDAR]: 'flight-number-calendar',
|
|
[S.FILTER_FLIGHT_NUMBER_SEARCH]: 'flight-number-search-button',
|
|
[S.FILTER_ROUTE_DEPARTURE_INPUT]: 'route-departure-city-input',
|
|
[S.FILTER_ROUTE_ARRIVAL_INPUT]: 'route-arrival-city-input',
|
|
[S.FILTER_ROUTE_CALENDAR]: 'route-calendar-input',
|
|
[S.FILTER_ROUTE_SEARCH]: 'route-search-button',
|
|
[S.SCHEDULE_DEPARTURE_INPUT]: 'schedule-departure-city-input',
|
|
[S.SCHEDULE_ARRIVAL_INPUT]: 'schedule-arrival-city-input',
|
|
[S.SCHEDULE_SEARCH_BUTTON]: 'schedule-search-button',
|
|
[S.MAP_DEPARTURE_INPUT]: 'route-departure-city-input',
|
|
[S.MAP_ARRIVAL_INPUT]: 'route-arrival-city-input',
|
|
[S.MAP_CALENDAR]: 'route-calendar-input',
|
|
[S.BOARD_LOADER]: 'loader',
|
|
[S.BOARD_SEARCH_RESULT]: 'board-search-result',
|
|
[S.BOARD_FLIGHT_RESULT]: 'flight-result',
|
|
[S.BOARD_FLIGHT_NUMBER]: 'flight-carrier-number',
|
|
[S.DETAILS_FLIGHT_NUMBER]: 'flight-details-number',
|
|
[S.CITY_AUTOCOMPLETE_INPUT]: 'city-autocomplete-input',
|
|
[S.CITY_AUTOCOMPLETE_CLEAR]: 'autocomplete-clear-input',
|
|
[S.CITY_AUTOCOMPLETE_POPUP]: 'autocomplete-popup-button',
|
|
[S.CITY_CODE_DISPLAY]: 'city-code',
|
|
[S.CALENDAR_INPUT]: 'calendar-input',
|
|
};
|
|
|
|
/**
|
|
* Get the data-testid selector string for a given app.
|
|
* Returns `[data-testid="..."]` ready for use with page.locator().
|
|
*/
|
|
export function tid(name: string, app: 'angular' | 'react'): string {
|
|
const testid = app === 'angular' && ANGULAR_OVERRIDES[name] ? ANGULAR_OVERRIDES[name] : name;
|
|
return `[data-testid="${testid}"]`;
|
|
}
|
|
|
|
/**
|
|
* Shorthand: get locator from page using canonical testid.
|
|
*/
|
|
export function byTestId(
|
|
page: { locator: (s: string) => unknown },
|
|
name: string,
|
|
app: 'angular' | 'react',
|
|
) {
|
|
return page.locator(tid(name, app));
|
|
}
|