From 7ec987c78d62d126b972efb6e5bf423ba3265d41 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sun, 21 Dec 2025 23:00:54 +0100 Subject: [PATCH] Phase 6: E2E Test - Update scripts/e2e.sh to start worker alongside backend - Create frontend/e2e/random-jobs.spec.ts with 3 tests: - Counter increment creates random job outcome visible to admin - Admin can view empty random jobs list - Regular user cannot access random jobs page --- frontend/e2e/random-jobs.spec.ts | 84 ++++++++++++++++++++++++++++++++ scripts/e2e.sh | 9 +++- 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 frontend/e2e/random-jobs.spec.ts diff --git a/frontend/e2e/random-jobs.spec.ts b/frontend/e2e/random-jobs.spec.ts new file mode 100644 index 0000000..fe06d5f --- /dev/null +++ b/frontend/e2e/random-jobs.spec.ts @@ -0,0 +1,84 @@ +import { test, expect } from "@playwright/test"; +import { clearAuth, loginUser, REGULAR_USER, ADMIN_USER } from "./helpers/auth"; + +test.describe("Random Jobs - E2E", () => { + test("counter increment creates random job outcome visible to admin", async ({ page }) => { + // Step 1: Login as regular user + await clearAuth(page); + await loginUser(page, REGULAR_USER.email, REGULAR_USER.password); + + // Wait for counter page to load + await expect(page.locator("h1")).toBeVisible(); + await expect(page.locator("h1")).not.toHaveText("..."); + + // Step 2: Click increment button + await page.click("text=Increment"); + + // Wait for the counter to update (value changes) + const counterValue = await page.locator("h1").textContent(); + expect(counterValue).toMatch(/^\d+$/); + + // Give the worker a moment to process the job + await page.waitForTimeout(1000); + + // Step 3: Logout + await page.click("text=Sign out"); + await expect(page).toHaveURL("/login"); + + // Step 4: Login as admin + await loginUser(page, ADMIN_USER.email, ADMIN_USER.password); + + // Admin should be on audit page by default + await expect(page).toHaveURL("/audit"); + + // Step 5: Navigate to Random Jobs page + await page.click('a[href="/admin/random-jobs"]'); + await expect(page).toHaveURL("/admin/random-jobs"); + + // Step 6: Verify the table contains at least one row + // Wait for table to load and have data + await expect(page.locator("table tbody tr")).toHaveCount(1, { timeout: 10000 }); + + // Verify the row has the regular user's email + const tableContent = await page.locator("table tbody").textContent(); + expect(tableContent).toContain(REGULAR_USER.email); + + // Verify the outcome has expected fields + // Value should be a number 0-100 + const valueCell = page.locator("table tbody tr td").nth(3); + const valueText = await valueCell.textContent(); + const value = Number(valueText); + expect(value).toBeGreaterThanOrEqual(0); + expect(value).toBeLessThanOrEqual(100); + + // Status should be "completed" + await expect(page.locator("table tbody")).toContainText("completed"); + }); + + test("admin can view empty random jobs list", async ({ page }) => { + // This test just verifies the page loads correctly + // In a fresh DB there might be no outcomes yet + await clearAuth(page); + await loginUser(page, ADMIN_USER.email, ADMIN_USER.password); + + await page.goto("/admin/random-jobs"); + await expect(page).toHaveURL("/admin/random-jobs"); + + // Page title should be visible + await expect(page.locator("h2")).toContainText("Random Number Job Outcomes"); + + // Table should exist + await expect(page.locator("table")).toBeVisible(); + }); + + test("regular user cannot access random jobs page", async ({ page }) => { + await clearAuth(page); + await loginUser(page, REGULAR_USER.email, REGULAR_USER.password); + + // Try to navigate directly to the admin page + await page.goto("/admin/random-jobs"); + + // Should be redirected away (to "/" since fallbackRedirect is "/") + await expect(page).toHaveURL("/"); + }); +}); diff --git a/scripts/e2e.sh b/scripts/e2e.sh index 66ee55f..32cdbf1 100755 --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -23,7 +23,11 @@ cd .. # Start backend (SECRET_KEY should be set via .envrc or environment) cd backend uv run uvicorn main:app --port 8000 --log-level warning & -PID=$! +BACKEND_PID=$! + +# Start worker for job processing +uv run python worker.py & +WORKER_PID=$! cd .. # Wait for backend @@ -46,7 +50,8 @@ fi EXIT_CODE=$? # Cleanup -kill $PID 2>/dev/null || true +kill $BACKEND_PID 2>/dev/null || true +kill $WORKER_PID 2>/dev/null || true exit $EXIT_CODE