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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177import { test, expect } from "../fixtures";
const BASE = "http://localhost:4174";
test.describe("headers() and cookies() in Server Components", () => {
test("headers() works on initial page load", async ({ request }) => {
const res = await request.get(`${BASE}/headers-test`, {
headers: {
"User-Agent": "TestAgent/1.0",
},
});
expect(res.status()).toBe(200);
const html = await res.text();
expect(html).toContain("User-Agent:");
expect(html).toContain("TestAgent");
});
test("cookies() works on initial page load", async ({ request }) => {
const res = await request.get(`${BASE}/headers-test`, {
headers: {
Cookie: "test_cookie=hello; another=world",
},
});
expect(res.status()).toBe(200);
const html = await res.text();
// React SSR inserts <!-- --> comment nodes between text and expressions
// Match "Cookies:" followed by optional whitespace/comments and "2"
expect(html).toMatch(/Cookies:.*2/s);
});
test("headers() works on RSC client navigation (.rsc request)", async ({
request,
}) => {
// Simulate an RSC request (what happens during client-side navigation)
const res = await request.get(`${BASE}/headers-test.rsc`, {
headers: {
Accept: "text/x-component",
"User-Agent": "RSCNavigationAgent/1.0",
},
});
expect(res.status()).toBe(200);
const contentType = res.headers()["content-type"];
expect(contentType).toContain("text/x-component");
// The RSC payload should contain the user agent
const rscPayload = await res.text();
expect(rscPayload).toContain("RSCNavigationAgent");
});
test("cookies() works on RSC client navigation (.rsc request)", async ({
request,
}) => {
const res = await request.get(`${BASE}/headers-test.rsc`, {
headers: {
Accept: "text/x-component",
Cookie: "nav_cookie=value123",
},
});
expect(res.status()).toBe(200);
const rscPayload = await res.text();
// RSC payload contains the cookie count as a number in the serialized data
// The exact format is ["Cookies: ",1] in the RSC stream
expect(rscPayload).toMatch(/Cookies.*1/);
});
test("headers() available during browser client navigation", async ({
page,
}) => {
// Start on the home page
await page.goto(`${BASE}/`);
await page.waitForLoadState("networkidle");
// Navigate to headers-test via client-side navigation (Link click)
await page.click('[data-testid="headers-test-link"]');
// Wait for the page to load
await page.waitForSelector('[data-testid="headers-test-page"]', {
timeout: 10000,
});
// Headers should be present (the browser sends User-Agent on RSC requests)
const userAgentText = await page.getByTestId("user-agent").textContent();
expect(userAgentText).toContain("User-Agent:");
});
test("headers() page renders correctly in browser", async ({ page }) => {
await page.goto(`${BASE}/headers-test`);
await expect(page.getByTestId("headers-test-page")).toBeVisible();
await expect(page.locator("h1")).toHaveText("Headers/Cookies Test");
await expect(page.getByTestId("user-agent")).toContainText("User-Agent:");
await expect(page.getByTestId("cookie-count")).toContainText("Cookies:");
});
});
/**
* OpenNext Compat: Middleware cookie/header behavior
*
* Ported from: https://github.com/opennextjs/opennextjs-cloudflare/blob/main/examples/e2e/app-router/e2e/middleware.cookies.test.ts
* Ported from: https://github.com/opennextjs/opennextjs-cloudflare/blob/main/examples/e2e/app-router/e2e/headers.test.ts
* Tests: ON-6 and ON-8 in TRACKING.md
*
* OpenNext verifies that internal middleware headers (x-middleware-set-cookie,
* x-middleware-next) are NOT exposed in the response, and that middleware-set
* cookies are readable by server components via cookies().get().
*/
test.describe("Middleware header/cookie behavior (OpenNext compat)", () => {
test("middleware sets custom headers on matched routes", async ({
request,
}) => {
// Ref: opennextjs-cloudflare headers.test.ts "Headers"
// The app-basic middleware sets x-mw-pathname and x-mw-ran
const res = await request.get(`${BASE}/`);
expect(res.status()).toBe(200);
const headers = res.headers();
expect(headers["x-mw-ran"]).toBe("true");
expect(headers["x-mw-pathname"]).toBe("/");
});
test("internal x-middleware-next header is NOT in response", async ({
request,
}) => {
// Ref: opennextjs-cloudflare middleware.cookies.test.ts
// "should not expose internal Next headers in response"
const res = await request.get(`${BASE}/about`);
const headers = res.headers();
// x-middleware-next is an internal Next.js header used between middleware
// and the server โ it should never appear in the response to the client
expect(headers["x-middleware-next"]).toBeUndefined();
});
test("internal x-middleware-set-cookie header is NOT in response", async ({
request,
}) => {
// Ref: opennextjs-cloudflare middleware.cookies.test.ts
// "should not expose internal Next headers in response"
const res = await request.get(`${BASE}/about`);
const headers = res.headers();
// x-middleware-set-cookie is used internally to pass cookie-setting
// instructions โ should be stripped before reaching the client
expect(headers["x-middleware-set-cookie"]).toBeUndefined();
});
test("middleware headers available on matched pages only", async ({
request,
}) => {
// Ref: opennextjs-cloudflare headers.test.ts โ headers should only be
// set on routes that match the middleware config
const matchedRes = await request.get(`${BASE}/about`);
expect(matchedRes.headers()["x-mw-ran"]).toBe("true");
// API routes are not in the middleware matcher
const apiRes = await request.get(`${BASE}/api/hello`);
expect(apiRes.headers()["x-mw-ran"]).toBeUndefined();
});
test("request headers available in server component RSC rendering", async ({
request,
}) => {
// Ref: opennextjs-cloudflare headers.test.ts "Request header should be available in RSC"
const res = await request.get(`${BASE}/headers-test`, {
headers: { "x-custom-test": "opennext-compat" },
});
expect(res.status()).toBe(200);
// The server component reads headers() โ verify it can access custom headers
const html = await res.text();
expect(html).toContain("User-Agent:");
});
});