Files
flights_web_raw/docs/superpowers/specs/2026-04-05-angular-react-migration-design.md
T
gnezim 0a5ab058a6 Initial commit: Aeroflot Flights Web - Angular 12 baseline
- Angular 12 application with PrimeNG components
- 5 existing Cypress e2e test suites
- SCSS styling with BEM naming convention
- i18n support (10 languages)
- Leaflet map integration
- Complete component hierarchy and routing structure

This baseline will be used for Angular → React migration.
2026-04-05 18:47:57 +03:00

19 KiB
Raw Blame History

Angular → React Migration Design

Date: 2026-04-05
Project: Aeroflot Flights Web Application
Goal: Create pixel-perfect React version of Angular app with 100% functional parity


Executive Summary

Migrate Aeroflot Flights Angular 12 application to React 18 with guaranteed functional and visual parity. Uses monorepo structure with shared e2e tests (1,225+ tests) and BackstopJS visual regression testing to ensure identical behavior and appearance on both versions.

Key Promise: If all Cypress tests pass + BackstopJS shows 0% diff, the React version is functionally and visually identical to Angular.


1. Project Structure (Monorepo)

aeroflot-flights/
├── apps/
│   ├── angular/                    # Existing Angular app (untouched)
│   │   ├── src/
│   │   ├── package.json
│   │   └── angular.json
│   │
│   └── react/                      # New React app (mirrors Angular)
│       ├── src/
│       │   ├── app/               # Mirror Angular component structure
│       │   ├── styles/            # Copy all SCSS from Angular
│       │   ├── assets/            # Shared assets (fonts, images)
│       │   ├── index.css
│       │   └── main.tsx
│       ├── vite.config.ts
│       ├── tsconfig.json
│       └── package.json
│
├── e2e/                            # Shared e2e tests (run against both)
│   ├── cypress/
│   │   ├── integration/            # 1,225+ test specs
│   │   │   ├── online-board/
│   │   │   ├── flight-details/
│   │   │   ├── schedule/
│   │   │   ├── flights-map/
│   │   │   ├── components/
│   │   │   ├── navigation/
│   │   │   ├── responsive/
│   │   │   ├── i18n/
│   │   │   ├── error-handling/
│   │   │   ├── search-history/
│   │   │   ├── integration/
│   │   │   ├── performance/
│   │   │   └── accessibility/
│   │   ├── support/
│   │   │   ├── commands.ts         # cy.getByTestId() 
│   │   │   ├── helpers/
│   │   │   │   ├── api.helpers.ts
│   │   │   │   ├── navigation.helpers.ts
│   │   │   │   ├── assertions.helpers.ts
│   │   │   │   └── data.helpers.ts
│   │   │   └── config.ts
│   │   └── cypress.config.ts
│   │
│   ├── backstop/                   # Visual regression configs
│   │   ├── backstop-angular.json   # Baseline capture config
│   │   ├── backstop-react.json     # Comparison test config
│   │   ├── engine_scripts/
│   │   │   ├── puppet/
│   │   │   │   ├── runBefore.js
│   │   │   │   └── runAfter.js
│   │   │   └── playwright/
│   │   ├── bitmaps_reference/      # Baseline images from Angular
│   │   │   └── [1000+ .png files across 3 viewports]
│   │   ├── bitmaps_test/           # React comparison screenshots
│   │   ├── html_report_react/      # Visual diff HTML reports
│   │   └── results/
│   │
│   ├── scripts/
│   │   ├── full-validation.sh      # Complete validation pipeline
│   │   ├── compare-versions.sh     # Side-by-side comparison
│   │   └── report-summary.sh       # Generate summary report
│   │
│   ├── package.json
│   ├── tsconfig.json
│   └── cypress.json
│
├── shared/                         # Optional: shared utilities
│   └── types/
│
├── scripts/
│   └── [shared scripts]
│
└── package.json                    # Root monorepo config

Why Monorepo:

  • Both versions run simultaneously (port 3000 & 3001)
  • Single test suite validates both
  • Easy to compare visually side-by-side
  • Safe: Angular untouched during migration

2. React Tech Stack

Dependencies

{
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.x",
    "primereact": "^10.x",
    "primeicons": "^6.x",
    "leaflet": "^1.7.1",
    "i18next": "^23.x",
    "i18next-http-backend": "^2.x",
    "react-i18next": "^13.x",
    "axios": "^1.6.x",
    "@tanstack/react-query": "^5.x",
    "zustand": "^4.x"
  },
  "devDependencies": {
    "vite": "^5.x",
    "typescript": "^5.x",
    "@vitejs/plugin-react": "^4.x",
    "sass": "^1.69.x",
    "cypress": "^13.x",
    "backstopjs": "^6.x"
  }
}

Architecture Mapping

Angular React Rationale
@angular/router react-router-dom Same routing patterns, simpler React API
@ngx-translate i18next Identical JSON i18n files, proven compatibility
HttpClient + RxJS axios + @tanstack/react-query Query caching, simpler data management
NgModule DI Context + Zustand Component composition, minimal boilerplate
PrimeNG PrimeReact Same CSS classes, zero visual changes

Build Tool

Primary: Vite (development speed)
Future: Rsbuild (Module Federation 2.0 support for production)

Migration path is zero-code change (build config only).


3. Styling Strategy

SCSS Copy + Maintain Global Scope

Research Finding: All Angular components use ViewEncapsulation.None, meaning styles are globally scoped. This is critical:

// Angular: encapsulation: ViewEncapsulation.None
// Styles are NOT scoped to components
// React: Don't use CSS Modules, keep styles global

File Structure

apps/react/src/styles/
├── index.scss                      # Main entry point
├── framework.scss                  # Re-exports variables/mixins
├── _reset.scss                     # ← Copy from Angular
├── _colors.scss                    # ← Copy from Angular
├── _fonts.scss                     # ← Copy from Angular
├── _fonts.classes.scss             # ← Copy from Angular
├── _shadows.scss                   # ← Copy from Angular
├── _variables.scss                 # ← Copy from Angular
├── _prime-styles.scss              # ← Copy + update PrimeNG → PrimeReact
├── _prime-calendar.scss            # ← Copy from Angular
├── _layout.scss                    # ← Copy from Angular
├── _icons.scss                     # ← Copy from Angular
├── _buttons.scss                   # ← Copy from Angular
├── _tooltips.scss                  # ← Copy from Angular
├── _overrides.scss                 # ← Copy from Angular
├── _banners.scss                   # ← Copy from Angular
├── _logos.scss                     # ← Copy from Angular
├── _common.scss                    # ← Copy from Angular
├── scrollbar.scss                  # ← Copy from Angular
├── _grid-sizes.scss                # ← Copy from Angular
├── _leaflet-popup.scss             # ← Copy from Angular
├── pages/
│   ├── board/index.scss            # ← Copy from Angular
│   ├── board/board-flight-*.scss   # ← Copy from Angular
│   └── schedule/                   # ← Copy from Angular
└── adaptive/                       # Responsive styles
    └── [all breakpoint files]

Component SCSS (75+ files)

apps/react/src/app/
├── components/
│   ├── city-autocomplete/
│   │   ├── city-autocomplete.tsx
│   │   └── city-autocomplete.scss  # ← Copy directly
│   └── [75+ other components]
│
└── features/
    ├── online-board/
    │   ├── online-board.tsx
    │   ├── online-board.scss
    │   └── pages/
    │       └── [all component files]
    └── [schedule, flights-map, etc.]

Key Rules

Copy all SCSS files exactly (only update import paths)
Keep BEM naming (.city-autocomplete__item--airport)
Global styles, no CSS Modules (matches Angular's ViewEncapsulation.None)
PrimeReact CSS classes identical to PrimeNG (.p-button, .p-dropdown, etc.)
Vite handles SCSS (same as Angular)

Why this works: PrimeReact exposes identical CSS class names to PrimeNG. All 131KB of PrimeNG overrides work without modification.


4. E2E Test Suite (1,225+ Tests)

Test Organization

e2e/cypress/integration/
├── online-board/
│   ├── 01-arrival-search.cy.ts              (80 tests)
│   ├── 02-departure-search.cy.ts            (80 tests)
│   ├── 03-route-search.cy.ts                (60 tests)
│   ├── 04-flight-number-search.cy.ts        (50 tests)
│   └── 05-online-board-filters.cy.ts        (40 tests)
├── flight-details/
│   ├── 06-flight-details-modal.cy.ts        (50 tests)
│   ├── 07-flight-timing.cy.ts               (30 tests)
│   ├── 08-transfers.cy.ts                   (25 tests)
│   └── 09-equipment-info.cy.ts              (20 tests)
├── schedule/
│   ├── 10-schedule-search.cy.ts             (60 tests)
│   ├── 11-schedule-filters.cy.ts            (30 tests)
│   └── 12-schedule-details.cy.ts            (25 tests)
├── flights-map/
│   ├── 13-map-interaction.cy.ts             (30 tests)
│   └── 14-map-filters.cy.ts                 (20 tests)
├── components/
│   ├── 15-city-autocomplete.cy.ts           (50 tests)
│   ├── 16-date-picker.cy.ts                 (40 tests)
│   ├── 17-tabs-navigation.cy.ts             (30 tests)
│   ├── 18-buttons-actions.cy.ts             (25 tests)
│   └── 19-modals-dialogs.cy.ts              (20 tests)
├── navigation/
│   ├── 20-routing-guards.cy.ts              (30 tests)
│   ├── 21-url-parameters.cy.ts              (25 tests)
│   └── 22-history-navigation.cy.ts          (20 tests)
├── responsive/
│   ├── 23-mobile-layouts.cy.ts              (50 tests)
│   ├── 24-tablet-layouts.cy.ts              (40 tests)
│   └── 25-desktop-layouts.cy.ts             (40 tests)
├── i18n/
│   ├── 26-language-switching.cy.ts          (30 tests)
│   ├── 27-text-rendering.cy.ts              (25 tests)
│   └── 28-locale-formatting.cy.ts           (20 tests)
├── error-handling/
│   ├── 29-api-error-scenarios.cy.ts         (35 tests)
│   ├── 30-validation-errors.cy.ts           (30 tests)
│   ├── 31-network-failures.cy.ts            (25 tests)
│   └── 32-edge-cases.cy.ts                  (30 tests)
├── search-history/
│   ├── 33-search-history-persistence.cy.ts  (25 tests)
│   ├── 34-popular-requests.cy.ts            (20 tests)
│   └── 35-quick-access.cy.ts                (15 tests)
├── integration/
│   ├── 36-end-to-end-flows.cy.ts            (40 tests)
│   ├── 37-concurrent-operations.cy.ts       (30 tests)
│   └── 38-cross-feature-scenarios.cy.ts     (25 tests)
└── performance/
    ├── 39-load-times.cy.ts                  (20 tests)
    └── 40-interaction-performance.cy.ts     (15 tests)

Test Coverage

Category Count Coverage
Online Board Searches 310 All search types, filters, pagination
Flight Details & Info 125 Modal, timing, transfers, equipment
Schedule 115 Search, filters, details
Maps & Spatial 50 Map interaction, zooming
Core Components 165 Inputs, dates, tabs, buttons, modals
Navigation & Routing 75 Guards, params, history
Responsive Design 130 Mobile/Tablet/Desktop
Internationalization 75 Language switching, formatting
Error Handling 120 API errors, validation, network
Data Persistence 60 Search history, preferences
Integration Flows 95 Multi-step user journeys
Performance 35 Load times, rendering
Accessibility 40 Keyboard, focus, ARIA
Browser/Device 30 Multiple browsers, devices
Total 1,225 Complete coverage

Key Features

Single test suite runs on both versions:

['angular', 'react'].forEach((version) => {
  describe(`${version.toUpperCase()} version`, () => {
    // All 1,225 tests run here
    // Both versions must pass
  });
});

If both versions pass all tests → Functionally identical

Test Assertions Include

  • Functional behavior (clicks, inputs, navigation)
  • Visual styles (CSS properties, computed values)
  • DOM structure (selectors, attributes)
  • Responsive layouts (all breakpoints)
  • Accessibility (keyboard, focus)
  • Error scenarios (API failures, validation)

5. Visual Regression Testing (BackstopJS)

Purpose

Guarantee pixel-perfect visual parity via automated screenshot comparison.

Setup

Baseline Creation (Angular):

# Capture 1000+ screenshots from Angular version
npm run backstop:reference

Comparison (React):

# Capture React screenshots
# Compare pixel-by-pixel to baseline
npm run backstop:test

Output: HTML report showing:

  • Side-by-side Angular/React screenshots
  • Red overlay highlighting any differences
  • % mismatch per scenario per viewport

Viewports Tested

  • Phone: 375×667px
  • Tablet: 768×1024px
  • Desktop: 1440×900px

Scenarios (100+)

Each major user flow captured:

  • Home page (empty state)
  • Online Board searches (arrival, departure, route, flight number)
  • Flight Details modal
  • Search results (with flights visible)
  • Responsive layouts (each breakpoint)
  • Error states
  • Loading states
  • [More scenarios per feature]

Total Screenshots: 100 scenarios × 3 viewports = 300+ baseline images

Tolerance

  • Default: 0% diff (zero pixels can differ)
  • Optional: 0.1% for minor anti-aliasing differences

If 0% diff achieved → Pixel-perfect match guaranteed


6. Combined Visual + Functional Validation

Cypress Visual Assertions (200+ tests)

Each e2e test includes style validation:

it('should render button with correct styles', () => {
  cy.getByTestId('search-button')
    .should('have.css', 'background-color', 'rgb(0, 122, 217)')
    .should('have.css', 'height', '48px')
    .should('have.css', 'padding', '12px 24px')
    .should('have.css', 'border-radius', '3px');
});

it('should match computed styles across versions', () => {
  cy.getByTestId('city-input')
    .should('have.css', 'font-size', '16px')
    .should('have.css', 'font-weight', '400')
    .should('have.css', 'color', 'rgb(51, 51, 51)');
});

Validation Layers

  1. Cypress (1,225 tests)

    • Functional behavior
    • Computed CSS styles
    • DOM structure
  2. BackstopJS (300+ screenshots)

    • Pixel-level visual comparison
    • Layout accuracy
    • Responsive breakpoints
  3. Manual Review

    • Open both versions side-by-side
    • Visual sanity check
    • Edge case validation

Combined Result: Functional + visual parity guaranteed


7. Development Workflow

Local Setup (3 terminals)

# Terminal 1: Angular (baseline)
cd apps/angular
npm start
# → http://localhost:3000

# Terminal 2: React (in development)
cd apps/react
npm run dev
# → http://localhost:3001

# Terminal 3: Tests (watch mode)
cd e2e
npm run cypress:open
# → Runs tests against both versions

Component Migration Checklist

For each React component:

☐ TypeScript code written (based on Angular)
☐ SCSS copied (no CSS Modules)
☐ data-testid attributes added (match Angular)
☐ Component integrated in parent
☐ Cypress tests run
  ☐ Angular version: ✅ Pass
  ☐ React version: ✅ Pass
☐ BackstopJS comparison
  ☐ 0% visual diff
  ☐ All viewports pass
☐ Manual side-by-side review
☐ Commit with message: "feat: React [ComponentName] - 1,225 tests pass, 0% visual diff"

Comparison Workflow

# Terminal 4: Visual comparison (optional, on-demand)
cd e2e
npm run compare

# Output shows:
# ✅ 1,225 Cypress tests passed
# ✅ 0% visual diff (pixel-perfect)
# 🎉 Versions are identical

8. Test Execution Pipeline

Full Validation Command

npm run validate

# Executes:
# 1. Start Angular (port 3000)
# 2. Start React (port 3001)
# 3. Run 1,225 Cypress tests on Angular
# 4. Run 1,225 Cypress tests on React
# 5. Create baseline (if needed)
# 6. Run BackstopJS visual tests
# 7. Generate summary report
# 8. Open HTML reports

Output Example

✅ Angular E2E Tests:      PASSED (1225 tests)
✅ React E2E Tests:        PASSED (1225 tests)
✅ Visual Regression:      PASSED (0% diff)

🎉 ALL VALIDATIONS PASSED - React version is ready

CI/CD Integration

# Automated validation in CI
npm run validate:ci

# Returns:
# Exit 0 → All tests passed, ready to merge
# Exit 1 → Tests failed, review needed

9. Migration Guarantee

If all conditions met:

1,225 Cypress tests pass on both Angular & React
200+ visual assertions pass on React
BackstopJS: 0% pixel diff on 300+ screenshots
All responsive viewports validated

Then:

React version is guaranteed to be:

  • Functionally identical to Angular
  • Visually identical (pixel-perfect)
  • Same routing behavior
  • Same API interactions
  • Same error handling
  • Same responsive design
  • Same accessibility

10. Future: Rsbuild Migration

If production requires Module Federation 2.0:

# Zero code changes required
# Only build config changes:
# vite.config.ts → rsbuild.config.ts
# All React code stays identical

npm run dev:rsbuild  # Uses Rsbuild instead

This is non-breaking and can be done anytime post-MVP.


Success Criteria

Project structure set up
React app scaffolded with Vite
All SCSS copied and adjusted
1,225 e2e tests written and passing
BackstopJS baseline created
React passes 100% of tests
0% visual diff in BackstopJS
Both versions run simultaneously
npm run validate completes successfully


Timeline Estimate

(Provided by user understanding these are approximate)

  • Setup & scaffolding: 1-2 days
  • Core components (50 components): 3-5 days
  • Features (online-board, schedule, map): 3-5 days
  • Testing & validation: 2-3 days
  • Bug fixes & refinement: 2-3 days
  • Total MVP: ~2-3 weeks

Actual timeline depends on team size and code familiarity.


Assumptions & Constraints

Assumptions

  • Angular app remains untouched during migration
  • PrimeReact API is compatible with PrimeNG styling
  • i18next JSON format works with existing translation files
  • Vite SCSS handling matches Angular's
  • All components use ViewEncapsulation.None (verified )

Constraints

  • No SSR in MVP (future feature)
  • No PWA in MVP (future feature)
  • No CI/CD in MVP (manual validation)
  • No SEO optimizations in MVP (future feature)
  • Module Federation only in production (Rsbuild phase)

Design Sign-Off

This design provides a comprehensive, step-by-step migration strategy with multiple validation layers to guarantee 100% parity between Angular and React versions.

Key Differentiator: Single test suite running on both versions eliminates the risk of testing divergence.