import { test, expect } from "@playwright/test"; const BASE = "http://localhost:4176"; test.describe("App Router interactive behavior on Cloudflare Workers", () => { test("counter increments on multiple clicks", async ({ page }) => { await page.goto(`${BASE}/`); await expect(page.locator("h1")).toHaveText("vinext on Cloudflare Workers"); // Wait for hydration by polling counter clicks await expect(async () => { await page.click('[data-testid="increment"]'); const text = await page.locator('[data-testid="count"]').textContent(); expect(text).not.toBe("Count: 0"); }).toPass({ timeout: 10_000 }); // Click more await page.click('[data-testid="increment"]'); await page.click('[data-testid="increment"]'); // Count should be at least 3 (may be higher if polling clicked extra) const text = await page.locator('[data-testid="count"]').textContent(); const count = parseInt(text!.replace("Count: ", ""), 10); expect(count).toBeGreaterThanOrEqual(3); }); test("clicking link to About navigates correctly", async ({ page }) => { await page.goto(`${BASE}/`); await expect(page.locator("h1")).toHaveText("vinext on Cloudflare Workers"); // Click About link await page.click('a[href="/about"]'); await expect(page.locator("h1")).toHaveText("About"); await expect(page.locator("p:first-of-type")).toContainText( "vinext app deployed on Cloudflare Workers", ); expect(page.url()).toBe(`${BASE}/about`); }); test("clicking back link returns to home", async ({ page }) => { await page.goto(`${BASE}/about`); await expect(page.locator("h1")).toHaveText("About"); // Click back link await page.click('a[href="/"]'); await expect(page.locator("h1")).toHaveText("vinext on Cloudflare Workers"); expect(page.url()).toBe(`${BASE}/`); }); test("browser back button works after navigation", async ({ page }) => { await page.goto(`${BASE}/`); await expect(page.locator("h1")).toHaveText("vinext on Cloudflare Workers"); // Navigate to about await page.click('a[href="/about"]'); await expect(page.locator("h1")).toHaveText("About"); // Browser back await page.goBack(); await expect(page.locator("h1")).toHaveText("vinext on Cloudflare Workers"); expect(page.url()).toBe(`${BASE}/`); // Browser forward await page.goForward(); await expect(page.locator("h1")).toHaveText("About"); expect(page.url()).toBe(`${BASE}/about`); }); test("server component timestamp changes on each request", async ({ page }) => { await page.goto(`${BASE}/`); const ts1 = await page.locator('[data-testid="timestamp"]').textContent(); await page.waitForTimeout(100); await page.goto(`${BASE}/`); const ts2 = await page.locator('[data-testid="timestamp"]').textContent(); // Each request should produce a different timestamp (server rendering) expect(ts1).not.toBe(ts2); }); test("counter state resets on full page navigation", async ({ page }) => { await page.goto(`${BASE}/`); await expect(page.locator("h1")).toHaveText("vinext on Cloudflare Workers"); // Wait for hydration and increment counter await expect(async () => { await page.click('[data-testid="increment"]'); const text = await page.locator('[data-testid="count"]').textContent(); expect(text).not.toBe("Count: 0"); }).toPass({ timeout: 10_000 }); // Full page reload await page.goto(`${BASE}/`); // Counter should be back to 0 await expect(page.locator('[data-testid="count"]')).toHaveText("Count: 0"); }); }); test.describe("App Router dynamic routes on Cloudflare Workers", () => { test("renders blog post with dynamic slug", async ({ page }) => { await page.goto(`${BASE}/blog/hello-world`); await expect(page.locator('[data-testid="blog-title"]')).toHaveText("Blog: hello-world"); await expect(page.locator('[data-testid="blog-slug"]')).toHaveText("Slug: hello-world"); }); test("different slugs render different content", async ({ page }) => { await page.goto(`${BASE}/blog/first-post`); await expect(page.locator('[data-testid="blog-title"]')).toHaveText("Blog: first-post"); await page.goto(`${BASE}/blog/second-post`); await expect(page.locator('[data-testid="blog-title"]')).toHaveText("Blog: second-post"); }); test("blog page has back link to home", async ({ page }) => { await page.goto(`${BASE}/blog/test`); await expect(page.locator('[data-testid="blog-title"]')).toHaveText("Blog: test"); await page.click('a[href="/"]'); await expect(page.locator("h1")).toHaveText("vinext on Cloudflare Workers"); }); }); test.describe("App Router API routes on Workers — extended", () => { test("API returns Cloudflare Workers runtime", async ({ request }) => { const response = await request.get(`${BASE}/api/hello`); const body = await response.json(); expect(body.runtime).toContain("Cloudflare-Workers"); }); test("API response is valid JSON with correct headers", async ({ request }) => { const response = await request.get(`${BASE}/api/hello`); expect(response.status()).toBe(200); expect(response.headers()["content-type"]).toContain("application/json"); const body = await response.json(); expect(body.message).toBeTruthy(); }); });