docs: add comprehensive e2e test suite design specification

This commit is contained in:
2026-04-04 12:02:18 +03:00
parent dfb9fed99a
commit 5ef60539ce
@@ -0,0 +1,781 @@
# E2E Test Suite Design: Aeroflot Flights Web (Angular → React)
**Date:** 2026-04-04
**Status:** Design Approved
**Scope:** 200-300 comprehensive e2e tests covering all UI elements and interactions
---
## 1. Overview
This document specifies a comprehensive end-to-end test suite for the Aeroflot Flights Web application, covering both the current Angular implementation (ClientApp/) and the new React implementation (react-app/). The tests verify feature parity between the two versions and ensure all UI elements, interactions, and edge cases function correctly.
**Approach:** Write all tests against Angular first, validate 100% pass rate, then adapt and run against React (with both mocked and real APIs).
**Total Test Count:** 200-300 tests
**Execution Time:** 8-15 minutes per suite (mocked API), 10-20 minutes (real API)
---
## 2. Test Architecture & File Organization
### 2.1 Directory Structure
```
ClientApp/cypress/
├── integration/
│ ├── features/
│ │ ├── online-board.cy.ts (~60-70 tests)
│ │ ├── schedule.cy.ts (~50-60 tests)
│ │ ├── flights-map.cy.ts (~30-40 tests)
│ │ ├── popular-requests.cy.ts (~20-30 tests)
│ │ ├── i18n.cy.ts (~15-20 tests)
│ │ └── error-states.cy.ts (~25-30 tests)
│ └── responsive.cy.ts (~30-40 tests)
├── support/
│ ├── commands.ts (custom Cypress commands)
│ ├── page-objects/
│ │ ├── online-board.po.ts
│ │ ├── schedule.po.ts
│ │ ├── flights-map.po.ts
│ │ ├── common.po.ts
│ │ └── index.ts
│ ├── fixtures.ts (mock data, test cities, flights)
│ └── index.ts
└── tsconfig.json
react-app/cypress/
├── integration/
│ ├── features/
│ │ ├── online-board.cy.ts (adapted from Angular)
│ │ ├── schedule.cy.ts
│ │ ├── flights-map.cy.ts
│ │ ├── popular-requests.cy.ts
│ │ ├── i18n.cy.ts
│ │ └── error-states.cy.ts
│ └── responsive.cy.ts
├── support/
│ ├── commands.ts (same as Angular)
│ ├── page-objects/ (may differ from Angular if DOM differs)
│ └── fixtures.ts (same as Angular)
└── tsconfig.json
```
### 2.2 Test Organization
Each spec file is organized as:
```typescript
// Example: online-board.cy.ts
describe('Online Board Feature', () => {
describe('Arrival Tab', () => {
describe('City Input', () => {
it('should accept manual city entry');
it('should show validation error for empty input');
it('should handle special characters gracefully');
// ... more tests
});
describe('Date Picker', () => {
it('should accept valid future dates');
it('should reject past dates');
// ... more tests
});
describe('Search & Results', () => {
it('should display flight results after successful search');
it('should show loading state during API call');
// ... more tests
});
});
describe('Departure Tab', () => {
// ... similar structure
});
// ... more features
});
```
---
## 3. Test Scope by Feature
### 3.1 Online Board (~60-70 tests)
**Arrival Tab (~20 tests):**
- City input: manual entry, dropdown selection, validation errors, empty input, special characters
- Date picker: valid dates, future dates, past dates, invalid formats, today, edge dates
- Search button: valid search, missing fields, network error, loading state, disabled state
- Results display: flight list rendering, correct count, flight details modal, pagination
**Departure Tab (~20 tests):**
- Same test coverage as Arrival (mirror feature)
**Flight Search/Filter (~15 tests):**
- Flight number input: valid format (e.g., "SU 123"), invalid format, special characters, clear button
- Autocomplete behavior: suggestions appear, filtering works, arrow key navigation, tab navigation
- Form submission with partial filters
**Flight Details Panel (~10 tests):**
- Modal opens on flight click, displays all correct flight info
- Modal closes on X button, escape key, outside click
- Links (airline, gate, terminal) navigate correctly
- Back button returns to results with filters preserved
### 3.2 Schedule (~50-60 tests)
**Search Page (~25 tests):**
- Origin autocomplete: manual entry, dropdown, validation
- Destination autocomplete: same as origin
- Date range picker: start date, end date, single day, full range, invalid ranges
- Passenger count: input, increment/decrement, max/min bounds
- Form submission: valid, incomplete, network error, empty results
**Flight Details Page (~20 tests):**
- Flight info displays: departure, arrival, duration, airline, flight number
- Timing details: gate, terminal, check-in time, boarding time
- Seat map: renders, interactive, can select seat
- Price info: base price, taxes, total, currency formatting
- Navigation: previous flight, next flight, back to search
**Filters & Sorting (~10-15 tests):**
- Time range filter: apply, clear, validate boundaries
- Airline filter: select multiple, deselect all, apply
- Price range filter: min/max sliders, apply
- Sorting: by departure time, duration, price (ascending/descending)
### 3.3 Flights Map (~30-40 tests)
**Map Rendering (~15 tests):**
- Map loads and is interactive
- Flight destination markers display
- Marker clustering at certain zoom levels
- Pan and zoom work correctly
- Geolocation button works (if enabled)
**Destination List (~15 tests):**
- List items render with destination name, flight count
- Click destination: highlights on map, center map on marker
- Search/filter in list: filters by city name
- Empty state when no destinations
**Map Interactions (~10 tests):**
- Click marker: shows popup with flight info
- Click destination in list: highlights on map, centers view
- Hover effects on markers and list items
- Popup contains: destination name, flight count, quick link to search
### 3.4 Popular Requests Widget (~20-30 tests)
- Widget renders on initial page load
- Displays all popular request items (mock data)
- Click item: navigates to search with correct parameters
- API fallback to mock data works
- Empty state handling (no popular requests)
- Widget positioning and styling correct
### 3.5 Internationalization (i18n) (~15-20 tests)
- Language switcher visible and functional (all 9 languages: ru, en, es, fr, it, ja, ko, zh, de)
- Switching language: page updates all text, no hard-coded strings visible
- Language persistence: localStorage remembers selection
- Date formats match locale (e.g., DD.MM.YYYY for ru, MM/DD/YYYY for en)
- Number formatting matches locale (decimal separator, thousands separator)
- Text truncation on narrow screens doesn't break layout
- All UI elements have translations (no missing keys)
### 3.6 Error States (~25-30 tests)
- Network errors: 404 (not found), 500 (server error), 503 (service unavailable)
- Timeout handling: API call exceeds timeout threshold
- Empty results: no flights found for search
- Validation errors: required fields missing, invalid input format
- Loading states: loader visible, correct messaging
- Recovery: retry button works, clears error after successful retry
- SignalR connection failures: connection lost message, auto-reconnect attempt, manual reconnect button
### 3.7 Responsive/Mobile (~30-40 tests)
**Mobile Viewport (375x667 - iPhone SE):**
- All text is readable (no overflow, proper line breaks)
- Touch targets are at least 44x44px
- Forms are usable (inputs accessible, not hidden behind keyboard)
- Navigation: hamburger menu or drawer opens/closes
- Accordion sections: collapse/expand works on tap
- Carousel/swipes: work with touch events
**Tablet Viewport (768x1024 - iPad):**
- Layout is optimized for tablet (not stretched, not too narrow)
- Multi-column layouts work correctly
- Touch interactions work
**Desktop Viewport (1920x1080 - Large screen):**
- Layout scales correctly
- No horizontal scrolling
- All content is accessible without zooming
---
## 4. Test Categories & Patterns
### 4.1 Happy Path Tests (~30-40% of total: 60-120 tests)
User performs the intended action and succeeds. Example:
```typescript
it('should search flights by arrival city and date', () => {
cy.selectArrivalCity('Москва');
cy.setArrivalDate('15.04.2026');
cy.clickSearchButton();
cy.getFlightResults().should('have.length.greaterThan', 0);
cy.getFirstFlightResult().should('be.visible');
});
```
### 4.2 Edge Case Tests (~20-25% of total: 40-75 tests)
Boundary conditions, extreme inputs, special characters. Examples:
```typescript
it('should handle special characters in flight number', () => {
cy.typeFlightNumber('SU-123@#$%');
cy.shouldShowValidationError('Invalid format');
});
it('should allow searching 1 year in the future', () => {
cy.setDepartureDate(moment().add(365, 'days').format('DD.MM.YYYY'));
cy.clickSearch();
cy.shouldLoadResults();
});
it('should handle empty autocomplete results', () => {
cy.typeArrivalCity('ZZZZZZ');
cy.shouldShowEmptyState('No cities found');
});
```
### 4.3 Error Handling Tests (~15-20% of total: 30-60 tests)
Network failures, invalid responses, timeouts, server errors. Examples:
```typescript
it('should show error message on 500 API failure', () => {
cy.intercept('GET', '**/api/flights/**', { statusCode: 500 });
cy.clickSearch();
cy.getErrorMessage().should('contain', 'Server error');
});
it('should handle network timeout gracefully', () => {
cy.intercept('GET', '**/api/flights/**', { delay: 10000 });
cy.clickSearch();
cy.getLoader().should('be.visible');
cy.getRetryButton().should('be.visible');
});
it('should recover from SignalR connection loss', () => {
cy.window().then(win => {
win.signalRConnection.stop();
});
cy.getConnectionStatusBanner().should('be.visible');
cy.getReconnectButton().click();
cy.getConnectionStatusBanner().should('not.exist');
});
```
### 4.4 State Management Tests (~10-15% of total: 20-45 tests)
Form state persistence, navigation state, localStorage/sessionStorage. Examples:
```typescript
it('should preserve search filters when navigating back', () => {
cy.selectArrivalCity('Москва');
cy.setDate('15.04.2026');
cy.clickFlightResult(0);
cy.goBack();
cy.getArrivalCityInput().should('have.value', 'Москва');
cy.getDateInput().should('have.value', '15.04.2026');
});
it('should remember language selection after page reload', () => {
cy.selectLanguage('en');
cy.reload();
cy.getCurrentLanguage().should('equal', 'en');
});
```
### 4.5 Accessibility & Interaction Tests (~10-15% of total: 20-45 tests)
Keyboard navigation, screen reader support, ARIA attributes, touch interactions. Examples:
```typescript
it('should navigate autocomplete with arrow keys', () => {
cy.typeInAutocomplete('Мос');
cy.get('body').type('{downarrow}');
cy.getFirstAutocompleteOption().should('have.focus');
cy.get('body').type('{enter}');
cy.shouldSelectOption('Москва');
});
it('should be keyboard navigable without mouse', () => {
cy.get('body').type('{tab}{tab}'); // Focus search button
cy.focused().should('have.attr', 'data-testid', 'search-button');
cy.get('body').type('{enter}');
cy.shouldLoadResults();
});
it('should be swipeable on mobile (right swipe)', () => {
cy.viewport('iphone-x');
cy.swipeRight();
cy.getDateInput().should('have.value', moment().subtract(1, 'day').format('DD.MM.YYYY'));
});
```
### 4.6 Localization Tests (~5-10% of total: 10-30 tests)
Language switching, date formats, number formatting. Examples:
```typescript
it('should display correct date format for Russian locale', () => {
cy.selectLanguage('ru');
cy.setDate('15.04.2026');
cy.getDateDisplay().should('contain', '15 апреля 2026');
});
it('should format currency for German locale', () => {
cy.selectLanguage('de');
cy.getFlightPrice().should('contain', '€');
});
it('should format numbers with correct decimal separator', () => {
cy.selectLanguage('de');
cy.getPrice().should('contain', ','); // German decimal separator
cy.selectLanguage('en');
cy.getPrice().should('contain', '.'); // English decimal separator
});
```
---
## 5. Data, Fixtures & Mocking Strategy
### 5.1 Fixture Architecture
```typescript
// cypress/support/fixtures.ts (shared between Angular & React)
export const CITIES = {
arrival: [
{ name: 'Москва', code: 'SVO', lat: 55.7558, lng: 37.62 },
{ name: 'Санкт-Петербург', code: 'LED', lat: 59.8011, lng: 30.2625 },
{ name: 'Анапа', code: 'AAQ', lat: 44.8972, lng: 37.3369 },
{ name: 'Екатеринбург', code: 'SVX', lat: 56.7431, lng: 60.8022 },
// ... 9+ cities total
],
departure: [
{ name: 'Москва', code: 'VKO', lat: 55.5917, lng: 37.2750 },
// ...
]
};
export const MOCK_FLIGHTS = {
arrival: [
{
id: 'SU123',
airline: 'Aeroflot',
number: 'SU 123',
departure: '10:15',
arrival: '11:45',
duration: '1h 30m',
status: 'landed',
gate: 'A5',
terminal: '1'
},
// ... 10+ flights for variety
],
departure: [ /* ... */ ]
};
export const TEST_USERS = {
default: { email: 'test@example.com' },
};
```
### 5.2 Intercept Strategy (Approach C: Real + Mocked)
**Test Suite 1: Mocked API** (deterministic, fast, runs in CI)
```typescript
beforeEach(() => {
cy.intercept('GET', '**/api/flights/v1.1/**', {
statusCode: 200,
body: MOCK_FLIGHTS.arrival
}).as('getFlights');
cy.intercept('GET', '**/api/cities/**', {
statusCode: 200,
body: CITIES.arrival
}).as('getCities');
cy.visit('http://localhost:4200'); // Angular
// OR cy.visit('http://localhost:3000'); // React
});
```
**Test Suite 2: Real API** (integration test, validates actual backend)
```typescript
beforeEach(() => {
// No intercepts — hits real backend
cy.visit('https://test.aeroflot.ru');
});
```
### 5.3 State Reset (Approach A: Full Reset per Test)
```typescript
beforeEach(() => {
// Clear all browser state
cy.clearAllLocalStorage();
cy.clearAllSessionStorage();
cy.clearCookies();
// Close any open WebSocket connections (SignalR)
cy.window().then(win => {
if (win.signalRConnection) {
win.signalRConnection.stop().catch(() => {});
}
});
// Reset to baseline URL
cy.visit('/');
cy.wait(500); // Allow page to fully load
});
afterEach(() => {
// Optional: take screenshot on failure
// (Cypress does this automatically by default)
});
```
### 5.4 Common Cypress Commands
```typescript
// cypress/support/commands.ts
Cypress.Commands.add('selectArrivalCity', (cityName: string) => {
cy.get('[data-testid="arrival-city-input"]').type(cityName);
cy.get(`[data-testid="city-option-${cityName}"]`).click();
});
Cypress.Commands.add('setArrivalDate', (date: string) => {
cy.get('[data-testid="arrival-date-input"]').clear().type(date);
});
Cypress.Commands.add('clickSearchButton', () => {
cy.get('[data-testid="search-button"]').click();
cy.get('[data-testid="loader"]').should('be.visible');
cy.get('[data-testid="loader"]').should('not.exist');
});
Cypress.Commands.add('getFlightResults', () => {
return cy.get('[data-testid="flight-result"]');
});
Cypress.Commands.add('shouldShowValidationError', (message: string) => {
cy.get('[data-testid="error-message"]').should('contain', message);
});
Cypress.Commands.add('swipeRight', () => {
cy.get('body').trigger('touchstart', { touches: [{ clientX: 0, clientY: 100 }] });
cy.get('body').trigger('touchmove', { touches: [{ clientX: 100, clientY: 100 }] });
cy.get('body').trigger('touchend');
});
```
---
## 6. Execution Strategy & Tooling
### 6.1 Cypress Configuration
```typescript
// cypress.config.ts (both Angular & React)
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: process.env.BASE_URL || 'http://localhost:4200',
viewportWidth: 1280,
viewportHeight: 720,
defaultCommandTimeout: 5000,
requestTimeout: 10000,
responseTimeout: 10000,
pageLoadTimeout: 30000,
chromeWebSecurity: false, // for Module Federation
video: true,
videoUploadOnPasses: false,
screenshotOnRunFailure: true,
specPattern: 'cypress/integration/**/*.cy.ts',
supportFile: 'cypress/support/index.ts',
setupNodeEvents(on, config) {
// Example: video recording configuration
return config;
},
},
});
```
### 6.2 NPM Scripts
Add to both `ClientApp/package.json` and `react-app/package.json`:
```json
{
"cypress:open": "cypress open",
"cypress:run": "cypress run",
"cypress:run:all": "cypress run --spec 'cypress/integration/**/*.cy.ts'",
"cypress:run:feature": "cypress run --spec 'cypress/integration/features/*.cy.ts'",
"cypress:run:responsive": "cypress run --spec 'cypress/integration/responsive.cy.ts'",
"cypress:report": "npm run cypress:run && npx mochawesome-report-generator",
"test:e2e": "npm run cypress:run -- --env API_MODE=mocked",
"test:e2e:real": "npm run cypress:run -- --env API_MODE=real BASE_URL=https://test.aeroflot.ru"
}
```
### 6.3 Execution Flow
**Phase 1: Write Angular Tests** (2-3 hours)
- Create all spec files with describe/it structure
- Implement helper functions in `cypress/support/commands.ts`
- Implement Page Object Models in `cypress/support/page-objects/`
- Run incrementally: `npm run cypress:open`
- Validate all tests pass: `npm run cypress:run:all`
**Phase 2: Validate Full Angular Suite** (30 mins)
- Full headless run with video/screenshots: `npm run cypress:run:all`
- Generate HTML report: `npm run cypress:report`
- Fix any flaky tests
**Phase 3: Adapt to React** (2-3 hours)
- Copy spec files to `react-app/cypress/integration/`
- Update selectors in page-objects if React DOM differs
- Update `cypress.config.ts` baseUrl to `:3000`
- Run against React with mocked API: `cd react-app && npm run test:e2e`
- Fix failures (likely selector or navigation changes)
**Phase 4: Run React Suite with Real API** (1-2 hours)
- Run full suite against staging: `npm run test:e2e:real`
- Validate all tests pass
- Fix any integration issues (timing, data, network)
### 6.4 CI/CD Integration
```yaml
# .github/workflows/e2e-tests.yml
name: E2E Tests
on: [push, pull_request]
jobs:
test-angular:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: cd ClientApp && npm ci
- run: npm start > /dev/null 2>&1 &
- run: npx wait-on http://localhost:4200 --timeout 30000
- run: npm run cypress:run:all
- uses: actions/upload-artifact@v3
if: always()
with:
name: cypress-videos-angular
path: ClientApp/cypress/videos
test-react:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: cd react-app && npm ci
- run: npm start > /dev/null 2>&1 &
- run: npx wait-on http://localhost:3000 --timeout 30000
- run: npm run test:e2e
- uses: actions/upload-artifact@v3
if: always()
with:
name: cypress-videos-react
path: react-app/cypress/videos
```
---
## 7. Success Criteria & Validation
### 7.1 Definition of Done
**Angular Tests:**
- ✅ All 200-300 tests written and organized by feature
- ✅ All tests pass against Angular app (100% pass rate)
- ✅ No test takes >10 seconds (performance gate)
- ✅ All test categories represented: happy path, edge case, error handling, state, accessibility, i18n, responsive
- ✅ Code coverage: 80%+ for tested components
**React Tests:**
- ✅ All Angular tests adapted to React (selector/navigation updates only)
- ✅ All tests pass against React app with mocked API (100% pass rate)
- ✅ All tests pass against React app with real API (staging backend)
- ✅ No test takes >10 seconds
- ✅ Feature parity verified: React UI behaves identically to Angular
### 7.2 Metrics to Track
| Metric | Target |
|--------|--------|
| Total Tests (Angular) | 200-300 |
| Total Tests (React) | 200-300 (same) |
| Pass Rate (Angular) | 100% |
| Pass Rate (React, mocked) | 100% |
| Pass Rate (React, real API) | 100% |
| Avg Test Duration | 2-3 seconds |
| Total Suite Time (mocked) | 8-15 minutes |
| Total Suite Time (real API) | 10-20 minutes |
| Code Coverage (tested components) | 80%+ |
| Flaky Test Count | 0 |
### 7.3 Stopping Condition
Work continues until:
- ✅ All 200-300 tests pass on Angular
- ✅ All 200-300 tests pass on React (mocked API)
- ✅ All 200-300 tests pass on React (real API)
- ✅ No test takes >10 seconds
- ✅ Re-running test suite 3x produces consistent results (no flaky tests)
- ✅ Angular and React behavior is identical for all covered features
---
## 8. Implementation Notes
### 8.1 DOM Structure Assumptions
Tests assume the following data-testid attributes exist on UI elements (both Angular and React must implement these):
```
Online Board:
- data-testid="arrival-city-input"
- data-testid="arrival-date-input"
- data-testid="search-button"
- data-testid="flight-result"
- data-testid="loader"
- data-testid="error-message"
- data-testid="flight-details-modal"
Schedule:
- data-testid="origin-input"
- data-testid="destination-input"
- data-testid="date-range-picker"
- data-testid="passenger-count"
- data-testid="search-button"
And so on...
```
If either implementation uses different selectors, Page Object Models will be updated to translate.
### 8.2 Test Data Lifecycle
- **Setup:** Full state reset before each test (localStorage, cookies, connections)
- **Execution:** Test runs against isolated mocked data
- **Teardown:** Browser state cleared automatically
No test data persists between tests.
### 8.3 Handling Flaky Tests
If a test is flaky:
1. Add explicit waits for async operations
2. Retry the specific assertion (Cypress built-in)
3. Check for race conditions in test logic
4. If unsolvable, mark as skipped with comment explaining issue
### 8.4 Performance Constraints
- Each test: <10 seconds (includes setup, execution, teardown)
- Full suite: <30 minutes (8-15 min for mocked, 10-20 min for real API)
- If a test exceeds 10 seconds, it's split or optimized
---
## 9. Risks & Mitigation
| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|-----------|
| React selectors differ from Angular | High | Medium | Page Object Model abstraction; update POM for React |
| Flaky network-dependent tests | Medium | High | Use mocked API for primary suite; real API as secondary |
| Test explosion (200-300 is large) | Medium | High | Phased execution; monitor suite time; parallelize if needed |
| Timing issues (async operations) | Medium | Medium | Explicit waits, retry logic, proper Cypress commands |
| Mobile tests on CI | Medium | Low | Use Cypress viewport, skip on CI if needed, test locally |
---
## 10. Timeline & Ownership
| Phase | Estimate | Owner |
|-------|----------|-------|
| 1. Write Angular Tests | 2-3 hours | Claude Code |
| 2. Validate Angular | 30 mins | Claude Code |
| 3. Adapt to React | 2-3 hours | Claude Code |
| 4. Validate React (mocked) | 1 hour | Claude Code |
| 5. Validate React (real API) | 1-2 hours | Claude Code |
| **Total** | **7-10 hours** | |
Work continues until **all tests pass** on both versions.
---
## Appendix: Reference
### A.1 Cypress Best Practices Used
- ✅ Page Object Model for selector abstraction
- ✅ Custom commands for common actions
- ✅ Explicit waits over implicit
- ✅ Data attributes (data-testid) for element selection
- ✅ Full state reset between tests
- ✅ Feature-based organization
- ✅ Mocking + real API testing
### A.2 Languages Supported (i18n)
1. Russian (ru)
2. English (en)
3. Spanish (es)
4. French (fr)
5. Italian (it)
6. Japanese (ja)
7. Korean (ko)
8. Chinese (zh)
9. German (de)
All 9 languages must have passing tests.
### A.3 Key Features Tested
1. Online Board (departure/arrival tabs, search, filters, flight details)
2. Schedule (search page, flight details, filters, sorting)
3. Flights Map (map rendering, markers, destination list, interactions)
4. Popular Requests (widget load, navigation, fallback)
5. Internationalization (language switching, formatting, persistence)
6. Error States (network failures, validation, loading states, recovery)
7. Responsive Design (mobile, tablet, desktop viewports)