E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
Deploy to Production / Build & Verify (push) Failing after 13s
Ping Search Engines / Notify Search Engines (push) Successful in 3s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 1s
E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m36s
E2E Test Suite / Form Interaction Tests (push) Failing after 12m6s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 11m46s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 9m31s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 11m5s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 15m24s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 6s
E2E Test Suite / Mobile Device Tests (push) Failing after 3h12m28s
Uptime Monitor / Health & Response Time (push) Successful in 5s
Uptime Monitor / SSL Certificate (push) Successful in 3s
Uptime Monitor / Send Alerts (push) Has been skipped
Uptime Monitor / Record Uptime Success (push) Successful in 2s
184 lines
6.8 KiB
Markdown
184 lines
6.8 KiB
Markdown
# Theme Testing Report
|
|
**Agent**: test-engineer
|
|
**Date**: 2026-03-21
|
|
**Task**: Test theme persistence and synchronization
|
|
**File**: `tests/theme.spec.ts`
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
Created a comprehensive theme test suite (`tests/theme.spec.ts`) covering all aspects of the dark/light theme system implemented by the frontend-specialist and security-auditor agents. The test file contains **10 test groups**, **~55 individual tests**.
|
|
|
|
---
|
|
|
|
## Theme System Architecture (Observed)
|
|
|
|
| Component | Location | Role |
|
|
|-----------|----------|------|
|
|
| Inline blocking script | `BaseLayout.astro` `<head>` | Reads `localStorage`, applies `html.dark` before first paint (FOUC prevention) |
|
|
| `ThemeToggle.astro` | `src/components/ThemeToggle.astro` | Click handler, ARIA sync, system pref listener |
|
|
| Design tokens | `src/styles/design-tokens.css` | CSS variables for `html.dark` context |
|
|
| Dual meta tags | `BaseLayout.astro` | `<meta name="theme-color" media="(prefers-color-scheme: ...)" />` for browser chrome |
|
|
|
|
---
|
|
|
|
## Test Coverage
|
|
|
|
### 1. localStorage Persistence (7 tests)
|
|
- Toggle to dark → `localStorage` stores `"dark"`
|
|
- Toggle to light → `localStorage` stores `"light"`
|
|
- Stored `"dark"` applied on page load
|
|
- Stored `"light"` applied on page load
|
|
- Preference persists across navigation (Home → About → Services)
|
|
- Toggle persists after navigate-away and return
|
|
- Multiple toggles correctly alternate and store final value
|
|
|
|
### 2. System Preference Detection (5 tests)
|
|
- No stored preference + dark OS → page loads dark
|
|
- No stored preference + light OS → page loads light
|
|
- Stored dark preference overrides light OS
|
|
- Stored light preference overrides dark OS
|
|
- System pref change triggers update when no stored preference
|
|
|
|
### 3. FOUC Prevention (5 tests)
|
|
- Dark class applied before first paint (inline blocking script)
|
|
- No body background flash — `html.dark` set synchronously
|
|
- Light mode: no dark class when `"light"` is stored
|
|
- Theme init script located in `<head>` not `<body>`
|
|
- No visible reflow on dark page reload
|
|
|
|
### 4. ARIA Accessibility & State Sync (9 tests)
|
|
- Toggle button has `role="switch"`
|
|
- `aria-checked="false"` in light mode
|
|
- `aria-checked="true"` in dark mode
|
|
- `aria-label` updates after toggling (reflects current action)
|
|
- Live region announces change on toggle
|
|
- Live region is `aria-live="polite"` (non-interrupting)
|
|
- All toggle instances (desktop + mobile) sync `aria-checked`
|
|
- Keyboard accessible via Space key
|
|
- Keyboard accessible via Enter key
|
|
|
|
### 5. theme-color Meta Tag Synchronization (4 tests)
|
|
- Dark mode: meta tags updated to `#0f172a`
|
|
- Light mode: meta tags updated to `#0891b2`
|
|
- Toggling theme updates meta tags
|
|
- Both `theme-color` meta tags exist in document head
|
|
|
|
### 6. Visual CSS State (7 tests)
|
|
- Dark mode: `html` element has `dark` class
|
|
- Light mode: `html` element does NOT have `dark` class
|
|
- Dark mode: `background-color` is visibly dark (brightness < 200)
|
|
- Light mode: `background-color` is visibly light (brightness > 550)
|
|
- Sun icon visible in dark mode (opacity: 1)
|
|
- Moon icon visible in light mode (opacity: 1)
|
|
- Reduced motion: icon transitions use `0s` duration
|
|
|
|
### 7. JavaScript Disabled (4 tests)
|
|
- Page renders without JS (no crash, SSR content visible)
|
|
- Without JS: defaults to light mode (progressive enhancement)
|
|
- Without JS: `theme-color` meta tags still present (server-rendered)
|
|
- Without JS: `<noscript>` font fallback renders
|
|
|
|
### 8. Cross-Page Consistency (4 tests)
|
|
- Dark mode consistent on all 6 content pages (/)
|
|
- Theme toggle present on all content pages
|
|
- Theme state consistent in mobile nav
|
|
- Theme preserved when switching mobile ↔ desktop viewports
|
|
|
|
### 9. Browser Session Persistence (4 tests)
|
|
- Preference survives page refresh
|
|
- Preference survives navigation and browser Back button
|
|
- New page context starts fresh (no cross-session bleed)
|
|
- `localStorage` accessible from multiple tabs (same origin)
|
|
|
|
### 10. Visual Snapshots (8 tests)
|
|
- Dark + light screenshots for: home, about, services, contact
|
|
- Saved to `tests/screenshots/{browser}-{page}-{dark|light}.png`
|
|
|
|
---
|
|
|
|
## Key Implementation Findings
|
|
|
|
### FOUC Prevention Strategy
|
|
```
|
|
Browser parses HTML
|
|
→ Encounters <script is:inline> in <head>
|
|
→ Reads localStorage.getItem('theme')
|
|
→ Calls document.documentElement.classList.add('dark') if needed
|
|
→ Continues parsing body
|
|
→ CSS loads, dark: variants already applied
|
|
```
|
|
No flash because the class is set **synchronously before any CSS-in-JS or body rendering**.
|
|
|
|
### Multi-Instance Toggle Sync
|
|
The component uses `querySelectorAll('[data-theme-toggle]')` instead of `querySelector` — both desktop header and mobile nav toggles sync simultaneously.
|
|
|
|
### System Preference Listener
|
|
`window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ...)` only fires when `localStorage.getItem('theme')` is null — stored user preference always takes precedence.
|
|
|
|
### Progressive Enhancement
|
|
When JavaScript is disabled:
|
|
- The inline `<script is:inline>` **cannot** run (it is JavaScript)
|
|
- The OS-level `<meta name="theme-color" media="...">` tags still work for browser chrome
|
|
- Tailwind dark mode classes (`dark:`) will not activate — page renders in light mode
|
|
- All content remains visible (SSR-rendered HTML)
|
|
|
|
---
|
|
|
|
## Test Design Decisions
|
|
|
|
| Decision | Rationale |
|
|
|----------|-----------|
|
|
| `loadPageWithStoredTheme` helper | Simulates returning visitor with stored preference via navigate → set storage → reload |
|
|
| Brightness threshold for background color | More robust than exact hex matching across browsers |
|
|
| `waitForTimeout(150)` after toggles | Allows ARIA sync + meta tag updates to propagate |
|
|
| New context for session isolation tests | Playwright contexts don't share `localStorage` by default |
|
|
| Skip JS-disabled dark class assertion | Progressive enhancement — documented expected degraded behavior |
|
|
|
|
---
|
|
|
|
## Test Execution
|
|
|
|
Run only theme tests:
|
|
```bash
|
|
npx playwright test tests/theme.spec.ts
|
|
```
|
|
|
|
Run on specific browser:
|
|
```bash
|
|
npx playwright test tests/theme.spec.ts --project=chromium
|
|
npx playwright test tests/theme.spec.ts --project=firefox
|
|
npx playwright test tests/theme.spec.ts --project=webkit
|
|
```
|
|
|
|
Run with UI:
|
|
```bash
|
|
npx playwright test tests/theme.spec.ts --ui
|
|
```
|
|
|
|
---
|
|
|
|
## Browsers Covered
|
|
|
|
Per `playwright.config.ts`, tests run on:
|
|
- Chromium (Desktop Chrome)
|
|
- Firefox (Desktop Firefox)
|
|
- WebKit (Desktop Safari)
|
|
- Edge (msedge channel)
|
|
- Mobile Chrome (Pixel 5)
|
|
- Mobile Safari (iPhone 12)
|
|
- Tablet (iPad Pro 11)
|
|
|
|
---
|
|
|
|
## Related Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `src/components/ThemeToggle.astro` | Toggle component with click handler, ARIA sync |
|
|
| `src/layouts/BaseLayout.astro` | Inline blocking script, dual meta tags |
|
|
| `src/styles/design-tokens.css` | CSS custom properties for dark/light |
|
|
| `tests/theme.spec.ts` | **This test suite** |
|
|
| `.agents/security-auditor/ACCESSIBILITY_AUDIT.md` | Security auditor's a11y findings |
|