From 1d02afddfd3629eb0fc7fe71a8e151f2c3a72a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Thu, 2 Apr 2026 23:25:12 +0200 Subject: [PATCH] ci(e2e): add Playwright smoke tests to deploy-test workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes Epic #37 remaining scope: - playwright.ci.config.ts — targets localhost:3100 (already-running Docker app), testMatch restricted to smoke.spec.ts, HTML report on failure - e2e/smoke.spec.ts — 5 tests: health endpoint, unauth redirect, signin page render, admin login redirect, app shell nav visible - deploy-test.yml — seed admin user via docker exec, setup Node 20, install Playwright 1.49 + Chromium, run smoke tests, upload report artifact on failure Co-Authored-By: claude-flow --- .github/workflows/deploy-test.yml | 28 +++++++++++++++++++++++ apps/web/e2e/smoke.spec.ts | 37 +++++++++++++++++++++++++++++++ apps/web/playwright.ci.config.ts | 25 +++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 apps/web/e2e/smoke.spec.ts create mode 100644 apps/web/playwright.ci.config.ts diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index b77c27b..d28e7f2 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -57,6 +57,34 @@ jobs: echo "$BODY" echo "$BODY" | grep '"status":"ok"' + - name: Seed admin user + run: | + docker compose exec -T app node /app/scripts/setup-admin.mjs \ + --email admin@capakraken.dev \ + --name "Admin" \ + --password admin123 + + - name: Set up Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install Playwright and Chromium + run: | + npm install -g @playwright/test@1.49 + playwright install chromium --with-deps + + - name: Run smoke tests + run: npx playwright test --config apps/web/playwright.ci.config.ts + + - name: Upload Playwright report + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-smoke-report + path: apps/web/playwright-report/ + retention-days: 7 + - name: Show logs on failure if: failure() run: docker compose logs --tail=100 diff --git a/apps/web/e2e/smoke.spec.ts b/apps/web/e2e/smoke.spec.ts new file mode 100644 index 0000000..8d282b0 --- /dev/null +++ b/apps/web/e2e/smoke.spec.ts @@ -0,0 +1,37 @@ +import { expect, test } from "@playwright/test"; + +test("health endpoint returns status ok", async ({ request }) => { + const res = await request.get("/api/health"); + expect(res.status()).toBe(200); + const body = await res.json() as { status: string }; + expect(body.status).toBe("ok"); +}); + +test("unauthenticated root redirects to signin", async ({ page }) => { + await page.goto("/"); + await expect(page).toHaveURL(/\/auth\/signin/); +}); + +test("signin page renders credential inputs and submit button", async ({ page }) => { + await page.goto("/auth/signin"); + await expect(page.locator('input[type="email"]')).toBeVisible(); + await expect(page.locator('input[type="password"]')).toBeVisible(); + await expect(page.locator('button[type="submit"]')).toBeVisible(); +}); + +test("admin login succeeds and redirects away from signin", async ({ page }) => { + await page.goto("/auth/signin"); + await page.fill('input[type="email"]', "admin@capakraken.dev"); + await page.fill('input[type="password"]', "admin123"); + await page.click('button[type="submit"]'); + await expect(page).not.toHaveURL(/\/auth\/signin/, { timeout: 15_000 }); +}); + +test("authenticated user sees app shell nav", async ({ page }) => { + await page.goto("/auth/signin"); + await page.fill('input[type="email"]', "admin@capakraken.dev"); + await page.fill('input[type="password"]', "admin123"); + await page.click('button[type="submit"]'); + await expect(page).not.toHaveURL(/\/auth\/signin/, { timeout: 15_000 }); + await expect(page.locator("nav").first()).toBeVisible({ timeout: 10_000 }); +}); diff --git a/apps/web/playwright.ci.config.ts b/apps/web/playwright.ci.config.ts new file mode 100644 index 0000000..6a4f579 --- /dev/null +++ b/apps/web/playwright.ci.config.ts @@ -0,0 +1,25 @@ +import { defineConfig, devices } from "@playwright/test"; + +export default defineConfig({ + testDir: "./e2e", + testMatch: ["**/smoke.spec.ts"], + fullyParallel: false, + forbidOnly: true, + retries: 1, + workers: 1, + reporter: process.env["CI"] + ? [["list"], ["html", { outputFolder: "playwright-report" }]] + : "list", + use: { + baseURL: "http://localhost:3100", + trace: "on-first-retry", + screenshot: "only-on-failure", + }, + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + ], + // No webServer block — Docker Compose has already started the app +});