diff --git a/docs/superpowers/specs/2026-04-04-e2e-tests-design.md b/docs/superpowers/specs/2026-04-04-e2e-tests-design.md new file mode 100644 index 00000000..6c665e55 --- /dev/null +++ b/docs/superpowers/specs/2026-04-04-e2e-tests-design.md @@ -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) +