171 lines
6.7 KiB
TypeScript
171 lines
6.7 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.context().clearCookies();
|
|
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());
|
|
|
|
// Click increment and wait for each update to complete
|
|
await page.click("text=Increment");
|
|
await expect(page.locator("h1")).not.toHaveText(String(before));
|
|
|
|
const afterFirst = Number(await page.locator("h1").textContent());
|
|
await page.click("text=Increment");
|
|
await expect(page.locator("h1")).not.toHaveText(String(afterFirst));
|
|
|
|
const afterSecond = Number(await page.locator("h1").textContent());
|
|
await page.click("text=Increment");
|
|
await expect(page.locator("h1")).not.toHaveText(String(afterSecond));
|
|
|
|
// Final value should be at least 3 more than we started with
|
|
const final = Number(await page.locator("h1").textContent());
|
|
expect(final).toBeGreaterThanOrEqual(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");
|
|
// Wait for the counter to update (value should increase by 2 from what this user started with)
|
|
await expect(page.locator("h1")).not.toHaveText(String(initialValue));
|
|
const afterFirstUser = Number(await page.locator("h1").textContent());
|
|
expect(afterFirstUser).toBeGreaterThan(initialValue);
|
|
|
|
// Second user in new context sees the current value
|
|
const page2 = await browser.newPage();
|
|
await authenticate(page2);
|
|
await expect(page2.locator("h1")).not.toHaveText("...");
|
|
const page2InitialValue = Number(await page2.locator("h1").textContent());
|
|
// The value should be at least what user 1 saw (might be higher due to parallel tests)
|
|
expect(page2InitialValue).toBeGreaterThanOrEqual(afterFirstUser);
|
|
|
|
// Second user increments
|
|
await page2.click("text=Increment");
|
|
// Wait for counter to update - use >= because parallel tests may also increment
|
|
await expect(page2.locator("h1")).not.toHaveText(String(page2InitialValue));
|
|
const page2AfterIncrement = Number(await page2.locator("h1").textContent());
|
|
expect(page2AfterIncrement).toBeGreaterThanOrEqual(page2InitialValue + 1);
|
|
|
|
// First user reloads and sees the increment (value should be >= what page2 has)
|
|
await page.reload();
|
|
await expect(page.locator("h1")).not.toHaveText("...");
|
|
const page1Reloaded = Number(await page.locator("h1").textContent());
|
|
expect(page1Reloaded).toBeGreaterThanOrEqual(page2InitialValue + 1);
|
|
|
|
await page2.close();
|
|
});
|
|
});
|
|
|
|
test.describe("Counter - Unauthenticated", () => {
|
|
test("redirects to login when accessing counter without auth", async ({ page }) => {
|
|
await page.context().clearCookies();
|
|
await page.goto("/");
|
|
await expect(page).toHaveURL("/login");
|
|
});
|
|
|
|
test("shows login form when redirected", async ({ page }) => {
|
|
await page.context().clearCookies();
|
|
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 - wait for it to load (not showing "...")
|
|
await expect(page.locator("h1")).toBeVisible();
|
|
await expect(page.locator("h1")).not.toHaveText("...");
|
|
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(401);
|
|
});
|
|
|
|
test("counter increment API requires authentication", async ({ page }) => {
|
|
const response = await page.request.post("http://localhost:8000/api/counter/increment");
|
|
expect(response.status()).toBe(401);
|
|
});
|
|
});
|