1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90import { test, expect } from "@playwright/test";
const BASE = "http://localhost:4174";
test.describe("App Router SSR", () => {
test("home page renders SSR HTML without JavaScript", async ({ page }) => {
// Block all JS to verify SSR-only content
await page.route("**/*.js", (route) => route.abort());
await page.route("**/*.mjs", (route) => route.abort());
await page.goto(`${BASE}/`);
await expect(page.locator("h1")).toHaveText("Welcome to App Router");
await expect(page.locator("p")).toContainText(
"This is the home page rendered as a Server Component.",
);
});
test("root layout wraps the page with html/head/body", async ({ page }) => {
await page.goto(`${BASE}/`);
// Root layout provides <html lang="en">
const html = page.locator("html");
await expect(html).toHaveAttribute("lang", "en");
// Head should contain charset and title
const title = await page.title();
expect(title).toBe("App Basic");
});
test("about page renders server component content", async ({ page }) => {
await page.goto(`${BASE}/about`);
await expect(page.locator("h1")).toHaveText("About");
await expect(page.locator("p")).toContainText("This is the about page.");
});
test("dynamic route renders with params", async ({ page }) => {
await page.goto(`${BASE}/blog/hello-world`);
await expect(page.locator("h1")).toHaveText("Blog Post");
await expect(page.locator("p")).toContainText("Slug: hello-world");
});
test("nested layout wraps dashboard pages", async ({ page }) => {
await page.goto(`${BASE}/dashboard`);
// Dashboard layout should be present
await expect(page.locator("#dashboard-layout")).toBeVisible();
await expect(page.locator("#dashboard-layout > nav span")).toHaveText(
"Dashboard Nav",
);
await expect(page.locator("h1")).toHaveText("Dashboard");
});
test("nested layout persists for sub-pages", async ({ page }) => {
await page.goto(`${BASE}/dashboard/settings`);
// Same dashboard layout should wrap settings page
await expect(page.locator("#dashboard-layout")).toBeVisible();
await expect(page.locator("#dashboard-layout > nav span")).toHaveText(
"Dashboard Nav",
);
await expect(page.locator("h1")).toHaveText("Settings");
});
test("interactive page SSR includes client component HTML", async ({
page,
}) => {
// Block JS to verify the "use client" Counter still renders HTML via SSR
await page.route("**/*.js", (route) => route.abort());
await page.route("**/*.mjs", (route) => route.abort());
await page.goto(`${BASE}/interactive`);
await expect(page.locator("h1")).toHaveText("Interactive Page");
// Counter should be SSR-rendered with initial count
await expect(page.locator('[data-testid="count"]')).toContainText(
"Count:",
);
});
test("custom 404 page renders for unknown routes", async ({ page }) => {
const response = await page.goto(`${BASE}/nonexistent-page`);
expect(response?.status()).toBe(404);
await expect(page.locator("h1")).toHaveText("404 - Page Not Found");
});
});