From a30bf6be4c4fab9b6dd3182e6f1f7eedc7eb1d72 Mon Sep 17 00:00:00 2001 From: adilallo <39313955+adilallo@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:22:59 -0700 Subject: [PATCH] Update E2E tests and simplify performance tests --- .gitea/workflows/ci.yaml | 8 +- README.md | 29 +- docs/TESTING_GUIDE.md | 72 ++- package.json | 8 +- playwright.config.ts | 7 +- scripts/performance-monitor.js | 297 ---------- scripts/test-lhci.js | 66 --- scripts/test-performance.js | 349 ----------- scripts/web-vitals-tracker.js | 335 ----------- tests/e2e/BlogNavigation.e2e.test.jsx | 201 ------- tests/e2e/ContentPageRendering.e2e.test.jsx | 173 ------ tests/e2e/LogoNavigation.e2e.test.jsx | 57 -- tests/e2e/critical-journeys.spec.ts | 217 +++++++ tests/e2e/edge-cases.spec.ts | 557 +----------------- tests/e2e/homepage.spec.ts | 485 --------------- tests/e2e/performance.spec.ts | 401 ++----------- tests/e2e/user-journeys.spec.ts | 396 ------------- tests/e2e/visual-regression.spec.ts | 464 +++------------ .../ask-organizer-chromium.png | Bin 21917 -> 0 bytes .../ask-organizer-firefox.png | Bin 30571 -> 0 bytes .../ask-organizer-mobile.png | Bin 20385 -> 0 bytes .../ask-organizer-webkit.png | Bin 30007 -> 0 bytes .../feature-card-hover-chromium.png | Bin 8060 -> 0 bytes .../feature-card-hover-firefox.png | Bin 10348 -> 0 bytes .../feature-card-hover-mobile.png | Bin 10676 -> 0 bytes .../feature-card-hover-webkit.png | Bin 10268 -> 0 bytes .../feature-card-normal-chromium.png | Bin 7316 -> 0 bytes .../feature-card-normal-firefox.png | Bin 9774 -> 0 bytes .../feature-card-normal-mobile.png | Bin 10931 -> 0 bytes .../feature-card-normal-webkit.png | Bin 9193 -> 0 bytes .../feature-grid-chromium.png | Bin 52973 -> 0 bytes .../feature-grid-firefox.png | Bin 76126 -> 0 bytes .../feature-grid-mobile.png | Bin 63994 -> 0 bytes .../feature-grid-webkit.png | Bin 67682 -> 0 bytes .../footer-chromium.png | Bin 42651 -> 0 bytes .../footer-firefox.png | Bin 62648 -> 0 bytes .../footer-mobile.png | Bin 36427 -> 0 bytes .../footer-webkit.png | Bin 60880 -> 0 bytes .../header-chromium.png | Bin 12913 -> 0 bytes .../header-firefox.png | Bin 15978 -> 0 bytes .../header-mobile.png | Bin 6241 -> 0 bytes .../header-webkit.png | Bin 16100 -> 0 bytes .../hero-banner-chromium.png | Bin 438651 -> 0 bytes .../hero-banner-desktop-chromium.png | Bin 481170 -> 0 bytes .../hero-banner-desktop-firefox.png | Bin 566065 -> 0 bytes .../hero-banner-desktop-mobile.png | Bin 589408 -> 0 bytes .../hero-banner-desktop-webkit.png | Bin 591084 -> 0 bytes .../hero-banner-firefox.png | Bin 511666 -> 0 bytes .../hero-banner-mobile-chromium.png | Bin 100170 -> 0 bytes .../hero-banner-mobile-firefox.png | Bin 120971 -> 0 bytes .../hero-banner-mobile-mobile.png | Bin 121473 -> 0 bytes .../hero-banner-mobile-webkit.png | Bin 121828 -> 0 bytes .../hero-banner-mobile.png | Bin 126746 -> 0 bytes .../hero-banner-tablet-chromium.png | Bin 222382 -> 0 bytes .../hero-banner-tablet-firefox.png | Bin 259260 -> 0 bytes .../hero-banner-tablet-mobile.png | Bin 274048 -> 0 bytes .../hero-banner-tablet-webkit.png | Bin 274627 -> 0 bytes .../hero-banner-webkit.png | Bin 546111 -> 0 bytes .../homepage-dark-mode-chromium.png | Bin 475913 -> 0 bytes .../homepage-dark-mode-firefox.png | Bin 553967 -> 0 bytes .../homepage-dark-mode-mobile.png | Bin 146931 -> 0 bytes .../homepage-dark-mode-webkit.png | Bin 588027 -> 0 bytes .../homepage-desktop-chromium.png | Bin 537375 -> 0 bytes .../homepage-desktop-firefox.png | Bin 627274 -> 0 bytes .../homepage-desktop-mobile.png | Bin 621294 -> 0 bytes .../homepage-desktop-webkit.png | Bin 622630 -> 0 bytes .../homepage-high-contrast-chromium.png | Bin 225698 -> 0 bytes .../homepage-high-contrast-firefox.png | Bin 272188 -> 0 bytes .../homepage-high-contrast-mobile.png | Bin 74148 -> 0 bytes .../homepage-high-contrast-webkit.png | Bin 348791 -> 0 bytes .../homepage-large-desktop-chromium.png | Bin 648426 -> 0 bytes .../homepage-large-desktop-firefox.png | Bin 769373 -> 0 bytes .../homepage-large-desktop-mobile.png | Bin 797719 -> 0 bytes .../homepage-large-desktop-webkit.png | Bin 799698 -> 0 bytes .../homepage-loading-chromium.png | Bin 475588 -> 0 bytes .../homepage-loading-firefox.png | Bin 553645 -> 0 bytes .../homepage-loading-mobile.png | Bin 147138 -> 0 bytes .../homepage-loading-webkit.png | Bin 586694 -> 0 bytes .../homepage-mobile-chromium.png | Bin 117680 -> 0 bytes .../homepage-mobile-firefox.png | Bin 142955 -> 0 bytes .../homepage-mobile-mobile.png | Bin 142934 -> 0 bytes .../homepage-mobile-webkit.png | Bin 143020 -> 0 bytes .../homepage-reduced-motion-chromium.png | Bin 475588 -> 0 bytes .../homepage-reduced-motion-firefox.png | Bin 553645 -> 0 bytes .../homepage-reduced-motion-mobile.png | Bin 146755 -> 0 bytes .../homepage-reduced-motion-webkit.png | Bin 586340 -> 0 bytes .../homepage-tablet-chromium.png | Bin 286784 -> 0 bytes .../homepage-tablet-firefox.png | Bin 338505 -> 0 bytes .../homepage-tablet-mobile.png | Bin 338131 -> 0 bytes .../homepage-tablet-webkit.png | Bin 353460 -> 0 bytes .../logo-hover-chromium.png | Bin 3427 -> 0 bytes .../logo-hover-firefox.png | Bin 4018 -> 0 bytes .../logo-hover-mobile.png | Bin 2489 -> 0 bytes .../logo-hover-webkit.png | Bin 3818 -> 0 bytes .../logo-normal-chromium.png | Bin 3222 -> 0 bytes .../logo-normal-firefox.png | Bin 3827 -> 0 bytes .../logo-normal-mobile.png | Bin 2489 -> 0 bytes .../logo-normal-webkit.png | Bin 3557 -> 0 bytes .../logo-wall-chromium.png | Bin 22837 -> 0 bytes .../logo-wall-firefox.png | Bin 28152 -> 0 bytes .../logo-wall-mobile.png | Bin 21109 -> 0 bytes .../logo-wall-webkit.png | Bin 28202 -> 0 bytes .../numbered-cards-chromium.png | Bin 54447 -> 0 bytes .../numbered-cards-firefox.png | Bin 77760 -> 0 bytes .../numbered-cards-mobile.png | Bin 67340 -> 0 bytes .../numbered-cards-webkit.png | Bin 74537 -> 0 bytes .../quote-block-chromium.png | Bin 206438 -> 0 bytes .../quote-block-firefox.png | Bin 266118 -> 0 bytes .../quote-block-mobile.png | Bin 53334 -> 0 bytes .../quote-block-webkit.png | Bin 266626 -> 0 bytes .../rule-card-hover-chromium.png | Bin 25968 -> 0 bytes .../rule-card-hover-firefox.png | Bin 31904 -> 0 bytes .../rule-card-hover-mobile.png | Bin 14689 -> 0 bytes .../rule-card-hover-webkit.png | Bin 28742 -> 0 bytes .../rule-card-normal-chromium.png | Bin 24498 -> 0 bytes .../rule-card-normal-firefox.png | Bin 30456 -> 0 bytes .../rule-card-normal-mobile.png | Bin 14689 -> 0 bytes .../rule-card-normal-webkit.png | Bin 27435 -> 0 bytes .../rule-stack-chromium.png | Bin 81910 -> 0 bytes .../rule-stack-firefox.png | Bin 113202 -> 0 bytes .../rule-stack-mobile.png | Bin 57116 -> 0 bytes .../rule-stack-webkit.png | Bin 102243 -> 0 bytes tests/performance/performance-monitor.js | 516 ---------------- vitest.config.mjs | 4 +- 124 files changed, 452 insertions(+), 4190 deletions(-) delete mode 100644 scripts/performance-monitor.js delete mode 100644 scripts/test-lhci.js delete mode 100644 scripts/test-performance.js delete mode 100644 scripts/web-vitals-tracker.js delete mode 100644 tests/e2e/BlogNavigation.e2e.test.jsx delete mode 100644 tests/e2e/ContentPageRendering.e2e.test.jsx delete mode 100644 tests/e2e/LogoNavigation.e2e.test.jsx create mode 100644 tests/e2e/critical-journeys.spec.ts delete mode 100644 tests/e2e/homepage.spec.ts delete mode 100644 tests/e2e/user-journeys.spec.ts delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/ask-organizer-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/ask-organizer-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/ask-organizer-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/ask-organizer-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-card-hover-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-card-hover-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-card-hover-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-card-hover-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-card-normal-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-card-normal-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-card-normal-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-card-normal-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-grid-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-grid-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-grid-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/feature-grid-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/footer-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/footer-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/footer-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/footer-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/header-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/header-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/header-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/header-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-desktop-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-desktop-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-desktop-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-desktop-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-mobile-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-mobile-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-mobile-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-mobile-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-tablet-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-tablet-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-tablet-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-tablet-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/hero-banner-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-dark-mode-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-dark-mode-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-dark-mode-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-dark-mode-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-desktop-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-desktop-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-desktop-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-desktop-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-high-contrast-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-high-contrast-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-high-contrast-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-high-contrast-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-large-desktop-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-large-desktop-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-large-desktop-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-large-desktop-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-loading-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-loading-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-loading-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-loading-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-mobile-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-mobile-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-mobile-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-mobile-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-reduced-motion-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-reduced-motion-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-reduced-motion-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-reduced-motion-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-tablet-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-tablet-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-tablet-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/homepage-tablet-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-hover-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-hover-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-hover-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-hover-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-normal-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-normal-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-normal-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-normal-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-wall-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-wall-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-wall-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/logo-wall-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/numbered-cards-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/numbered-cards-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/numbered-cards-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/numbered-cards-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/quote-block-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/quote-block-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/quote-block-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/quote-block-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-card-hover-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-card-hover-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-card-hover-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-card-hover-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-card-normal-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-card-normal-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-card-normal-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-card-normal-webkit.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-stack-chromium.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-stack-firefox.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-stack-mobile.png delete mode 100644 tests/e2e/visual-regression.spec.ts-snapshots/rule-stack-webkit.png delete mode 100644 tests/performance/performance-monitor.js diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 1c679f6..1d3ff2e 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -307,13 +307,7 @@ jobs: - name: Build application run: npm run build - - name: Comprehensive Performance Testing - run: | - echo "π§ͺ Running comprehensive performance testing..." - npm run test:performance:ci - echo "β Performance testing complete" - - # 1) Sanity check that the build exists + # 1) Sanity check that the build exists - name: Verify Next build output run: | set -euo pipefail diff --git a/README.md b/README.md index 9f23e61..2324721 100644 --- a/README.md +++ b/README.md @@ -72,16 +72,27 @@ This project includes comprehensive performance optimizations for sub-2-second l ### Performance Monitoring -```bash -# Individual monitoring tools -npm run bundle:analyze # Analyze bundle sizes and budgets -npm run performance:monitor # Performance metrics and Lighthouse CI -npm run web-vitals:track # Core Web Vitals tracking +Performance testing is handled by: -# Comprehensive testing -npm run test:performance # All performance tests -npm run monitor:all # All monitoring tools -``` +- **Lighthouse CI** (`.lighthouserc.json`): Comprehensive performance testing in CI + + ```bash + npm run lhci # Run Lighthouse CI + npm run lhci:mobile # Mobile preset + npm run lhci:desktop # Desktop preset + npm run performance:budget # With performance budgets + ``` + +- **E2E Performance Tests** (`tests/e2e/performance.spec.ts`): Essential performance checks + + ```bash + npm run e2e:performance # Run E2E performance tests + ``` + +- **Bundle Analysis**: Analyze bundle sizes + ```bash + npm run bundle:analyze # Analyze bundle sizes + ``` ### Performance Targets diff --git a/docs/TESTING_GUIDE.md b/docs/TESTING_GUIDE.md index ce45fb0..0a35865 100644 --- a/docs/TESTING_GUIDE.md +++ b/docs/TESTING_GUIDE.md @@ -23,10 +23,10 @@ tests/ home.test.jsx blog.test.jsx e2e/ # True endβtoβend flows + visual regression (Playwright) - homepage.spec.ts - user-journeys.spec.ts - visual-regression.spec.ts - performance.spec.ts + critical-journeys.spec.ts # Main user journeys (homepage, navigation, interactions) + visual-regression.spec.ts # Critical page screenshots only (5 tests) + edge-cases.spec.ts # Critical error scenarios (4 tests) + performance.spec.ts # Essential performance checks (2 tests) utils/ # Shared test utilities componentTestSuite.tsx msw/ # MSW server setup for mocking @@ -38,6 +38,18 @@ tests/ **Component tests** (`tests/components/`) use the standard `componentTestSuite` utility to ensure consistent baseline coverage for all UI components. **Page tests** (`tests/pages/`) cover page-level rendering and flows. **E2E tests** (`tests/e2e/`) focus on critical user journeys, visual regression, and performance. **Accessibility E2E** (`tests/accessibility/e2e/`) provides high-level WCAG compliance checks. +### E2E Testing Philosophy + +E2E tests follow a **sparse, critical-path approach** optimized for open source projects: + +- **Focus on user value**: Test critical user journeys that span multiple pages or systems, not individual component interactions +- **Maintainability over coverage**: Keep tests maintainable and contributor-friendly rather than comprehensive +- **Visual regression is minimal**: Only capture screenshots of major pages (homepage, blog listing/post, 404), not every component or viewport +- **Performance monitoring is essential**: Track homepage load and Core Web Vitals, but detailed performance analysis is handled by Lighthouse CI +- **Edge cases are critical only**: Test scenarios that would break user experience (slow network, offline mode, JS errors, missing images) + +This approach reduces test maintenance burden while ensuring critical functionality remains stable. + ### Standard Component Test Suite Use the shared suite in `tests/utils/componentTestSuite.tsx` to get a consistent baseline: @@ -182,15 +194,51 @@ describe("Input β behaviour specifics", () => { ### E2E and Visual Regression -- Use **Playwright** for: - - Critical user journeys (e.g., create rule, navigate blog, key flows). - - Responsive behaviour and crossβbrowser checks. - - Visual regression (`tests/e2e/visual-regression.spec.ts`). +E2E tests are organized into focused files: - ```bash - npm run test:e2e - npm run visual:test - ``` +- **`critical-journeys.spec.ts`**: Main user journeys (homepage loads, navigation, key interactions) +- **`visual-regression.spec.ts`**: Critical page screenshots only (homepage full/viewport, blog listing/post, 404) +- **`edge-cases.spec.ts`**: Critical error scenarios (slow network, offline mode, JS errors, missing images) +- **`performance.spec.ts`**: Essential performance checks (homepage load, Core Web Vitals) + +**Commands:** + +```bash +# Run all E2E tests +npm run test:e2e + +# Run visual regression tests only +npm run visual:test + +# Update visual regression snapshots (after UI changes) +npm run visual:update + +# Run specific test file +npx playwright test tests/e2e/critical-journeys.spec.ts +``` + +**When to add E2E tests:** + +- **Add E2E tests** when: + - A new critical user journey is introduced (e.g., new multi-step flow) + - A major page is added that needs visual regression coverage + - A critical error scenario needs to be tested (e.g., payment failure, form submission errors) + +- **Don't add E2E tests** for: + - Component-level interactions (use component tests instead) + - Single-page functionality (use page tests instead) + - Minor UI changes (visual regression will catch major regressions) + - Edge cases that don't impact core user experience + +**Visual regression snapshots:** + +Visual regression tests capture screenshots of critical pages. When UI changes are intentional, update snapshots: + +```bash +npm run visual:update +``` + +This updates snapshots for all 5 critical page tests. Review the changes carefully before committing. ### Storybook diff --git a/package.json b/package.json index 79434ce..3a0a236 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,6 @@ "lhci:mobile": "lhci autorun --settings.preset=mobile", "lhci:desktop": "lhci autorun --settings.preset=desktop", "performance:budget": "lhci autorun --budgetPath=performance-budgets.json", - "performance:monitor": "node scripts/performance-monitor.js", - "test:lhci": "node scripts/test-lhci.js", "preview": "next build && next start -p 3000", "e2e:serve": "start-server-and-test preview http://localhost:3000 e2e", "seed-snapshots": "./scripts/seed-snapshots.sh", @@ -41,11 +39,7 @@ "analyze": "npm run analyze:browser && npm run analyze:server", "analyze:server": "ANALYZE=true npm run build", "analyze:browser": "BUNDLE_ANALYZE=true npm run build", - "bundle:analyze": "node scripts/bundle-analyzer.js", - "web-vitals:track": "node scripts/web-vitals-tracker.js", - "monitor:all": "npm run bundle:analyze && npm run performance:monitor && npm run web-vitals:track", - "test:performance": "node scripts/test-performance.js", - "test:performance:ci": "npm run test:performance" + "bundle:analyze": "node scripts/bundle-analyzer.js" }, "dependencies": { "@mdx-js/loader": "^3.1.1", diff --git a/playwright.config.ts b/playwright.config.ts index d13ae67..47e049f 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -33,14 +33,15 @@ export default defineConfig({ headless: true, }, // Only start webServer in non-CI environments (CI starts its own server) + // Use production server for E2E tests to match CI and ensure reliability ...(process.env.CI ? {} : { webServer: { - command: "npm run dev -- --port 3010", + command: "npm run build && npx next start -p 3010", url: "http://localhost:3010", - reuseExistingServer: true, - timeout: 120_000, + reuseExistingServer: !process.env.CI, + timeout: 180_000, // Increased timeout to account for build time }, }), // Browser-specific snapshot path template (includes projectName for cross-browser support) diff --git a/scripts/performance-monitor.js b/scripts/performance-monitor.js deleted file mode 100644 index 02e1a52..0000000 --- a/scripts/performance-monitor.js +++ /dev/null @@ -1,297 +0,0 @@ -#!/usr/bin/env node - -/** - * Performance Monitoring Script - * Monitors Core Web Vitals and performance metrics - */ - -const fs = require("fs"); -const path = require("path"); - -const PERFORMANCE_BUDGETS = require("../performance-budgets.json"); -const MONITORING_DIR = path.join(__dirname, "..", ".next", "monitoring"); - -class PerformanceMonitor { - constructor() { - this.metrics = { - timestamp: new Date().toISOString(), - coreWebVitals: {}, - bundleMetrics: {}, - recommendations: [], - }; - } - - /** - * Run comprehensive performance monitoring - */ - async monitorPerformance() { - console.log("π Starting performance monitoring..."); - - try { - // Ensure monitoring directory exists - if (!fs.existsSync(MONITORING_DIR)) { - fs.mkdirSync(MONITORING_DIR, { recursive: true }); - } - - // Run Lighthouse CI for Core Web Vitals - await this.runLighthouseCI(); - - // Analyze bundle performance - await this.analyzeBundlePerformance(); - - // Check performance budgets - this.checkPerformanceBudgets(); - - // Generate performance report - this.generatePerformanceReport(); - - console.log("β Performance monitoring complete!"); - console.log(`π Results saved to: ${MONITORING_DIR}`); - } catch (error) { - console.error("β Performance monitoring failed:", error.message); - process.exit(1); - } - } - - /** - * Run Lighthouse CI for Core Web Vitals - */ - async runLighthouseCI() { - console.log("π Running Lighthouse CI..."); - - try { - // Check if server is running - const { execSync } = require("child_process"); - try { - execSync("curl -s http://localhost:3000 > /dev/null", { - stdio: "pipe", - }); - } catch { - console.warn( - "β οΈ Development server not running, skipping Lighthouse CI...", - ); - return; - } - - // Run Lighthouse CI with performance focus - execSync("npx lhci autorun --collect.url=http://localhost:3000", { - stdio: "inherit", - cwd: path.join(__dirname, ".."), - }); - - // Parse Lighthouse results - await this.parseLighthouseResults(); - } catch { - console.warn("β οΈ Lighthouse CI failed, continuing with other metrics..."); - } - } - - /** - * Parse Lighthouse CI results - */ - async parseLighthouseResults() { - const lhciResultsPath = path.join(__dirname, "..", ".lighthouseci"); - - if (fs.existsSync(lhciResultsPath)) { - const files = fs.readdirSync(lhciResultsPath); - const resultFile = files.find((f) => f.endsWith(".json")); - - if (resultFile) { - const results = JSON.parse( - fs.readFileSync(path.join(lhciResultsPath, resultFile), "utf8"), - ); - - if (results.lhr && results.lhr.audits) { - this.metrics.coreWebVitals = { - lcp: this.getAuditScore( - results.lhr.audits, - "largest-contentful-paint", - ), - fid: this.getAuditScore(results.lhr.audits, "max-potential-fid"), - cls: this.getAuditScore( - results.lhr.audits, - "cumulative-layout-shift", - ), - fcp: this.getAuditScore( - results.lhr.audits, - "first-contentful-paint", - ), - tti: this.getAuditScore(results.lhr.audits, "interactive"), - performance: results.lhr.categories.performance?.score * 100 || 0, - }; - } - } - } - } - - /** - * Get audit score from Lighthouse results - */ - getAuditScore(audits, auditId) { - const audit = audits[auditId]; - if (!audit) return null; - - return { - score: audit.score * 100, - value: audit.numericValue, - displayValue: audit.displayValue, - }; - } - - /** - * Analyze bundle performance - */ - async analyzeBundlePerformance() { - console.log("π¦ Analyzing bundle performance..."); - - const bundleStatsPath = path.join( - __dirname, - "..", - ".next", - "static", - "chunks", - ); - - if (fs.existsSync(bundleStatsPath)) { - const files = fs.readdirSync(bundleStatsPath); - let totalSize = 0; - let jsFiles = 0; - - files.forEach((file) => { - if (file.endsWith(".js")) { - const filePath = path.join(bundleStatsPath, file); - const stats = fs.statSync(filePath); - totalSize += stats.size; - jsFiles++; - } - }); - - this.metrics.bundleMetrics = { - totalSizeKB: Math.round(totalSize / 1024), - totalSizeMB: Math.round((totalSize / (1024 * 1024)) * 100) / 100, - fileCount: jsFiles, - averageSizeKB: Math.round(totalSize / jsFiles / 1024), - }; - } - } - - /** - * Check performance budgets - */ - checkPerformanceBudgets() { - const budgets = PERFORMANCE_BUDGETS.budgets; - const violations = []; - - // Check Core Web Vitals - if (this.metrics.coreWebVitals.lcp) { - const lcpValue = this.metrics.coreWebVitals.lcp.value; - const lcpBudget = budgets.find((b) => b.name === "lcp")?.maxValue; - - if (lcpBudget && lcpValue > lcpBudget) { - violations.push({ - metric: "LCP", - current: lcpValue, - budget: lcpBudget, - severity: lcpValue > lcpBudget * 1.5 ? "high" : "medium", - }); - } - } - - // Check bundle size - if (this.metrics.bundleMetrics.totalSizeKB > 2000) { - violations.push({ - metric: "Bundle Size", - current: this.metrics.bundleMetrics.totalSizeKB, - budget: 2000, - severity: "medium", - }); - } - - this.metrics.budgetViolations = violations; - } - - /** - * Generate performance report - */ - generatePerformanceReport() { - const reportPath = path.join(MONITORING_DIR, "performance-report.json"); - fs.writeFileSync(reportPath, JSON.stringify(this.metrics, null, 2)); - - // Generate markdown report - this.generateMarkdownReport(); - } - - /** - * Generate markdown performance report - */ - generateMarkdownReport() { - const reportPath = path.join(MONITORING_DIR, "performance-report.md"); - - let report = `# Performance Monitoring Report\n\n`; - report += `**Generated:** ${this.metrics.timestamp}\n\n`; - - // Core Web Vitals - if (Object.keys(this.metrics.coreWebVitals).length > 0) { - report += `## Core Web Vitals\n\n`; - report += `| Metric | Score | Value | Status |\n`; - report += `|--------|-------|-------|--------|\n`; - - Object.entries(this.metrics.coreWebVitals).forEach(([metric, data]) => { - if (data && typeof data === "object" && data.score !== undefined) { - const status = this.getMetricStatus(metric, data.score); - report += `| ${metric.toUpperCase()} | ${data.score} | ${ - data.displayValue || "N/A" - } | ${status} |\n`; - } - }); - } - - // Bundle Metrics - if (Object.keys(this.metrics.bundleMetrics).length > 0) { - report += `\n## Bundle Metrics\n\n`; - report += `- **Total Size:** ${this.metrics.bundleMetrics.totalSizeMB}MB (${this.metrics.bundleMetrics.totalSizeKB}KB)\n`; - report += `- **File Count:** ${this.metrics.bundleMetrics.fileCount}\n`; - report += `- **Average Size:** ${this.metrics.bundleMetrics.averageSizeKB}KB per file\n`; - } - - // Budget Violations - if ( - this.metrics.budgetViolations && - this.metrics.budgetViolations.length > 0 - ) { - report += `\n## Budget Violations\n\n`; - this.metrics.budgetViolations.forEach((violation) => { - report += `- **${violation.metric}**: ${violation.current} (exceeds ${ - violation.budget - }) - ${violation.severity.toUpperCase()}\n`; - }); - } - - // Recommendations - report += `\n## Recommendations\n\n`; - report += `- Monitor Core Web Vitals regularly\n`; - report += `- Implement code splitting for large bundles\n`; - report += `- Use dynamic imports for non-critical components\n`; - report += `- Optimize images and fonts\n`; - report += `- Enable compression and caching\n`; - - fs.writeFileSync(reportPath, report); - } - - /** - * Get status emoji for metric score - */ - getMetricStatus(metric, score) { - if (score >= 90) return "β Good"; - if (score >= 50) return "β οΈ Needs Improvement"; - return "β Poor"; - } -} - -// Run monitoring if called directly -if (require.main === module) { - const monitor = new PerformanceMonitor(); - monitor.monitorPerformance().catch(console.error); -} - -module.exports = PerformanceMonitor; diff --git a/scripts/test-lhci.js b/scripts/test-lhci.js deleted file mode 100644 index 621a907..0000000 --- a/scripts/test-lhci.js +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env node - -/** - * Simple test script to verify LHCI configuration - * This script validates the configuration without running actual tests - */ - -const fs = require("fs"); -const path = require("path"); - -console.log("π Testing LHCI Configuration...\n"); - -// Check if .lighthouserc.json exists -const configPath = path.join(process.cwd(), ".lighthouserc.json"); -if (fs.existsSync(configPath)) { - console.log("β .lighthouserc.json found"); - - try { - const config = JSON.parse(fs.readFileSync(configPath, "utf8")); - console.log("β Configuration is valid JSON"); - - if (config.ci && config.ci.collect && config.ci.assert) { - console.log("β Configuration has required sections (collect, assert)"); - console.log(`β Testing ${config.ci.collect.numberOfRuns} runs`); - console.log(`β URL: ${config.ci.collect.url[0]}`); - } else { - console.log("β Configuration missing required sections"); - } - } catch (error) { - console.log("β Configuration is not valid JSON:", error.message); - } -} else { - console.log("β .lighthouserc.json not found"); -} - -// Check if @lhci/cli is installed -try { - const { execSync } = require("child_process"); - execSync("npx lhci --version", { stdio: "pipe" }); - console.log("β @lhci/cli package is installed and working"); -} catch (error) { - console.log("β @lhci/cli package is not working:", error.message); -} - -// Check package.json scripts -const packagePath = path.join(process.cwd(), "package.json"); -if (fs.existsSync(packagePath)) { - try { - const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8")); - if (packageJson.scripts && packageJson.scripts.lhci) { - console.log("β LHCI script found in package.json"); - } else { - console.log("β LHCI script not found in package.json"); - } - } catch (error) { - console.log("β Error reading package.json:", error.message); - } -} - -console.log("\nπ LHCI Configuration Test Complete!"); -console.log( - "Note: Actual LHCI tests may fail locally due to Node.js architecture issues on macOS.", -); -console.log( - "The CI environment should work correctly with the provided configuration.", -); diff --git a/scripts/test-performance.js b/scripts/test-performance.js deleted file mode 100644 index d655e54..0000000 --- a/scripts/test-performance.js +++ /dev/null @@ -1,349 +0,0 @@ -#!/usr/bin/env node - -/** - * Comprehensive Performance Testing Script - * Integrates bundle analysis, performance monitoring, and Web Vitals tracking - */ - -const { execSync } = require("child_process"); -const fs = require("fs"); -const path = require("path"); - -const TEST_RESULTS_DIR = path.join(__dirname, "..", ".next", "test-results"); - -class PerformanceTester { - constructor() { - this.results = { - timestamp: new Date().toISOString(), - bundleAnalysis: {}, - performanceMonitoring: {}, - webVitals: {}, - lighthouse: {}, - summary: { - passed: 0, - failed: 0, - warnings: 0, - total: 0, - }, - }; - } - - /** - * Run comprehensive performance testing - */ - async runTests() { - console.log("π§ͺ Starting comprehensive performance testing..."); - - try { - // Ensure test results directory exists - if (!fs.existsSync(TEST_RESULTS_DIR)) { - fs.mkdirSync(TEST_RESULTS_DIR, { recursive: true }); - } - - // 1. Bundle Analysis - console.log("π Running bundle analysis..."); - await this.runBundleAnalysis(); - - // 2. Performance Monitoring - console.log("π Running performance monitoring..."); - await this.runPerformanceMonitoring(); - - // 3. Web Vitals Tracking - console.log("π Setting up Web Vitals tracking..."); - await this.runWebVitalsTracking(); - - // 4. Lighthouse CI (if server is available) - console.log("π Running Lighthouse CI..."); - await this.runLighthouseCI(); - - // 5. Generate comprehensive report - this.generateComprehensiveReport(); - - console.log("β Performance testing complete!"); - console.log(`π Results saved to: ${TEST_RESULTS_DIR}`); - - // Return exit code based on results - const hasFailures = this.results.summary.failed > 0; - if (hasFailures) { - console.log("β Performance tests failed"); - process.exit(1); - } else { - console.log("β All performance tests passed"); - process.exit(0); - } - } catch (error) { - console.error("β Performance testing failed:", error.message); - process.exit(1); - } - } - - /** - * Run bundle analysis - */ - async runBundleAnalysis() { - try { - execSync("npm run bundle:analyze", { stdio: "inherit" }); - - // Parse bundle analysis results - const bundleReportPath = path.join( - __dirname, - "..", - ".next", - "analyze", - "bundle-analysis.json", - ); - if (fs.existsSync(bundleReportPath)) { - const bundleData = JSON.parse( - fs.readFileSync(bundleReportPath, "utf8"), - ); - this.results.bundleAnalysis = bundleData; - - // Check for budget violations - if ( - bundleData.budgetViolations && - bundleData.budgetViolations.length > 0 - ) { - this.results.summary.failed += bundleData.budgetViolations.length; - console.log( - `β οΈ Found ${bundleData.budgetViolations.length} budget violations`, - ); - } else { - this.results.summary.passed += 1; - console.log("β Bundle analysis passed"); - } - } - - this.results.summary.total += 1; - } catch (error) { - console.error("β Bundle analysis failed:", error.message); - this.results.summary.failed += 1; - this.results.summary.total += 1; - } - } - - /** - * Run performance monitoring - */ - async runPerformanceMonitoring() { - try { - execSync("npm run performance:monitor", { stdio: "inherit" }); - - // Parse performance monitoring results - const perfReportPath = path.join( - __dirname, - "..", - ".next", - "monitoring", - "performance-report.json", - ); - if (fs.existsSync(perfReportPath)) { - const perfData = JSON.parse(fs.readFileSync(perfReportPath, "utf8")); - this.results.performanceMonitoring = perfData; - - // Check for budget violations - if (perfData.budgetViolations && perfData.budgetViolations.length > 0) { - this.results.summary.failed += perfData.budgetViolations.length; - console.log( - `β οΈ Found ${perfData.budgetViolations.length} performance violations`, - ); - } else { - this.results.summary.passed += 1; - console.log("β Performance monitoring passed"); - } - } - - this.results.summary.total += 1; - } catch (error) { - console.error("β Performance monitoring failed:", error.message); - this.results.summary.failed += 1; - this.results.summary.total += 1; - } - } - - /** - * Run Web Vitals tracking - */ - async runWebVitalsTracking() { - try { - execSync("npm run web-vitals:track", { stdio: "inherit" }); - - // Parse Web Vitals results - const vitalsReportPath = path.join( - __dirname, - "..", - ".next", - "web-vitals", - "report.json", - ); - if (fs.existsSync(vitalsReportPath)) { - const vitalsData = JSON.parse( - fs.readFileSync(vitalsReportPath, "utf8"), - ); - this.results.webVitals = vitalsData; - console.log("β Web Vitals tracking setup complete"); - } - - this.results.summary.passed += 1; - this.results.summary.total += 1; - } catch (error) { - console.error("β Web Vitals tracking failed:", error.message); - this.results.summary.failed += 1; - this.results.summary.total += 1; - } - } - - /** - * Run Lighthouse CI - */ - async runLighthouseCI() { - try { - // Check if server is running - try { - execSync("curl -s http://localhost:3000 > /dev/null", { - stdio: "pipe", - }); - } catch { - console.warn( - "β οΈ Development server not running, skipping Lighthouse CI...", - ); - this.results.summary.warnings += 1; - this.results.summary.total += 1; - return; - } - - execSync("npm run lhci", { stdio: "inherit" }); - - // Parse Lighthouse results - const lhciResultsPath = path.join(__dirname, "..", ".lighthouseci"); - if (fs.existsSync(lhciResultsPath)) { - const files = fs.readdirSync(lhciResultsPath); - const resultFile = files.find((f) => f.endsWith(".json")); - - if (resultFile) { - const lhciData = JSON.parse( - fs.readFileSync(path.join(lhciResultsPath, resultFile), "utf8"), - ); - this.results.lighthouse = lhciData; - console.log("β Lighthouse CI completed"); - } - } - - this.results.summary.passed += 1; - this.results.summary.total += 1; - } catch (error) { - console.warn("β οΈ Lighthouse CI failed:", error.message); - this.results.summary.warnings += 1; - this.results.summary.total += 1; - } - } - - /** - * Generate comprehensive test report - */ - generateComprehensiveReport() { - // Ensure test results directory exists - if (!fs.existsSync(TEST_RESULTS_DIR)) { - fs.mkdirSync(TEST_RESULTS_DIR, { recursive: true }); - } - - const reportPath = path.join( - TEST_RESULTS_DIR, - "performance-test-report.json", - ); - fs.writeFileSync(reportPath, JSON.stringify(this.results, null, 2)); - - // Generate markdown report - this.generateMarkdownReport(); - } - - /** - * Generate markdown test report - */ - generateMarkdownReport() { - const reportPath = path.join( - TEST_RESULTS_DIR, - "performance-test-report.md", - ); - - let report = `# Performance Test Report\n\n`; - report += `**Generated:** ${this.results.timestamp}\n\n`; - - // Summary - report += `## Test Summary\n\n`; - report += `- **Total Tests:** ${this.results.summary.total}\n`; - report += `- **Passed:** ${this.results.summary.passed} β \n`; - report += `- **Failed:** ${this.results.summary.failed} β\n`; - report += `- **Warnings:** ${this.results.summary.warnings} β οΈ\n\n`; - - // Bundle Analysis Results - if (Object.keys(this.results.bundleAnalysis).length > 0) { - report += `## Bundle Analysis\n\n`; - if ( - this.results.bundleAnalysis.budgetViolations && - this.results.bundleAnalysis.budgetViolations.length > 0 - ) { - report += `### Budget Violations\n\n`; - this.results.bundleAnalysis.budgetViolations.forEach((violation) => { - report += `- **${violation.file}**: ${ - violation.currentSize - }KB (exceeds ${violation.maxSize}KB by ${ - violation.overage - }KB) - ${violation.severity.toUpperCase()}\n`; - }); - } else { - report += `β No bundle budget violations found\n\n`; - } - } - - // Performance Monitoring Results - if (Object.keys(this.results.performanceMonitoring).length > 0) { - report += `## Performance Monitoring\n\n`; - if ( - this.results.performanceMonitoring.budgetViolations && - this.results.performanceMonitoring.budgetViolations.length > 0 - ) { - report += `### Budget Violations\n\n`; - this.results.performanceMonitoring.budgetViolations.forEach( - (violation) => { - report += `- **${violation.metric}**: ${ - violation.current - } (exceeds ${ - violation.budget - }) - ${violation.severity.toUpperCase()}\n`; - }, - ); - } else { - report += `β No performance budget violations found\n\n`; - } - } - - // Web Vitals Results - if (Object.keys(this.results.webVitals).length > 0) { - report += `## Web Vitals Tracking\n\n`; - report += `β Web Vitals tracking setup complete\n\n`; - } - - // Lighthouse Results - if (Object.keys(this.results.lighthouse).length > 0) { - report += `## Lighthouse CI\n\n`; - report += `β Lighthouse CI completed successfully\n\n`; - } - - // Recommendations - report += `## Recommendations\n\n`; - report += `- Monitor bundle sizes regularly\n`; - report += `- Track Core Web Vitals in production\n`; - report += `- Run performance tests in CI/CD pipeline\n`; - report += `- Set up performance budgets and alerts\n`; - - fs.writeFileSync(reportPath, report); - } -} - -// Run if called directly -if (require.main === module) { - const tester = new PerformanceTester(); - tester.runTests().catch(console.error); -} - -module.exports = PerformanceTester; diff --git a/scripts/web-vitals-tracker.js b/scripts/web-vitals-tracker.js deleted file mode 100644 index 0f23302..0000000 --- a/scripts/web-vitals-tracker.js +++ /dev/null @@ -1,335 +0,0 @@ -#!/usr/bin/env node - -/** - * Web Vitals Tracker - * Real-time monitoring of Core Web Vitals in production - */ - -const fs = require("fs"); -const path = require("path"); - -const WEB_VITALS_DIR = path.join(__dirname, "..", ".next", "web-vitals"); - -class WebVitalsTracker { - constructor() { - this.metrics = { - timestamp: new Date().toISOString(), - vitals: { - lcp: [], - fid: [], - cls: [], - fcp: [], - ttfb: [], - }, - summary: {}, - }; - } - - /** - * Track Web Vitals from client-side - */ - trackWebVitals() { - const trackingCode = ` -// Web Vitals Tracking Script -(function() { - // Import web-vitals library - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - const vitals = {}; - - // Track Largest Contentful Paint - getLCP((metric) => { - vitals.lcp = { - value: metric.value, - rating: metric.rating, - delta: metric.delta, - timestamp: Date.now() - }; - sendVitals('lcp', vitals.lcp); - }); - - // Track First Input Delay - getFID((metric) => { - vitals.fid = { - value: metric.value, - rating: metric.rating, - delta: metric.delta, - timestamp: Date.now() - }; - sendVitals('fid', vitals.fid); - }); - - // Track Cumulative Layout Shift - getCLS((metric) => { - vitals.cls = { - value: metric.value, - rating: metric.rating, - delta: metric.delta, - timestamp: Date.now() - }; - sendVitals('cls', vitals.cls); - }); - - // Track First Contentful Paint - getFCP((metric) => { - vitals.fcp = { - value: metric.value, - rating: metric.rating, - delta: metric.delta, - timestamp: Date.now() - }; - sendVitals('fcp', vitals.fcp); - }); - - // Track Time to First Byte - getTTFB((metric) => { - vitals.ttfb = { - value: metric.value, - rating: metric.rating, - delta: metric.delta, - timestamp: Date.now() - }; - sendVitals('ttfb', vitals.ttfb); - }); - }); - - // Send vitals to server - function sendVitals(metric, data) { - if (typeof window !== 'undefined' && window.fetch) { - fetch('/api/web-vitals', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - metric, - data, - url: window.location.href, - userAgent: navigator.userAgent, - timestamp: Date.now() - }) - }).catch(console.error); - } - } -})(); -`; - - return trackingCode; - } - - /** - * Create API endpoint for receiving Web Vitals - */ - createAPIEndpoint() { - const apiCode = ` -// API endpoint for Web Vitals tracking -export default function handler(req, res) { - if (req.method !== 'POST') { - return res.status(405).json({ error: 'Method not allowed' }); - } - - try { - const { metric, data, url, userAgent, timestamp } = req.body; - - // Store the metric data - const vitalsData = { - metric, - data, - url, - userAgent, - timestamp: new Date(timestamp).toISOString() - }; - - // In production, you would save this to a database - // For now, we'll log it - console.log('Web Vital received:', vitalsData); - - res.status(200).json({ success: true }); - } catch (error) { - console.error('Error processing web vital:', error); - res.status(500).json({ error: 'Internal server error' }); - } -} -`; - - return apiCode; - } - - /** - * Generate Web Vitals dashboard - */ - generateDashboard() { - const dashboardCode = ` -import React, { useState, useEffect } from 'react'; - -const WebVitalsDashboard = () => { - const [vitals, setVitals] = useState({ - lcp: { value: 0, rating: 'unknown' }, - fid: { value: 0, rating: 'unknown' }, - cls: { value: 0, rating: 'unknown' }, - fcp: { value: 0, rating: 'unknown' }, - ttfb: { value: 0, rating: 'unknown' } - }); - - useEffect(() => { - // In a real implementation, you would fetch from your database - // For now, we'll use localStorage for demo purposes - const storedVitals = localStorage.getItem('web-vitals'); - if (storedVitals) { - setVitals(JSON.parse(storedVitals)); - } - }, []); - - const getRatingColor = (rating) => { - switch (rating) { - case 'good': return 'text-green-600'; - case 'needs-improvement': return 'text-yellow-600'; - case 'poor': return 'text-red-600'; - default: return 'text-gray-600'; - } - }; - - const getRatingIcon = (rating) => { - switch (rating) { - case 'good': return 'β '; - case 'needs-improvement': return 'β οΈ'; - case 'poor': return 'β'; - default: return 'β'; - } - }; - - return ( -
This is the main content of the test article.
It has multiple paragraphs.
", -}; - -describe("Content Page Rendering E2E", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - describe("ContentBanner Component", () => { - it("should render blog post banner with correct information", () => { - render(vb623?U9(Q8R0t*G@j9HX&BJ*Koww**)+-%RZ;L!-81$iVLF `uA0qo%!2+@e8Ka+eP
z_Tp7=n7~#6iLE5`*yCQgtM?gh?r+yn7f1Ce`6$tHeU*8m;}Gj4H4!PF-%LeRZNcb-
z6;*b3s*jlgBm%~J4Xul-s#~eeCrkw9Z6Z@q3qAu#(cI;!rlvaSP|mO9kyPSC!3rnz
zoWc9lfGsh9hY3tPEMOsuh*F1Gi_Ad`K4+<1^EMN+OjkMP&Gq@;vj7qg4Ju4;He;n0
zj1A)wSU1a?Bk6^wLImFw>8=VXGS(_9HU-EAlpMsGMa(xFXv8^zAF|h;IA)kkVAS=M
zpxMA^%k|{Gl^O^WG;)Do-u<_ptC;|3F=5Y7vjx4sg1Uex=z6NmnK@-#M6GdJln1B=
z>Cx~OOuDdyqP#0q%B1^~m=^U3k(Dv%*XHCys&QI`{&vn=g(%2mNhu$&8koybwp
z)A;E_Og8%ZuxM46^m(rAwm
zI_}rr7ZUl#g5Mj`5if*}KD6_K{R$gk=kE_yR6mL6Vq4WMGFUQ2>UPM)y7-NDe6eeu
zpg}-|E`
{HEr0yI$5!d`t~R1zQlHr($ZJ%dKrZQ&{1)4cv>l9FLZ1uRk}9&kjtM<
zryrgSEN`1&iyc*G^Z(eEB8qFfYtCb&j+M=b+hB~lbebEDf3I=$%}(cXHGQ5l#R-Fa
zb+#k68|ySHxF#{vv6?g3%AZDUwH7+=nzWa~v!0Tbj9PJ5@LicdzxY`1Ubo