arbret/frontend/e2e/counter.spec.ts
2025-12-18 22:08:31 +01:00

148 lines
5.3 KiB
TypeScript

import { test, expect, Page } from "@playwright/test";
// Helper to generate unique email for each test
function uniqueEmail(): string {
return `counter-${Date.now()}-${Math.random().toString(36).substring(7)}@example.com`;
}
// Helper to authenticate a user
async function authenticate(page: Page): Promise<string> {
const email = uniqueEmail();
await page.evaluate(() => localStorage.clear());
await page.goto("/signup");
await page.fill('input[type="email"]', email);
await page.fill('input[type="password"]', "password123");
await page.locator('input[type="password"]').nth(1).fill("password123");
await page.click('button[type="submit"]');
await expect(page).toHaveURL("/");
return email;
}
test.describe("Counter - Authenticated", () => {
test("displays counter value", async ({ page }) => {
await authenticate(page);
await expect(page.locator("h1")).toBeVisible();
// Counter should be a number (not loading state)
const text = await page.locator("h1").textContent();
expect(text).toMatch(/^\d+$/);
});
test("displays current count label", async ({ page }) => {
await authenticate(page);
await expect(page.getByText("Current Count")).toBeVisible();
});
test("clicking increment button increases counter", async ({ page }) => {
await authenticate(page);
await expect(page.locator("h1")).not.toHaveText("...");
const before = await page.locator("h1").textContent();
await page.click("text=Increment");
await expect(page.locator("h1")).toHaveText(String(Number(before) + 1));
});
test("clicking increment multiple times", async ({ page }) => {
await authenticate(page);
await expect(page.locator("h1")).not.toHaveText("...");
const before = Number(await page.locator("h1").textContent());
await page.click("text=Increment");
await page.click("text=Increment");
await page.click("text=Increment");
await expect(page.locator("h1")).toHaveText(String(before + 3));
});
test("counter persists after page reload", async ({ page }) => {
await authenticate(page);
await expect(page.locator("h1")).not.toHaveText("...");
const before = await page.locator("h1").textContent();
await page.click("text=Increment");
const expected = String(Number(before) + 1);
await expect(page.locator("h1")).toHaveText(expected);
await page.reload();
await expect(page.locator("h1")).toHaveText(expected);
});
test("counter is shared between users", async ({ page, browser }) => {
// First user increments
await authenticate(page);
await expect(page.locator("h1")).not.toHaveText("...");
const initialValue = Number(await page.locator("h1").textContent());
await page.click("text=Increment");
await page.click("text=Increment");
const afterFirst = initialValue + 2;
await expect(page.locator("h1")).toHaveText(String(afterFirst));
// Second user in new context sees the same value
const page2 = await browser.newPage();
await authenticate(page2);
await expect(page2.locator("h1")).toHaveText(String(afterFirst));
// Second user increments
await page2.click("text=Increment");
await expect(page2.locator("h1")).toHaveText(String(afterFirst + 1));
// First user reloads and sees the increment
await page.reload();
await expect(page.locator("h1")).toHaveText(String(afterFirst + 1));
await page2.close();
});
});
test.describe("Counter - Unauthenticated", () => {
test("redirects to login when accessing counter without auth", async ({ page }) => {
await page.evaluate(() => localStorage.clear());
await page.goto("/");
await expect(page).toHaveURL("/login");
});
test("shows login form when redirected", async ({ page }) => {
await page.evaluate(() => localStorage.clear());
await page.goto("/");
await expect(page.locator("h1")).toHaveText("Welcome back");
});
});
test.describe("Counter - Session Integration", () => {
test("can access counter after login", async ({ page }) => {
const email = uniqueEmail();
// Sign up first
await page.goto("/signup");
await page.fill('input[type="email"]', email);
await page.fill('input[type="password"]', "password123");
await page.locator('input[type="password"]').nth(1).fill("password123");
await page.click('button[type="submit"]');
await expect(page).toHaveURL("/");
// Logout
await page.click("text=Sign out");
await expect(page).toHaveURL("/login");
// Login again
await page.fill('input[type="email"]', email);
await page.fill('input[type="password"]', "password123");
await page.click('button[type="submit"]');
await expect(page).toHaveURL("/");
// Counter should be visible
await expect(page.locator("h1")).toBeVisible();
const text = await page.locator("h1").textContent();
expect(text).toMatch(/^\d+$/);
});
test("counter API requires authentication", async ({ page }) => {
// Try to access counter API directly without auth
const response = await page.request.get("http://localhost:8000/api/counter");
expect(response.status()).toBe(403);
});
test("counter increment API requires authentication", async ({ page }) => {
const response = await page.request.post("http://localhost:8000/api/counter/increment");
expect(response.status()).toBe(403);
});
});