Phase 0.3: Update E2E tests for cleanup

- Delete counter.spec.ts and random-jobs.spec.ts
- Rewrite permissions.spec.ts for new permission structure
- Update scripts/e2e.sh: remove worker.py execution
- Update generated api.ts types
This commit is contained in:
counterweight 2025-12-22 18:13:24 +01:00
parent a5c1eccb4b
commit c89e0312fa
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
5 changed files with 72 additions and 816 deletions

View file

@ -4,8 +4,8 @@ import { test, expect, Page } from "@playwright/test";
* Permission-based E2E tests
*
* These tests verify that:
* 1. Regular users can only access Counter and Sum pages
* 2. Admin users can only access the Audit page
* 1. Regular users can access booking and appointments pages
* 2. Admin users can access admin pages (invites, availability, appointments)
* 3. Users are properly redirected based on their permissions
* 4. API calls respect permission boundaries
*/
@ -64,87 +64,44 @@ test.describe("Regular User Access", () => {
await loginUser(page, REGULAR_USER.email, REGULAR_USER.password);
});
test("can access counter page", async ({ page }) => {
test("redirected from home to booking page", async ({ page }) => {
await page.goto("/");
// Should stay on counter page
await expect(page).toHaveURL("/");
// Should see counter UI
await expect(page.getByText("Current Count")).toBeVisible();
await expect(page.getByRole("button", { name: /increment/i })).toBeVisible();
// Should be redirected to booking page
await expect(page).toHaveURL("/booking");
});
test("can access sum page", async ({ page }) => {
await page.goto("/sum");
test("can access booking page", async ({ page }) => {
await page.goto("/booking");
// Should stay on sum page
await expect(page).toHaveURL("/sum");
// Should stay on booking page
await expect(page).toHaveURL("/booking");
// Should see sum UI
await expect(page.getByText("Sum Calculator")).toBeVisible();
// Should see booking UI
await expect(page.getByText("Book an Appointment")).toBeVisible();
});
test("cannot access audit page - redirected to counter", async ({ page }) => {
await page.goto("/audit");
test("can access appointments page", async ({ page }) => {
await page.goto("/appointments");
// Should be redirected to counter page (home)
await expect(page).toHaveURL("/");
// Should stay on appointments page
await expect(page).toHaveURL("/appointments");
// Should see appointments UI
await expect(page.getByText("My Appointments")).toBeVisible();
});
test("navigation only shows Counter and Sum", async ({ page }) => {
await page.goto("/");
test("navigation shows booking and appointments", async ({ page }) => {
await page.goto("/appointments");
// Should see Counter and Sum in nav
await expect(page.getByText("Counter")).toBeVisible();
await expect(page.getByText("Sum")).toBeVisible();
// From appointments page, we can see the nav links
// "Appointments" is the current page (shown as span, not link)
// "Book" should be a link - use first() since there may be other booking links on page
await expect(page.locator('a[href="/booking"]').first()).toBeVisible();
// Should NOT see Audit in nav (for regular users)
const auditLinks = page.locator('a[href="/audit"]');
await expect(auditLinks).toHaveCount(0);
});
test("can navigate between Counter and Sum", async ({ page }) => {
await page.goto("/");
// Go to Sum
await page.click('a[href="/sum"]');
await expect(page).toHaveURL("/sum");
// Go back to Counter
await page.click('a[href="/"]');
await expect(page).toHaveURL("/");
});
test("can use counter functionality", async ({ page }) => {
await page.goto("/");
// Get initial count (might be any number)
const countElement = page.locator("h1").first();
await expect(countElement).toBeVisible();
// Click increment
await page.click('button:has-text("Increment")');
// Wait for update
await page.waitForTimeout(500);
// Counter should have updated (we just verify no error occurred)
await expect(countElement).toBeVisible();
});
test("can use sum functionality", async ({ page }) => {
await page.goto("/sum");
// Fill in numbers
await page.fill('input[aria-label="First number"]', "5");
await page.fill('input[aria-label="Second number"]', "3");
// Calculate
await page.click('button:has-text("Calculate")');
// Should show result
await expect(page.getByText("8")).toBeVisible();
// Should NOT see admin links
const availabilityLinks = page.locator('a[href="/admin/availability"]');
await expect(availabilityLinks).toHaveCount(0);
});
});
@ -154,53 +111,44 @@ test.describe("Admin User Access", () => {
await loginUser(page, ADMIN_USER.email, ADMIN_USER.password);
});
test("redirected from counter page to audit", async ({ page }) => {
test("redirected from home to admin appointments", async ({ page }) => {
await page.goto("/");
// Should be redirected to audit page
await expect(page).toHaveURL("/audit");
// Should be redirected to admin appointments page
await expect(page).toHaveURL("/admin/appointments");
});
test("redirected from sum page to audit", async ({ page }) => {
await page.goto("/sum");
test("can access admin appointments page", async ({ page }) => {
await page.goto("/admin/appointments");
// Should be redirected to audit page
await expect(page).toHaveURL("/audit");
// Should stay on admin appointments page
await expect(page).toHaveURL("/admin/appointments");
// Should see appointments UI (use heading for specificity)
await expect(page.getByRole("heading", { name: "All Appointments" })).toBeVisible();
});
test("can access audit page", async ({ page }) => {
await page.goto("/audit");
test("can access admin availability page", async ({ page }) => {
await page.goto("/admin/availability");
// Should stay on audit page
await expect(page).toHaveURL("/audit");
// Should stay on availability page
await expect(page).toHaveURL("/admin/availability");
// Should see audit tables
await expect(page.getByText("Counter Activity")).toBeVisible();
await expect(page.getByText("Sum Activity")).toBeVisible();
// Should see availability UI (use heading for specificity)
await expect(page.getByRole("heading", { name: "Availability" })).toBeVisible();
});
test("navigation only shows Audit", async ({ page }) => {
await page.goto("/audit");
test("navigation shows admin links", async ({ page }) => {
await page.goto("/admin/appointments");
// Should see Audit as current
await expect(page.getByText("Audit")).toBeVisible();
// Should see admin nav items (use locator for nav links)
await expect(page.locator('a[href="/admin/invites"]')).toBeVisible();
await expect(page.locator('a[href="/admin/availability"]')).toBeVisible();
await expect(page.locator('a[href="/admin/appointments"]')).toHaveCount(0); // Current page, shown as text not link
// Should NOT see Counter or Sum links (for admin users)
const counterLinks = page.locator('a[href="/"]');
const sumLinks = page.locator('a[href="/sum"]');
await expect(counterLinks).toHaveCount(0);
await expect(sumLinks).toHaveCount(0);
});
test("audit page shows records", async ({ page }) => {
await page.goto("/audit");
// Should see the tables
await expect(page.getByRole("table")).toHaveCount(2);
// Should see column headers (use first() since there are two tables with same headers)
await expect(page.getByRole("columnheader", { name: "User" }).first()).toBeVisible();
await expect(page.getByRole("columnheader", { name: "Date" }).first()).toBeVisible();
// Should NOT see regular user links
const bookLinks = page.locator('a[href="/booking"]');
await expect(bookLinks).toHaveCount(0);
});
});
@ -209,24 +157,24 @@ test.describe("Unauthenticated Access", () => {
await clearAuth(page);
});
test("counter page redirects to login", async ({ page }) => {
test("home page redirects to login", async ({ page }) => {
await page.goto("/");
await expect(page).toHaveURL("/login");
});
test("sum page redirects to login", async ({ page }) => {
await page.goto("/sum");
test("booking page redirects to login", async ({ page }) => {
await page.goto("/booking");
await expect(page).toHaveURL("/login");
});
test("audit page redirects to login", async ({ page }) => {
await page.goto("/audit");
test("admin page redirects to login", async ({ page }) => {
await page.goto("/admin/appointments");
await expect(page).toHaveURL("/login");
});
});
test.describe("Permission Boundary via API", () => {
test("regular user API call to audit returns 403", async ({ page, request }) => {
test("regular user API call to admin appointments returns 403", async ({ page, request }) => {
// Login as regular user
await clearAuth(page);
await loginUser(page, REGULAR_USER.email, REGULAR_USER.password);
@ -236,8 +184,8 @@ test.describe("Permission Boundary via API", () => {
const authCookie = cookies.find((c) => c.name === "auth_token");
if (authCookie) {
// Try to call audit API directly
const response = await request.get(`${API_URL}/api/audit/counter`, {
// Try to call admin appointments API directly
const response = await request.get(`${API_URL}/api/admin/appointments`, {
headers: {
Cookie: `auth_token=${authCookie.value}`,
},
@ -247,7 +195,7 @@ test.describe("Permission Boundary via API", () => {
}
});
test("admin user API call to counter returns 403", async ({ page, request }) => {
test("admin user API call to booking slots returns 403", async ({ page, request }) => {
// Login as admin
await clearAuth(page);
await loginUser(page, ADMIN_USER.email, ADMIN_USER.password);
@ -257,8 +205,12 @@ test.describe("Permission Boundary via API", () => {
const authCookie = cookies.find((c) => c.name === "auth_token");
if (authCookie) {
// Try to call counter API directly
const response = await request.get(`${API_URL}/api/counter`, {
// Try to call booking slots API directly (requires regular user permission)
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const dateStr = tomorrow.toISOString().split("T")[0];
const response = await request.get(`${API_URL}/api/booking/slots?date=${dateStr}`, {
headers: {
Cookie: `auth_token=${authCookie.value}`,
},
@ -274,14 +226,14 @@ test.describe("Session and Logout", () => {
// Login
await clearAuth(page);
await loginUser(page, REGULAR_USER.email, REGULAR_USER.password);
await expect(page).toHaveURL("/");
await expect(page).toHaveURL("/booking");
// Logout
await page.click("text=Sign out");
await expect(page).toHaveURL("/login");
// Try to access counter
await page.goto("/");
// Try to access booking
await page.goto("/booking");
await expect(page).toHaveURL("/login");
});
@ -297,7 +249,7 @@ test.describe("Session and Logout", () => {
]);
// Try to access protected page
await page.goto("/");
await page.goto("/booking");
// Should be redirected to login
await expect(page).toHaveURL("/login");