Files
flights_web/ClientApp/cypress/integration/features/schedule.cy.ts

641 lines
27 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 * as moment from 'moment';
/**
* Mock schedule results for testing
*/
const MOCK_SCHEDULE_RESULTS = [
{
flightNumber: 'SU1001',
carrier: 'SU',
number: '1001',
departure: 'Москва',
departureCode: 'MOW',
arrival: 'Санкт-Петербург',
arrivalCode: 'LED',
departureTime: '09:00',
arrivalTime: '10:30',
duration: '1h 30m',
aircraft: 'A320',
price: 3500,
stops: 0,
operating: 'SU',
flightStatus: 'On Schedule',
},
{
flightNumber: 'SU1002',
carrier: 'SU',
number: '1002',
departure: 'Москва',
departureCode: 'MOW',
arrival: 'Санкт-Петербург',
arrivalCode: 'LED',
departureTime: '12:15',
arrivalTime: '13:45',
duration: '1h 30m',
aircraft: 'A330',
price: 4200,
stops: 0,
operating: 'SU',
flightStatus: 'On Schedule',
},
{
flightNumber: 'SU1003',
carrier: 'SU',
number: '1003',
departure: 'Москва',
departureCode: 'MOW',
arrival: 'Санкт-Петербург',
arrivalCode: 'LED',
departureTime: '15:30',
arrivalTime: '17:00',
duration: '1h 30m',
aircraft: 'B737',
price: 2800,
stops: 0,
operating: 'SU',
flightStatus: 'On Schedule',
},
{
flightNumber: 'SU1004',
carrier: 'SU',
number: '1004',
departure: 'Москва',
departureCode: 'MOW',
arrival: 'Санкт-Петербург',
arrivalCode: 'LED',
departureTime: '18:45',
arrivalTime: '20:15',
duration: '1h 30m',
aircraft: 'A320',
price: 3100,
stops: 0,
operating: 'SU',
flightStatus: 'On Schedule',
},
{
flightNumber: 'SU1005',
carrier: 'SU',
number: '1005',
departure: 'Москва',
departureCode: 'MOW',
arrival: 'Санкт-Петербург',
arrivalCode: 'LED',
departureTime: '21:00',
arrivalTime: '22:30',
duration: '1h 30m',
aircraft: 'A320',
price: 3000,
stops: 0,
operating: 'SU',
flightStatus: 'On Schedule',
},
];
const MOCK_FLIGHT_DETAILS = {
flightNumber: 'SU1001',
carrier: 'SU',
number: '1001',
departure: 'Москва',
departureCode: 'MOW',
departureTime: '09:00',
departureTerminal: 'A',
departureGate: '5',
departureCheckIn: '07:00-08:45',
arrival: 'Санкт-Петербург',
arrivalCode: 'LED',
arrivalTime: '10:30',
arrivalTerminal: 'B',
arrivalGate: '12',
duration: '1h 30m',
aircraft: 'A320',
aircraftCode: 'A20',
boardingTime: '08:30',
price: 3500,
stops: 0,
operating: 'SU',
flightStatus: 'On Schedule',
};
describe('Расписание: Комплексные тесты', () => {
const route = {
departureCity: {
name: 'Москва',
code: 'MOW',
latitude: 55.7558,
longitude: 37.62,
},
arrivalCity: {
name: 'Санкт-Петербург',
code: 'LED',
latitude: 59.9311,
longitude: 30.3609,
},
alternateArrivalCity: {
name: 'Сочи',
code: 'AER',
latitude: 43.4391,
longitude: 39.9566,
},
};
beforeEach(() => {
cy.intercept('GET', '**/api/flights/1/ru/schedule**', MOCK_SCHEDULE_RESULTS).as('getSchedule');
cy.intercept('GET', '**/api/flights/1/ru/schedule/details**', MOCK_FLIGHT_DETAILS).as('getFlightDetails');
cy.intercept('GET', '**/api/cities/**', {
statusCode: 200,
body: [route.departureCity, route.arrivalCity, route.alternateArrivalCity],
}).as('getCities');
cy.mockGeolocation(route.departureCity);
cy.visit('/ru-ru/schedule');
});
// ============================================================
// SEARCH PAGE TESTS (~25 tests)
// ============================================================
describe('Search Page - Origin Autocomplete', () => {
it('Should allow manual entry of origin city', () => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.getByTestId('schedule-departure-city-input').getByTestId('city-code').should('contain', 'MOW');
});
it('Should filter origin cities as user types', () => {
cy.getByTestId('schedule-departure-city-input').type('М');
cy.getByTestId('city-dropdown-option').should('have.length.at.least', 1);
});
it('Should select origin city from dropdown', () => {
cy.getByTestId('schedule-departure-city-input').type('Мо');
cy.getByTestId('city-dropdown-option').first().click();
cy.getByTestId('schedule-departure-city-input').getByTestId('city-code').should('contain', 'MOW');
});
it('Should clear origin city selection', () => {
cy.getByTestId('schedule-departure-city-input').type('Москва');
cy.getByTestId('schedule-departure-city-input').parent().find('[class*="clear"]').click({ force: true });
cy.getByTestId('schedule-departure-city-input').should('have.value', '');
});
it('Should validate that origin city is required', () => {
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург');
cy.getByTestId('schedule-search-button').click();
cy.getByTestId('validation-error').should('be.visible');
});
it('Should display origin city code after selection', () => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.getByTestId('city-code').contains('MOW').should('be.visible');
});
it('Should handle rapid typing in origin field', () => {
cy.getByTestId('schedule-departure-city-input').type('М', { delay: 10 }).type('о', { delay: 10 });
cy.getByTestId('city-dropdown-option').should('have.length.at.least', 1);
});
it('Should preserve origin city when navigating to details', () => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getSchedule');
cy.getByTestId('schedule-search-result').first().click();
cy.url().should('include', 'details');
});
});
describe('Search Page - Destination Autocomplete', () => {
it('Should allow manual entry of destination city', () => {
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-arrival-city-input').getByTestId('city-code').should('contain', 'LED');
});
it('Should filter destination cities as user types', () => {
cy.getByTestId('schedule-arrival-city-input').type('С');
cy.getByTestId('city-dropdown-option').should('have.length.at.least', 1);
});
it('Should select destination city from dropdown', () => {
cy.getByTestId('schedule-arrival-city-input').type('Са');
cy.getByTestId('city-dropdown-option').first().click();
cy.getByTestId('schedule-arrival-city-input').getByTestId('city-code').should('be.visible');
});
it('Should clear destination city selection', () => {
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург');
cy.getByTestId('schedule-arrival-city-input').parent().find('[class*="clear"]').click({ force: true });
cy.getByTestId('schedule-arrival-city-input').should('have.value', '');
});
it('Should validate that destination city is required', () => {
cy.getByTestId('schedule-departure-city-input').type('Москва');
cy.getByTestId('schedule-search-button').click();
cy.getByTestId('validation-error').should('be.visible');
});
it('Should prevent same city for origin and destination', () => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Москва').type('{enter}');
cy.getByTestId('validation-error').should('contain', 'одинаков');
});
it('Should display destination city code after selection', () => {
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('city-code').contains('LED').should('be.visible');
});
});
describe('Search Page - Date Range Picker', () => {
it('Should set start date using date picker', () => {
const startDate = moment().format('DD.MM.YYYY');
cy.getByTestId('schedule-calendar').first().clear().type(startDate).type('{enter}');
cy.getByTestId('schedule-calendar').first().should('have.value', startDate);
});
it('Should set end date using date picker', () => {
const endDate = moment().add(7, 'days').format('DD.MM.YYYY');
cy.getByTestId('schedule-calendar').last().clear().type(endDate).type('{enter}');
cy.getByTestId('schedule-calendar').last().should('have.value', endDate);
});
it('Should allow single-day range', () => {
const singleDate = moment().format('DD.MM.YYYY');
cy.getByTestId('schedule-calendar').first().clear().type(singleDate).type('{enter}');
cy.getByTestId('schedule-calendar').last().clear().type(singleDate).type('{enter}');
cy.getByTestId('schedule-calendar').first().should('have.value', singleDate);
cy.getByTestId('schedule-calendar').last().should('have.value', singleDate);
});
it('Should allow full range selection (7 days)', () => {
const startDate = moment().format('DD.MM.YYYY');
const endDate = moment().add(7, 'days').format('DD.MM.YYYY');
cy.getByTestId('schedule-calendar').first().clear().type(startDate).type('{enter}');
cy.getByTestId('schedule-calendar').last().clear().type(endDate).type('{enter}');
cy.getByTestId('schedule-calendar').first().should('have.value', startDate);
cy.getByTestId('schedule-calendar').last().should('have.value', endDate);
});
it('Should reject end date before start date', () => {
const endDate = moment().format('DD.MM.YYYY');
const startDate = moment().add(7, 'days').format('DD.MM.YYYY');
cy.getByTestId('schedule-calendar').first().clear().type(startDate).type('{enter}');
cy.getByTestId('schedule-calendar').last().clear().type(endDate).type('{enter}');
cy.getByTestId('validation-error').should('be.visible');
});
it('Should use today as default start date', () => {
cy.getByTestId('schedule-calendar').first().should('have.value', moment().format('DD.MM.YYYY'));
});
it('Should prevent date in the past', () => {
const pastDate = moment().subtract(1, 'days').format('DD.MM.YYYY');
cy.getByTestId('schedule-calendar').first().clear().type(pastDate).type('{enter}');
cy.getByTestId('validation-error').should('be.visible');
});
it('Should allow date selection via calendar popup', () => {
cy.getByTestId('schedule-calendar').first().click();
cy.get('[class*="calendar"]').find('[class*="day"]').contains(moment().date().toString()).click({ force: true });
cy.getByTestId('schedule-calendar').first().should('have.value', moment().format('DD.MM.YYYY'));
});
});
describe('Search Page - Form Submission', () => {
it('Should submit valid search form', () => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getSchedule');
cy.getByTestId('schedule-search-results').should('be.visible');
});
it('Should show loading indicator during search', () => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.getByTestId('loader').should('be.visible');
});
it('Should display error on network failure', () => {
cy.intercept('GET', '**/api/flights/1/ru/schedule**', { statusCode: 500 }).as('getScheduleError');
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getScheduleError');
cy.getByTestId('error-message').should('be.visible');
});
it('Should handle empty search results', () => {
cy.intercept('GET', '**/api/flights/1/ru/schedule**', []).as('getScheduleEmpty');
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getScheduleEmpty');
cy.getByTestId('empty-results-message').should('be.visible');
});
it('Should not submit with missing origin city', () => {
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.get('@getSchedule.all').should('have.length', 0);
});
it('Should not submit with missing destination city', () => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.get('@getSchedule.all').should('have.length', 0);
});
it('Should display correct URL after search', () => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getSchedule');
cy.url().should('include', 'schedule');
});
it('Should enable search button only when form is valid', () => {
cy.getByTestId('schedule-search-button').should('be.disabled');
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-search-button').should('be.disabled');
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-search-button').should('be.enabled');
});
});
// ============================================================
// FLIGHT DETAILS PAGE TESTS (~20 tests)
// ============================================================
describe('Flight Details Page - Flight Information', () => {
beforeEach(() => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getSchedule');
cy.getByTestId('schedule-search-result').first().click();
cy.wait('@getFlightDetails');
});
it('Should display flight number', () => {
cy.getByTestId('flight-details-number').should('contain', 'SU');
});
it('Should display departure information', () => {
cy.getByTestId('flight-departure-time').should('be.visible');
cy.getByTestId('flight-departure-city').should('contain', 'Москва');
});
it('Should display arrival information', () => {
cy.getByTestId('flight-arrival-time').should('be.visible');
cy.getByTestId('flight-arrival-city').should('contain', 'Санкт-Петербург');
});
it('Should display flight duration', () => {
cy.getByTestId('flight-duration').should('contain', 'h');
});
it('Should display aircraft type', () => {
cy.getByTestId('flight-aircraft').should('contain', 'A320');
});
it('Should display airline logo', () => {
cy.getByTestId('flight-company-logo').should('be.visible');
});
it('Should display price information', () => {
cy.getByTestId('flight-price').should('be.visible').should('contain', '3500');
});
it('Should display number of stops', () => {
cy.getByTestId('flight-stops').should('contain', '0');
});
});
describe('Flight Details Page - Timing Details', () => {
beforeEach(() => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getSchedule');
cy.getByTestId('schedule-search-result').first().click();
cy.wait('@getFlightDetails');
});
it('Should display departure gate', () => {
cy.getByTestId('flight-departure-gate').should('contain', '5');
});
it('Should display departure terminal', () => {
cy.getByTestId('flight-departure-terminal').should('contain', 'A');
});
it('Should display check-in time range', () => {
cy.getByTestId('flight-check-in-time').should('contain', '07:00');
});
it('Should display boarding time', () => {
cy.getByTestId('flight-boarding-time').should('contain', '08:30');
});
it('Should display arrival gate', () => {
cy.getByTestId('flight-arrival-gate').should('contain', '12');
});
it('Should display arrival terminal', () => {
cy.getByTestId('flight-arrival-terminal').should('contain', 'B');
});
});
describe('Flight Details Page - Navigation', () => {
beforeEach(() => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getSchedule');
cy.getByTestId('schedule-search-result').first().click();
cy.wait('@getFlightDetails');
});
it('Should navigate to next flight', () => {
cy.getByTestId('next-flight-button').click();
cy.wait('@getFlightDetails');
cy.getByTestId('flight-details-number').should('be.visible');
});
it('Should navigate to previous flight', () => {
cy.getByTestId('next-flight-button').click();
cy.wait('@getFlightDetails');
cy.getByTestId('prev-flight-button').click();
cy.wait('@getFlightDetails');
cy.getByTestId('flight-details-number').should('be.visible');
});
it('Should return to search results', () => {
cy.getByTestId('back-to-search-button').click();
cy.url().should('include', 'schedule');
cy.getByTestId('schedule-search-results').should('be.visible');
});
it('Should remember search filters when returning', () => {
cy.getByTestId('back-to-search-button').click();
cy.getByTestId('schedule-departure-city-input').getByTestId('city-code').should('contain', 'MOW');
cy.getByTestId('schedule-arrival-city-input').getByTestId('city-code').should('contain', 'LED');
});
it('Should disable previous button on first flight', () => {
cy.getByTestId('prev-flight-button').should('be.disabled');
});
});
// ============================================================
// FILTERS & SORTING TESTS (~15 tests)
// ============================================================
describe('Search Results - Filters', () => {
beforeEach(() => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getSchedule');
});
it('Should toggle time range filter', () => {
cy.getByTestId('time-filter-toggle').click();
cy.getByTestId('time-filter-panel').should('be.visible');
});
it('Should set minimum departure time', () => {
cy.getByTestId('time-filter-toggle').click();
cy.getByTestId('time-filter-min-slider').invoke('val', '09').trigger('input');
cy.getByTestId('schedule-search-result').each(($flight) => {
cy.wrap($flight).getByTestId('flight-departure-time').should('be.visible');
});
});
it('Should set maximum departure time', () => {
cy.getByTestId('time-filter-toggle').click();
cy.getByTestId('time-filter-max-slider').invoke('val', '18').trigger('input');
cy.getByTestId('schedule-search-result').each(($flight) => {
cy.wrap($flight).getByTestId('flight-departure-time').should('be.visible');
});
});
it('Should toggle airline filter', () => {
cy.getByTestId('airline-filter-toggle').click();
cy.getByTestId('airline-filter-panel').should('be.visible');
});
it('Should select single airline', () => {
cy.getByTestId('airline-filter-toggle').click();
cy.getByTestId('airline-filter-option').first().click();
cy.getByTestId('schedule-search-result').should('have.length.at.least', 1);
});
it('Should deselect airline', () => {
cy.getByTestId('airline-filter-toggle').click();
cy.getByTestId('airline-filter-option').first().click();
cy.getByTestId('airline-filter-option').first().click();
cy.getByTestId('schedule-search-result').should('have.length.at.least', 1);
});
it('Should toggle price range filter', () => {
cy.getByTestId('price-filter-toggle').click();
cy.getByTestId('price-filter-panel').should('be.visible');
});
it('Should set minimum price', () => {
cy.getByTestId('price-filter-toggle').click();
cy.getByTestId('price-filter-min-input').clear().type('3000');
cy.getByTestId('schedule-search-result').should('have.length.at.least', 1);
});
it('Should set maximum price', () => {
cy.getByTestId('price-filter-toggle').click();
cy.getByTestId('price-filter-max-input').clear().type('4000');
cy.getByTestId('schedule-search-result').should('have.length.at.least', 1);
});
it('Should clear all filters', () => {
cy.getByTestId('clear-filters-button').click();
cy.getByTestId('schedule-search-result').should('have.length.at.least', 1);
});
});
describe('Search Results - Sorting', () => {
beforeEach(() => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getSchedule');
});
it('Should sort by departure time ascending', () => {
cy.getByTestId('sort-dropdown').click();
cy.getByTestId('sort-option-departure-asc').click();
cy.getByTestId('schedule-search-result').first().getByTestId('flight-departure-time').should('contain', '09:00');
});
it('Should sort by departure time descending', () => {
cy.getByTestId('sort-dropdown').click();
cy.getByTestId('sort-option-departure-desc').click();
cy.getByTestId('schedule-search-result').first().getByTestId('flight-departure-time').should('contain', '21:00');
});
it('Should sort by flight duration', () => {
cy.getByTestId('sort-dropdown').click();
cy.getByTestId('sort-option-duration').click();
cy.getByTestId('schedule-search-result').should('have.length.at.least', 1);
});
it('Should sort by price ascending', () => {
cy.getByTestId('sort-dropdown').click();
cy.getByTestId('sort-option-price-asc').click();
cy.getByTestId('schedule-search-result').first().getByTestId('flight-price').should('contain', '2800');
});
it('Should sort by price descending', () => {
cy.getByTestId('sort-dropdown').click();
cy.getByTestId('sort-option-price-desc').click();
cy.getByTestId('schedule-search-result').first().getByTestId('flight-price').should('contain', '4200');
});
});
describe('Search Results - Result Display', () => {
beforeEach(() => {
cy.getByTestId('schedule-departure-city-input').type('Москва').type('{enter}');
cy.wait(200);
cy.getByTestId('schedule-arrival-city-input').type('Санкт-Петербург').type('{enter}');
cy.getByTestId('schedule-search-button').click();
cy.wait('@getSchedule');
});
it('Should display multiple flight results', () => {
cy.getByTestId('schedule-search-result').should('have.length', 5);
});
it('Should highlight flight on hover', () => {
cy.getByTestId('schedule-search-result').first().trigger('mouseover');
cy.getByTestId('schedule-search-result').first().should('have.class', 'highlighted');
});
it('Should show flight details on click', () => {
cy.getByTestId('schedule-search-result').first().click();
cy.wait('@getFlightDetails');
cy.getByTestId('flight-details-number').should('be.visible');
});
});
});