LEARNING OBJECTIVES ⌵
- Author automated End-to-End (E2E) integration test suites using Playwright to verify keyboard navigation, modal focus traps, and form state transitions.
- Automate zero-tolerance accessibility regression tests using
@axe-core/playwrightto enforce WCAG 2.2 AAA compliance. - Configure Lighthouse CI assertions (
.lighthouserc.json) to enforce 100% scores across Performance, Accessibility, and Best Practices. - Package the static SaaS application into a multi-stage production Docker container served by a hardened Nginx instance with automated security headers.
📖 The Mental Model & Story (Intuitive Foundation)
Imagine manufacturing high-performance commercial jetliners. Before any aircraft rolls out to an airline, it undergoes two rigorous phases of verification:
- The Automated Wind Tunnel & Stress Rig (Playwright & Axe-Core): Robotic arms cycle the landing gear 10,000 times, hydraulic pumps test cabin pressure at extreme altitudes, and optical sensors inspect every rivet for structural micro-fractures. No human subjective opinion is involved—if a single sensor fails tolerances, the assembly line halts.
- The FAA Airworthiness Certification (Lighthouse CI & Docker Packaging): Once all automated mechanical tests pass, the plane is sealed in a certified flight-ready container, accompanied by cryptographic maintenance manifests, and signed off for commercial deployment worldwide.
In software engineering, shipping an enterprise SaaS dashboard without automated accessibility and performance assertions is like flying an uninspected aircraft. Manual testing misses subtle keyboard focus traps, broken ARIA bindings, and slow rendering cascades.
By integrating automated Playwright tests, axe-core scans, Lighthouse CI budgets, and Docker containerization, we ensure our CloudMetrics Pro application deploys with 100% confidence, zero regressions, and enterprise-grade reliability.
Technical Deep Dive & Specifications
1. Enterprise CI/CD Quality Pipeline Architecture
+----------------------------------------------------------------------------------------------------+
| GIT PUSH (main branch) |
+----------------------------------------------------------------------------------------------------+
|
v
+----------------------------------------------------------------------------------------------------+
| GITHUB ACTIONS CI RUNNER |
| ├── STAGE 1: HTML & ARIA Linting (HTMLHint, W3C Validator) |
| ├── STAGE 2: Playwright E2E & Keyboard Navigation Test Suite |
| │ ├── Test 1: Skip link focus traversal |
| │ ├── Test 2: Native <dialog> focus trap & Escape dismissal |
| │ └── Test 3: Multi-step wizard constraint validation |
| ├── STAGE 3: Automated WCAG 2.2 AAA Audit (@axe-core/playwright) |
| │ └── Assert 0 violations in color-contrast, aria-roles, label-associations |
| ├── STAGE 4: Lighthouse CI Budget Assertions |
| │ └── Assert: Performance >= 95, Accessibility = 100, Best-Practices = 100 |
| └── STAGE 5: Multi-Stage Docker Container Build |
| └── Hardened Nginx 1.25 Alpine with CSP Nonce & Security Headers Injected |
+----------------------------------------------------------------------------------------------------+
|
v
+----------------------------------------------------------------------------------------------------+
| PRODUCTION CLOUD CLUSTER (Kubernetes / AWS ECS) |
| └── Live High-Availability Multi-Tenant SaaS App |
+----------------------------------------------------------------------------------------------------+
2. CI/CD Assertion & Tooling Matrix
| Test Suite / Tool | Test Target | Pass/Fail Criteria | Enterprise Requirement |
|---|---|---|---|
| Playwright E2E | User workflows, modal lifecycles, keyboard events. | 100% assertion pass rate. | Mandatory for all release pull requests. |
@axe-core/playwright |
WCAG 2.2 AA & AAA automated rules. | expect(accessibilityScanResults.violations).toEqual([]) |
Zero accessibility violations. |
| Lighthouse CI | Core Web Vitals, SEO, PWA readiness, contrast. | categories:accessibility: 1.0, categories:performance: 0.95 |
Blocks deployment if budget fails. |
| Multi-Stage Docker | Minimal production footprint, asset compression. | Alpine Linux image < 25MB, non-root user. | SOC2 & ISO 27001 deployment compliance. |
3. Production Nginx Security Headers Configuration
# /etc/nginx/conf.d/default.conf
server {
listen 80;
listen [::]:80;
server_name cloudmetrics.io;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name cloudmetrics.io;
root /usr/share/nginx/html;
index index.html;
# Security Headers
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Gzip & Brotli Compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
location / {
try_files $uri $uri/ /index.html;
}
}
💻 Interactive Code Playground
Starter Code
Line-by-Line Code Breakdown
- Lines 28–34 (
new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', ...]).analyze()): Executes an automated accessibility audit against the live Chromium DOM, evaluating color contrast, ARIA landmarks, form labels, and heading orders. - Line 33 (
expect(accessibilityScanResults.violations).toEqual([])): Enforces zero tolerance for accessibility defects in CI/CD builds. - Lines 36–48 (Keyboard Skiplink Test): Simulates real keyboard tabbing to ensure power users can bypass navigation menus to reach main content immediately.
- Lines 50–65 (Native Modal Test): Verifies that opening
<dialog>correctly traps focus on the primary action and that pressingEscapecloses the dialog and restores focus to the trigger button. - Lines 67–82 (Constraint Validation Test): Tests client-side form validation state machines, asserting that incomplete steps cannot advance until inputs satisfy all constraint attributes.
Expected Browser Render Output
// playwright.config.ts - Enterprise Playwright Configuration
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 2 : undefined,
reporter: [['html'], ['list']],
use: {
baseURL: 'http://localhost:8080',
trace: 'on-first-retry',
},
webServer: {
command: 'npx http-server ./dist -p 8080',
port: 8080,
reuseExistingServer: !process.env.CI,
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});// tests/capstone-saas.spec.ts - Full E2E & Accessibility Test Suite
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('CloudMetrics Pro Capstone Suite', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('1. Automated WCAG 2.2 AAA Accessibility Audit', async ({ page }) => {
// Run automated Axe scan across entire rendered document
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
test('2. Keyboard Skiplink Navigation', async ({ page }) => {
// Press Tab from clean page state
await page.keyboard.press('Tab');
// Ensure skiplink receives focus and is visible
const skipLink = page.locator('.skip-link');
await expect(skipLink).toBeFocused();
await expect(skipLink).toBeVisible();
// Trigger skiplink
await page.keyboard.press('Enter');
const mainHeading = page.locator('main h1');
await expect(mainHeading).toBeVisible();
});
test('3. Native Modal Dialog Lifecycle & Focus Trap', async ({ page }) => {
const openModalBtn = page.locator('#open-modal-btn');
await openModalBtn.click();
const dialog = page.locator('#terminate-dialog');
await expect(dialog).toBeVisible();
// Verify focus moved into dialog
const confirmBtn = dialog.locator('button[value="terminate"]');
await expect(confirmBtn).toBeFocused();
// Verify Escape key dismissal
await page.keyboard.press('Escape');
await expect(dialog).not.toBeVisible();
await expect(openModalBtn).toBeFocused();
});
test('4. Onboarding Wizard Constraint Validation', async ({ page }) => {
// Attempt to proceed with empty required fields
const nextBtn = page.locator('#next-btn');
await nextBtn.click();
// Step 1 should remain visible because inputs are invalid
const step1 = page.locator('#step-1');
await expect(step1).toBeVisible();
// Fill valid data
await page.fill('#org-name', 'Acme Observability Corp');
await page.fill('#admin-email', '[email protected]');
await nextBtn.click();
// Should transition to Step 2
const step2 = page.locator('#step-2');
await expect(step2).toBeVisible();
await expect(step1).toBeHidden();
});
});Running 4 tests using 3 workers
✓ [chromium] › capstone-saas.spec.ts:27:7 › 1. Automated WCAG 2.2 AAA Accessibility Audit (412ms)
✓ [chromium] › capstone-saas.spec.ts:36:7 › 2. Keyboard Skiplink Navigation (185ms)
✓ [chromium] › capstone-saas.spec.ts:50:7 › 3. Native Modal Dialog Lifecycle & Focus Trap (240ms)
✓ [chromium] › capstone-saas.spec.ts:67:7 › 4. Onboarding Wizard Constraint Validation (310ms)
4 passed (1.8s)🏋️ Hands-On Exercise
🎯 The Challenge: Production Multi-Stage Dockerfile & Lighthouse Budget
Write a production-grade multi-stage Dockerfile and a .lighthouserc.json configuration file that guarantees high-performance static asset serving and asserts strict Lighthouse performance budgets.
Instructions:
- Create a
Dockerfilethat:- Uses
node:20-alpineas the build stage to build static HTML/CSS/JS assets. - Copies static output to
nginx:1.25-alpine. - Runs as a non-privileged user and exposes port 80.
- Uses
- Create
.lighthouserc.jsonasserting:categories:performance:0.95categories:accessibility:1.0categories:best-practices:1.0
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Testing Accessibility Only Manually: Manual audits catch only a fraction of regressions over time. Automated axe-core tests in CI prevent accidental ARIA breakage across continuous feature deployments.
- Flaky E2E Tests from Arbitrary Timeouts: Avoid
await page.waitForTimeout(3000). Always wait for deterministic DOM assertions likeawait expect(locator).toBeVisible(). - Deploying Nginx without Gzip/Brotli: Forgetting static compression increases initial HTML/CSS payload transfer sizes by up to 80%, degrading First Contentful Paint (FCP) scores.
💡 Pro Tips
- Visual Regression Testing with Playwright: Use
await expect(page).toHaveScreenshot('dashboard-dark.png')to capture pixel-perfect visual diffs across Chromium, Firefox, and WebKit rendering engines. - GitHub Actions Matrix Builds: Run your Playwright test matrix concurrently across multiple OS environments (Ubuntu, macOS, Windows) to guarantee cross-platform user experience parity.
📌 Key Takeaways
- Automated end-to-end testing with Playwright verifies real user keyboard workflows, modal focus traps, and form state transitions.
@axe-core/playwrightenables continuous, zero-tolerance accessibility regression testing in CI/CD pipelines.- Lighthouse CI enforces quantitative performance, accessibility, and SEO budgets before deployment.
- Production Nginx containers must inject essential security headers (
X-Frame-Options,X-Content-Type-Options,HSTS,CSP). - Multi-stage Docker builds produce minimal, hardened container images suitable for enterprise multi-tenant cloud architectures.
- --