# E2E Smoke Tests Playwright-based smoke tests that validate the Polaris plugin against a live Headlamp deployment. ## CI E2E tests run automatically in GitHub Actions on pushes to `main` and pull requests. The workflow (`.github/workflows/e2e.yaml`): 1. Builds the plugin (`npm run build`) 2. Creates a ConfigMap from the built `dist/` output 3. Deploys a stock Headlamp instance via Helm with the plugin mounted as a ConfigMap volume 4. Generates a ServiceAccount token for test auth 5. Runs Playwright tests against the E2E instance 6. Tears down the E2E instance This approach uses the stock `ghcr.io/headlamp-k8s/headlamp` image with no custom Docker builds. The plugin is loaded via `HEADLAMP_PLUGINS_DIR` volume mount. ### Required GitHub Secrets Configure these in GitHub repository settings (Settings → Secrets and variables → Actions): | Secret | Required | Description | | -------------------- | -------- | -------------------------------------------------------------- | | `AUTHENTIK_USERNAME` | OIDC | Authentik email or username for a CI user with Headlamp access | | `AUTHENTIK_PASSWORD` | OIDC | Password for that user | Token-based auth is auto-generated by the deploy script. OIDC secrets are only needed if testing against the shared Headlamp instance. No `GHCR_TOKEN` or Docker registry secrets are needed — the stock Headlamp image is public. ## Running Locally ### Option 1: OIDC via Authentik (same as CI) ```bash AUTHENTIK_USERNAME=you@example.com AUTHENTIK_PASSWORD=... npm run e2e ``` The default base URL is `https://headlamp.animaniacs.farh.net`. Override with `HEADLAMP_URL` if needed. ### Option 2: K8s bearer token (port-forward) ```bash kubectl port-forward -n kube-system svc/headlamp 4466:80 export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system) HEADLAMP_URL=http://localhost:4466 npm run e2e ``` Or in headed mode (opens a browser window): ```bash HEADLAMP_URL=http://localhost:4466 npm run e2e:headed ``` ## Environment Variables | Variable | Required | Default | Description | | -------------------- | -------- | -------------------------------------- | --------------------------------------- | | `HEADLAMP_URL` | No | `https://headlamp.animaniacs.farh.net` | Base URL of the Headlamp instance | | `AUTHENTIK_USERNAME` | OIDC | — | Authentik email/username | | `AUTHENTIK_PASSWORD` | OIDC | — | Authentik password | | `HEADLAMP_TOKEN` | Token | — | Kubernetes bearer token (auto-generated in CI) | In CI, `HEADLAMP_URL` and `HEADLAMP_TOKEN` are set automatically by the deploy script. For local runs, set either OIDC credentials or a token manually. ## What the Tests Validate - **Sidebar entry** — The Polaris sidebar item appears after login - **Overview page** — Cluster score and check distribution render correctly - **Namespaces page** — Table of namespaces loads with clickable links - **Namespace detail** — Clicking a namespace shows its score and resource table These are smoke tests against real cluster data. They verify the plugin loads and renders without errors, not specific data values. ## Test Coverage ### Current Tests (`polaris.spec.ts`) 1. **`sidebar contains Polaris entry`** - Verifies Polaris appears in the navigation sidebar - Ensures plugin successfully registered sidebar entry 2. **`overview page renders cluster score`** - Navigates to `/c/main/polaris` - Checks for "Polaris — Overview" heading - Verifies cluster score percentage is displayed - Validates data fetching and rendering 3. **`namespaces page renders table with namespace buttons`** - Navigates to `/c/main/polaris/namespaces` - Checks for "Polaris — Namespaces" heading - Verifies table is visible with at least one row - Ensures namespace buttons are clickable 4. **`namespace detail drawer opens from table button`** - Clicks first namespace button in table - Verifies drawer opens with namespace name in heading - Checks "Namespace Score" section is visible - Confirms "Resources" table is displayed - Validates URL hash is updated with namespace name 5. **`namespace detail drawer closes with Escape key`** - Opens namespace drawer - Presses Escape key - Verifies drawer closes - Checks URL hash is cleared 6. **`namespace detail drawer opens from URL hash`** - Navigates directly to `/c/main/polaris/namespaces#` - Verifies drawer automatically opens - Checks namespace details are displayed ## Prerequisites ### Cluster Requirements 1. **Polaris Deployment** ```bash # Verify Polaris is running kubectl -n polaris get pods kubectl -n polaris get svc polaris-dashboard ``` 2. **Polaris Audit Data** ```bash # Check if Polaris has generated audit results kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json | jq '.AuditTime' ``` 3. **RBAC Permissions** - Headlamp service account (or test user) needs `get` on `services/proxy` for `polaris-dashboard` - See main README for RBAC setup ### Local Setup ```bash # 1. Install dependencies npm install npx playwright install chromium # 2. Create .env file (optional, for persistent config) cp .env.example .env # 3. Set environment variables export HEADLAMP_URL=https://your-headlamp-instance.com export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system) # 4. Run tests npm run e2e ``` ## Debugging ### Run in Headed Mode See the browser UI while tests run: ```bash npm run e2e:headed ``` ### Enable Debug Mode Step through tests with Playwright Inspector: ```bash npx playwright test --debug ``` ### Generate Trace Record full trace for failed tests: ```bash npx playwright test --trace on npx playwright show-trace test-results//trace.zip ``` ### Screenshot on Failure Tests automatically capture screenshots on failure in `test-results/` ### Common Issues **Auth fails with "Sign In button not found":** - Check HEADLAMP_URL is correct - Verify Headlamp is accessible - Ensure OIDC is configured if using Authentik **Polaris sidebar entry not found:** - Plugin may not be installed: Check Settings → Plugins in Headlamp - Plugin may have failed to load: Check browser console - Clear browser cache and hard refresh **Cluster score not displayed:** - Polaris may not have audit data yet - Check Polaris is running: `kubectl -n polaris get pods` - Verify service proxy: `kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json` **Namespace table empty:** - Polaris hasn't run audit yet (wait a few minutes) - Check Polaris logs: `kubectl -n polaris logs -l app.kubernetes.io/name=polaris` ## Writing New Tests ### Example: Testing Plugin Settings ```typescript test('plugin settings page shows Polaris configuration', async ({ page }) => { await page.goto('/c/main/settings/plugins'); // Find and click Polaris plugin await page.getByText('headlamp-polaris-plugin').click(); // Check settings are visible await expect(page.getByText('Polaris Settings')).toBeVisible(); await expect(page.getByText('Refresh Interval')).toBeVisible(); await expect(page.getByText('Dashboard URL')).toBeVisible(); }); ``` ### Example: Testing App Bar Badge ```typescript test('app bar displays Polaris score badge', async ({ page }) => { await page.goto('/c/main'); // Badge should be visible in app bar const badge = page.getByRole('button', { name: /Polaris: \d+%/ }); await expect(badge).toBeVisible(); // Clicking should navigate to overview await badge.click(); await expect(page).toHaveURL(/\/c\/main\/polaris$/); }); ``` ### Example: Testing Dark Mode ```typescript test('plugin UI adapts to dark mode', async ({ page }) => { await page.goto('/c/main/polaris'); // Toggle dark mode await page.getByRole('button', { name: /theme/i }).click(); // Check background color changes const body = page.locator('body'); await expect(body).toHaveCSS('background-color', 'rgb(18, 18, 18)'); // Plugin components should adapt const sectionBox = page.locator('[class*="MuiPaper"]').first(); await expect(sectionBox).not.toHaveCSS('background-color', 'rgb(255, 255, 255)'); }); ``` ## CI/CD Integration Tests run automatically in GitHub Actions on pushes to `main` and pull requests. See `.github/workflows/e2e.yaml` for workflow configuration. ### Architecture The E2E workflow deploys a **dedicated Headlamp instance** for each test run: 1. Build plugin (`npm run build`) 2. Create ConfigMap from `dist/` output (`scripts/deploy-e2e-headlamp.sh`) 3. Deploy stock Headlamp via Helm with ConfigMap volume mount 4. Run Playwright tests against the E2E instance 5. Tear down (`scripts/teardown-e2e-headlamp.sh`) No custom Docker images, no PVCs, no kubectl exec/cp, no patching of existing deployments. The plugin is mounted from a ConfigMap into the stock Headlamp image. ### Cluster Prerequisites One-time setup by a cluster admin: ```bash kubectl apply -f deployment/e2e-ci-runner-rbac.yaml ``` ### Manual Trigger You can manually trigger E2E tests from GitHub Actions: 1. Go to Actions → E2E Tests 2. Click "Run workflow" 3. Select branch and run ## Best Practices 1. **Use semantic selectors**: `getByRole`, `getByText` over CSS selectors 2. **Wait for visibility**: Use `await expect(...).toBeVisible()` instead of `waitForTimeout` 3. **Keep tests independent**: Each test should work in isolation 4. **Test user flows**: Complete journeys, not just page loads 5. **Clean up state**: Close drawers/modals after tests 6. **Use storage state**: Reuse auth across tests (already configured) 7. **Parallelize carefully**: Currently disabled due to shared state ## Resources - [Playwright Documentation](https://playwright.dev/) - [Playwright Best Practices](https://playwright.dev/docs/best-practices) - [Headlamp Plugin Development](https://headlamp.dev/docs/latest/development/plugins/) - [Project Main README](../README.md)