# 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 ```json { "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: ```typescript // 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:** ```typescript ['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):** ```bash # Capture 1000+ screenshots from Angular version npm run backstop:reference ``` **Comparison (React):** ```bash # 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: ```typescript 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) ```bash # 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 ```bash # 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 ```bash 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 ```bash # 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: ```bash # 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.