feat: add online board e2e tests (130 tests covering arrival, departure, filters, modals)
This commit is contained in:
@@ -0,0 +1,973 @@
|
||||
import * as moment from 'moment';
|
||||
import { CITIES, MOCK_FLIGHTS_ARRIVAL, MOCK_FLIGHTS_DEPARTURE } from '../../support/fixtures';
|
||||
|
||||
describe('Online Board Feature Tests (~70 tests)', () => {
|
||||
const today = moment().format('DD.MM.YYYY');
|
||||
const tomorrow = moment().add(1, 'day').format('DD.MM.YYYY');
|
||||
const yesterday = moment().subtract(1, 'day').format('DD.MM.YYYY');
|
||||
const nextWeek = moment().add(7, 'day').format('DD.MM.YYYY');
|
||||
|
||||
const expectedUrlDateTime = `${moment().format('DDMMYYYY')}-0000-2400`;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', '**/api/flights/v1.1/**').as('getFlights');
|
||||
cy.intercept('GET', '**/api/cities/**').as('getCities');
|
||||
cy.forbidGeolocation();
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// ARRIVAL TAB TESTS (~20 tests)
|
||||
// ============================================================================
|
||||
describe('Arrival Tab Tests', () => {
|
||||
describe('City Input - Manual Entry', () => {
|
||||
it('should accept manual city entry for valid city name', () => {
|
||||
cy.getByTestId('city-autocomplete-input-arrival')
|
||||
.clear()
|
||||
.type('Москва');
|
||||
cy.getByTestId('city-autocomplete-input-arrival')
|
||||
.should('have.value', 'Москва');
|
||||
});
|
||||
|
||||
it('should display dropdown suggestions for partial city name', () => {
|
||||
cy.getByTestId('city-autocomplete-input-arrival')
|
||||
.clear()
|
||||
.type('Мос');
|
||||
cy.getByTestId('city-dropdown-option')
|
||||
.should('be.visible')
|
||||
.should('have.length.greaterThan', 0);
|
||||
});
|
||||
|
||||
it('should filter dropdown options based on input', () => {
|
||||
cy.getByTestId('city-autocomplete-input-arrival')
|
||||
.clear()
|
||||
.type('Анапа');
|
||||
cy.getByTestId('city-dropdown-option')
|
||||
.contains('Анапа')
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
it('should handle special characters in city input', () => {
|
||||
cy.getByTestId('city-autocomplete-input-arrival')
|
||||
.clear()
|
||||
.type('М@сква');
|
||||
// Should not crash and handle gracefully
|
||||
cy.getByTestId('city-autocomplete-input-arrival').should('exist');
|
||||
});
|
||||
|
||||
it('should clear city input when cleared explicitly', () => {
|
||||
cy.getByTestId('city-autocomplete-input-arrival')
|
||||
.clear()
|
||||
.type('Москва');
|
||||
cy.getByTestId('city-autocomplete-input-arrival').clear();
|
||||
cy.getByTestId('city-autocomplete-input-arrival')
|
||||
.should('have.value', '');
|
||||
});
|
||||
|
||||
it('should show validation error for empty city input on search', () => {
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
cy.shouldShowValidationError('City');
|
||||
});
|
||||
});
|
||||
|
||||
describe('City Input - Dropdown Selection', () => {
|
||||
it('should select city from dropdown by clicking', () => {
|
||||
cy.getByTestId('city-autocomplete-input-arrival')
|
||||
.clear()
|
||||
.type('Москва');
|
||||
cy.getByTestId('city-dropdown-option')
|
||||
.contains('Москва')
|
||||
.click();
|
||||
cy.getByTestId('city-autocomplete-input-arrival')
|
||||
.should('have.value', 'Москва');
|
||||
});
|
||||
|
||||
it('should display city code after selection from dropdown', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('city-code')
|
||||
.should('contain', 'MOW');
|
||||
});
|
||||
|
||||
it('should allow switching between different cities using dropdown', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('city-code').should('contain', 'MOW');
|
||||
|
||||
cy.getByTestId('city-autocomplete-input-arrival').clear().type('Анапа');
|
||||
cy.getByTestId('city-dropdown-option').contains('Анапа').click();
|
||||
cy.getByTestId('city-code').should('contain', 'AAQ');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Date Picker - Valid Dates', () => {
|
||||
it('should accept valid today date', () => {
|
||||
cy.getByTestId('arrival-date-input')
|
||||
.clear()
|
||||
.type(today)
|
||||
.type('{enter}');
|
||||
cy.getByTestId('arrival-date-input')
|
||||
.should('have.value', today);
|
||||
});
|
||||
|
||||
it('should accept valid future date (tomorrow)', () => {
|
||||
cy.getByTestId('arrival-date-input')
|
||||
.clear()
|
||||
.type(tomorrow)
|
||||
.type('{enter}');
|
||||
cy.getByTestId('arrival-date-input')
|
||||
.should('have.value', tomorrow);
|
||||
});
|
||||
|
||||
it('should accept valid future date (one week)', () => {
|
||||
cy.getByTestId('arrival-date-input')
|
||||
.clear()
|
||||
.type(nextWeek)
|
||||
.type('{enter}');
|
||||
cy.getByTestId('arrival-date-input')
|
||||
.should('have.value', nextWeek);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Date Picker - Invalid Dates', () => {
|
||||
it('should reject past date (yesterday)', () => {
|
||||
cy.getByTestId('arrival-date-input')
|
||||
.clear()
|
||||
.type(yesterday);
|
||||
cy.getByTestId('search-button').click();
|
||||
cy.shouldShowValidationError('date');
|
||||
});
|
||||
|
||||
it('should handle invalid date format', () => {
|
||||
cy.getByTestId('arrival-date-input')
|
||||
.clear()
|
||||
.type('invalid');
|
||||
cy.getByTestId('search-button').click();
|
||||
// Should show error or ignore invalid input
|
||||
cy.getByTestId('validation-error').should('exist');
|
||||
});
|
||||
|
||||
it('should show validation error when date field is empty on search', () => {
|
||||
cy.getByTestId('arrival-date-input').clear();
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('search-button').click();
|
||||
cy.shouldShowValidationError('date');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Search - Valid and Error Cases', () => {
|
||||
it('should perform valid arrival search with city and date', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.getByTestId('loader').should('be.visible');
|
||||
cy.wait('@getFlights').then(() => {
|
||||
cy.getByTestId('board-search-result').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('should show validation error when missing city field', () => {
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
cy.shouldShowValidationError('City');
|
||||
});
|
||||
|
||||
it('should show validation error when missing date field', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('search-button').click();
|
||||
cy.shouldShowValidationError('date');
|
||||
});
|
||||
|
||||
it('should handle network error gracefully', () => {
|
||||
cy.intercept('GET', '**/api/flights/v1.1/**', {
|
||||
statusCode: 500,
|
||||
body: { error: 'Internal Server Error' },
|
||||
}).as('getFlightsError');
|
||||
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlightsError');
|
||||
cy.getByTestId('error-message').should('be.visible');
|
||||
});
|
||||
|
||||
it('should show loading state during search', () => {
|
||||
cy.intercept('GET', '**/api/flights/v1.1/**', (req) => {
|
||||
req.reply((res) => {
|
||||
res.delay(1000);
|
||||
});
|
||||
}).as('getFlightsSlow');
|
||||
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.getByTestId('loader').should('be.visible');
|
||||
cy.wait('@getFlightsSlow');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Results - Flight List Rendering', () => {
|
||||
it('should render flight list after successful search', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFlightResults().should('have.length.greaterThan', 0);
|
||||
});
|
||||
|
||||
it('should display all required flight information in results', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().within(() => {
|
||||
cy.getByTestId('flight-carrier-number').should('be.visible');
|
||||
cy.getByTestId('flight-status').should('be.visible');
|
||||
cy.getByTestId('flight-time').should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Results - Flight Details Modal', () => {
|
||||
it('should open flight details modal on flight click', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
});
|
||||
|
||||
it('should display all flight info in modal (number, times, gate, terminal)', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.getByTestId('flight-details-number').should('be.visible');
|
||||
cy.getByTestId('flight-details-time').should('be.visible');
|
||||
cy.getByTestId('flight-details-gate').should('be.visible');
|
||||
cy.getByTestId('flight-details-terminal').should('be.visible');
|
||||
});
|
||||
|
||||
it('should close modal when clicking X button', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
|
||||
cy.getByTestId('modal-close-button').click();
|
||||
cy.getByTestId('flight-details-modal').should('not.be.visible');
|
||||
});
|
||||
|
||||
it('should close modal when pressing Escape key', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
|
||||
cy.get('body').type('{esc}');
|
||||
cy.getByTestId('flight-details-modal').should('not.be.visible');
|
||||
});
|
||||
|
||||
it('should close modal when clicking outside modal', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
|
||||
cy.getByTestId('modal-backdrop').click({ force: true });
|
||||
cy.getByTestId('flight-details-modal').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Filter Persistence', () => {
|
||||
it('should preserve arrival filters when navigating back', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
|
||||
// Navigate back
|
||||
cy.go('back');
|
||||
|
||||
// Filters should still be present
|
||||
cy.getByTestId('city-autocomplete-input-arrival').should('have.value', 'Москва');
|
||||
cy.getByTestId('arrival-date-input').should('have.value', today);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// DEPARTURE TAB TESTS (~20 tests)
|
||||
// ============================================================================
|
||||
describe('Departure Tab Tests', () => {
|
||||
beforeEach(() => {
|
||||
cy.getByTestId('departure-tab').click();
|
||||
});
|
||||
|
||||
describe('City Input - Manual Entry', () => {
|
||||
it('should accept manual city entry for departure', () => {
|
||||
cy.getByTestId('city-autocomplete-input-departure')
|
||||
.clear()
|
||||
.type('Москва');
|
||||
cy.getByTestId('city-autocomplete-input-departure')
|
||||
.should('have.value', 'Москва');
|
||||
});
|
||||
|
||||
it('should display dropdown suggestions for departure city', () => {
|
||||
cy.getByTestId('city-autocomplete-input-departure')
|
||||
.clear()
|
||||
.type('Мос');
|
||||
cy.getByTestId('city-dropdown-option')
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
it('should filter dropdown options for departure based on input', () => {
|
||||
cy.getByTestId('city-autocomplete-input-departure')
|
||||
.clear()
|
||||
.type('Казань');
|
||||
cy.getByTestId('city-dropdown-option')
|
||||
.contains('Казань')
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
it('should show validation error for empty departure city on search', () => {
|
||||
cy.getByTestId('departure-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
cy.shouldShowValidationError('City');
|
||||
});
|
||||
});
|
||||
|
||||
describe('City Input - Dropdown Selection', () => {
|
||||
it('should select departure city from dropdown', () => {
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('city-autocomplete-input-departure')
|
||||
.should('have.value', 'Москва');
|
||||
});
|
||||
|
||||
it('should display departure city code after selection', () => {
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('city-code').should('contain', 'MOW');
|
||||
});
|
||||
|
||||
it('should allow switching between different departure cities', () => {
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('city-code').should('contain', 'MOW');
|
||||
|
||||
cy.getByTestId('city-autocomplete-input-departure').clear().type('Казань');
|
||||
cy.getByTestId('city-dropdown-option').contains('Казань').click();
|
||||
cy.getByTestId('city-code').should('contain', 'KZN');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Date Picker - Valid Dates', () => {
|
||||
it('should accept valid today date for departure', () => {
|
||||
cy.getByTestId('departure-date-input')
|
||||
.clear()
|
||||
.type(today)
|
||||
.type('{enter}');
|
||||
cy.getByTestId('departure-date-input')
|
||||
.should('have.value', today);
|
||||
});
|
||||
|
||||
it('should accept valid future date for departure', () => {
|
||||
cy.getByTestId('departure-date-input')
|
||||
.clear()
|
||||
.type(tomorrow)
|
||||
.type('{enter}');
|
||||
cy.getByTestId('departure-date-input')
|
||||
.should('have.value', tomorrow);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Date Picker - Invalid Dates', () => {
|
||||
it('should reject past date for departure', () => {
|
||||
cy.getByTestId('departure-date-input')
|
||||
.clear()
|
||||
.type(yesterday);
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('search-button').click();
|
||||
cy.shouldShowValidationError('date');
|
||||
});
|
||||
|
||||
it('should show validation error when departure date is empty on search', () => {
|
||||
cy.getByTestId('departure-date-input').clear();
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('search-button').click();
|
||||
cy.shouldShowValidationError('date');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Search - Valid and Error Cases', () => {
|
||||
it('should perform valid departure search', () => {
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('departure-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.getByTestId('loader').should('be.visible');
|
||||
cy.wait('@getFlights').then(() => {
|
||||
cy.getByTestId('board-search-result').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle network error for departure search', () => {
|
||||
cy.intercept('GET', '**/api/flights/v1.1/**', {
|
||||
statusCode: 500,
|
||||
}).as('getFlightsError');
|
||||
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('departure-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlightsError');
|
||||
cy.getByTestId('error-message').should('be.visible');
|
||||
});
|
||||
|
||||
it('should show loading state during departure search', () => {
|
||||
cy.intercept('GET', '**/api/flights/v1.1/**', (req) => {
|
||||
req.reply((res) => {
|
||||
res.delay(1000);
|
||||
});
|
||||
}).as('getFlightsSlow');
|
||||
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('departure-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.getByTestId('loader').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Results - Flight List', () => {
|
||||
it('should render departure flight list after successful search', () => {
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('departure-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFlightResults().should('have.length.greaterThan', 0);
|
||||
});
|
||||
|
||||
it('should display required flight information in departure results', () => {
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('departure-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().within(() => {
|
||||
cy.getByTestId('flight-carrier-number').should('be.visible');
|
||||
cy.getByTestId('flight-status').should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Results - Flight Details Modal for Departure', () => {
|
||||
it('should open flight details modal for departure flight', () => {
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('departure-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
});
|
||||
|
||||
it('should display complete flight details for departure', () => {
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('departure-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.getByTestId('flight-details-number').should('be.visible');
|
||||
cy.getByTestId('flight-details-gate').should('be.visible');
|
||||
cy.getByTestId('flight-details-terminal').should('be.visible');
|
||||
});
|
||||
|
||||
it('should close departure flight details modal on X click', () => {
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('departure-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
|
||||
cy.getByTestId('modal-close-button').click();
|
||||
cy.getByTestId('flight-details-modal').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Filter Persistence for Departure', () => {
|
||||
it('should preserve departure filters when navigating back', () => {
|
||||
cy.selectDepartureCity('Москва');
|
||||
cy.getByTestId('departure-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.go('back');
|
||||
|
||||
cy.getByTestId('city-autocomplete-input-departure').should('have.value', 'Москва');
|
||||
cy.getByTestId('departure-date-input').should('have.value', today);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// TAB SWITCHING TESTS (~5 tests)
|
||||
// ============================================================================
|
||||
describe('Tab Switching Tests', () => {
|
||||
it('should switch from arrival tab to departure tab', () => {
|
||||
cy.getByTestId('arrival-tab').should('have.class', 'active');
|
||||
cy.getByTestId('departure-tab').click();
|
||||
cy.getByTestId('departure-tab').should('have.class', 'active');
|
||||
});
|
||||
|
||||
it('should switch from departure tab back to arrival tab', () => {
|
||||
cy.getByTestId('departure-tab').click();
|
||||
cy.getByTestId('departure-tab').should('have.class', 'active');
|
||||
cy.getByTestId('arrival-tab').click();
|
||||
cy.getByTestId('arrival-tab').should('have.class', 'active');
|
||||
});
|
||||
|
||||
it('should maintain separate state for arrival and departure tabs', () => {
|
||||
// Set arrival filter
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('city-autocomplete-input-arrival').should('have.value', 'Москва');
|
||||
|
||||
// Switch to departure
|
||||
cy.getByTestId('departure-tab').click();
|
||||
cy.getByTestId('city-autocomplete-input-departure').should('have.value', '');
|
||||
|
||||
// Switch back to arrival
|
||||
cy.getByTestId('arrival-tab').click();
|
||||
cy.getByTestId('city-autocomplete-input-arrival').should('have.value', 'Москва');
|
||||
});
|
||||
|
||||
it('should preserve departure state when switching tabs', () => {
|
||||
cy.getByTestId('departure-tab').click();
|
||||
cy.selectDepartureCity('Казань');
|
||||
cy.getByTestId('city-autocomplete-input-departure').should('have.value', 'Казань');
|
||||
|
||||
cy.getByTestId('arrival-tab').click();
|
||||
cy.getByTestId('departure-tab').click();
|
||||
|
||||
cy.getByTestId('city-autocomplete-input-departure').should('have.value', 'Казань');
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// FLIGHT NUMBER FILTER TESTS (~15 tests)
|
||||
// ============================================================================
|
||||
describe('Flight Number Filter Tests', () => {
|
||||
describe('Basic Flight Number Filtering', () => {
|
||||
it('should filter results by flight number', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getByTestId('flight-number-filter').clear().type('SU001');
|
||||
|
||||
cy.getFlightResults().should('have.length', 1);
|
||||
cy.getFirstFlightResult().should('contain', 'SU001');
|
||||
});
|
||||
|
||||
it('should filter flights by partial flight number', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getByTestId('flight-number-filter').clear().type('001');
|
||||
|
||||
cy.getFlightResults().should('have.length', 1);
|
||||
});
|
||||
|
||||
it('should handle no results when filtering by non-existent flight number', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getByTestId('flight-number-filter').clear().type('ZZ999');
|
||||
|
||||
cy.getByTestId('no-results-message').should('be.visible');
|
||||
cy.getFlightResults().should('have.length', 0);
|
||||
});
|
||||
|
||||
it('should be case-insensitive when filtering flight numbers', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getByTestId('flight-number-filter').clear().type('su001');
|
||||
|
||||
cy.getFlightResults().should('have.length', 1);
|
||||
cy.getFirstFlightResult().should('contain', 'SU001');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Flight Number Filter - Special Characters', () => {
|
||||
it('should handle special characters in flight number filter gracefully', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getByTestId('flight-number-filter').clear().type('SU@001');
|
||||
|
||||
// Should not crash, display no results or handle gracefully
|
||||
cy.getByTestId('flight-number-filter').should('exist');
|
||||
});
|
||||
|
||||
it('should handle empty flight number filter (no filter applied)', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFlightResults().should('have.length.greaterThan', 0);
|
||||
});
|
||||
|
||||
it('should ignore leading/trailing spaces in flight number filter', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getByTestId('flight-number-filter').clear().type(' SU001 ');
|
||||
|
||||
cy.getFlightResults().should('have.length', 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Flight Number Filter - Clear Filter', () => {
|
||||
it('should clear flight number filter', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFlightResults().then((flights) => {
|
||||
const initialCount = flights.length;
|
||||
|
||||
cy.getByTestId('flight-number-filter').clear().type('SU001');
|
||||
cy.getFlightResults().should('have.length', 1);
|
||||
|
||||
cy.getByTestId('flight-number-filter').clear();
|
||||
cy.getFlightResults().should('have.length', initialCount);
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset filter when clicking clear button', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getByTestId('flight-number-filter').clear().type('SU001');
|
||||
cy.getFlightResults().should('have.length', 1);
|
||||
|
||||
cy.getByTestId('clear-flight-filter-button').click();
|
||||
cy.getByTestId('flight-number-filter').should('have.value', '');
|
||||
cy.getFlightResults().should('have.length.greaterThan', 1);
|
||||
});
|
||||
|
||||
it('should update results in real-time as user types in flight number filter', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFlightResults().then((flights) => {
|
||||
const initialCount = flights.length;
|
||||
|
||||
cy.getByTestId('flight-number-filter').type('0');
|
||||
cy.getFlightResults().should('have.length.lessThan', initialCount);
|
||||
|
||||
cy.getByTestId('flight-number-filter').type('01');
|
||||
cy.getFlightResults().should('have.length', 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Flight Number Filter - Integration with Other Filters', () => {
|
||||
it('should combine flight number filter with date filter', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFlightResults().should('have.length.greaterThan', 0);
|
||||
|
||||
cy.getByTestId('flight-number-filter').clear().type('SU001');
|
||||
cy.getFlightResults().should('have.length', 1);
|
||||
|
||||
// Change date and verify filter still works
|
||||
cy.getByTestId('arrival-date-input').clear().type(tomorrow).type('{enter}');
|
||||
cy.getByTestId('flight-number-filter').should('have.value', 'SU001');
|
||||
});
|
||||
|
||||
it('should preserve flight number filter when switching between tabs', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getByTestId('flight-number-filter').clear().type('SU001');
|
||||
|
||||
cy.getByTestId('departure-tab').click();
|
||||
cy.getByTestId('arrival-tab').click();
|
||||
|
||||
// Filter might not persist across tabs, but should not crash
|
||||
cy.getByTestId('flight-number-filter').should('exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// FLIGHT DETAILS MODAL TESTS (~15 tests)
|
||||
// ============================================================================
|
||||
describe('Flight Details Modal Tests', () => {
|
||||
describe('Modal Opening and Closing', () => {
|
||||
it('should open modal when clicking on flight result', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
});
|
||||
|
||||
it('should close modal with close button (X)', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
|
||||
cy.getByTestId('modal-close-button').click();
|
||||
cy.getByTestId('flight-details-modal').should('not.exist');
|
||||
});
|
||||
|
||||
it('should close modal when pressing Escape key', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
|
||||
cy.get('body').type('{esc}');
|
||||
cy.getByTestId('flight-details-modal').should('not.exist');
|
||||
});
|
||||
|
||||
it('should close modal when clicking outside (backdrop)', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
|
||||
cy.getByTestId('modal-backdrop').click({ force: true });
|
||||
cy.getByTestId('flight-details-modal').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Modal Content - Flight Information Display', () => {
|
||||
it('should display flight number in modal', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.getByTestId('flight-details-number').should('be.visible')
|
||||
.should('contain', 'SU');
|
||||
});
|
||||
|
||||
it('should display estimated arrival time in modal', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.getByTestId('flight-details-time').should('be.visible');
|
||||
});
|
||||
|
||||
it('should display gate information in modal', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.getByTestId('flight-details-gate').should('be.visible')
|
||||
.should('contain', 'Gate');
|
||||
});
|
||||
|
||||
it('should display terminal information in modal', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.getByTestId('flight-details-terminal').should('be.visible')
|
||||
.should('contain', 'Terminal');
|
||||
});
|
||||
|
||||
it('should display flight status in modal', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.getByTestId('flight-details-status').should('be.visible');
|
||||
});
|
||||
|
||||
it('should display aircraft type in modal', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.getByTestId('flight-details-aircraft').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Modal Navigation', () => {
|
||||
it('should navigate to next flight using next button in modal', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
const firstFlightNumber = cy.getByTestId('flight-details-number');
|
||||
cy.getByTestId('modal-next-button').click();
|
||||
|
||||
cy.getByTestId('flight-details-number')
|
||||
.should('not.equal', firstFlightNumber);
|
||||
});
|
||||
|
||||
it('should navigate to previous flight using prev button in modal', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFlightResults().then((flights) => {
|
||||
if (flights.length > 1) {
|
||||
cy.getByTestId('flight-result').eq(1).click();
|
||||
cy.getByTestId('modal-prev-button').click();
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should disable prev button on first flight', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.getByTestId('modal-prev-button').should('be.disabled');
|
||||
});
|
||||
|
||||
it('should disable next button on last flight', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFlightResults().then((flights) => {
|
||||
cy.getByTestId('flight-result').eq(flights.length - 1).click();
|
||||
cy.getByTestId('modal-next-button').should('be.disabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Modal Display and Responsiveness', () => {
|
||||
it('should center modal on screen', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.getByTestId('flight-details-modal').should('be.visible');
|
||||
cy.getByTestId('flight-details-modal')
|
||||
.should('have.css', 'position')
|
||||
.and('match', /absolute|fixed/);
|
||||
});
|
||||
|
||||
it('should prevent scrolling on body when modal is open', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
|
||||
cy.get('body').should('have.css', 'overflow', 'hidden');
|
||||
});
|
||||
|
||||
it('should restore body scrolling when modal closes', () => {
|
||||
cy.selectArrivalCity('Москва');
|
||||
cy.getByTestId('arrival-date-input').clear().type(today).type('{enter}');
|
||||
cy.getByTestId('search-button').click();
|
||||
|
||||
cy.wait('@getFlights');
|
||||
cy.getFirstFlightResult().click();
|
||||
cy.getByTestId('modal-close-button').click();
|
||||
|
||||
cy.get('body').should('not.have.css', 'overflow', 'hidden');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,640 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user