Skip to content

Commit bdf74de

Browse files
authored
Sync e2e tests with aws (#476)
1 parent 2650043 commit bdf74de

File tree

12 files changed

+410
-101
lines changed

12 files changed

+410
-101
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export async function GET(request: Request, { params }: { params: Promise<{ slug: string }> }) {
2+
const { slug } = await params;
3+
return Response.json({ slug });
4+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { NextRequest } from "next/server";
2+
3+
export function GET(request: NextRequest) {
4+
const searchParams = request.nextUrl.searchParams;
5+
const query = searchParams.get("query");
6+
if (query === "OpenNext is awesome!") {
7+
return Response.json({ query });
8+
}
9+
return new Response("Internal Server Error", { status: 500 });
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { redirect } from "next/navigation";
2+
3+
export async function GET(request: Request) {
4+
redirect("https://nextjs.org/");
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const revalidate = 5;
2+
3+
async function getTime() {
4+
return new Date().toISOString();
5+
}
6+
7+
export async function GET() {
8+
const time = await getTime();
9+
return Response.json({ time });
10+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const dynamic = "force-static";
2+
3+
async function getTime() {
4+
return new Date().toISOString();
5+
}
6+
7+
export async function GET() {
8+
const time = await getTime();
9+
return Response.json({ time });
10+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { cookies } from "next/headers";
2+
3+
export async function POST(request: Request) {
4+
const formData = await request.formData();
5+
const username = formData.get("username");
6+
const password = formData.get("password");
7+
if (username === "hakuna" && password === "matata") {
8+
(await cookies()).set("auth_session", "SUPER_SECRET_SESSION_ID_1234");
9+
return Response.json(
10+
{
11+
message: "ok",
12+
},
13+
{
14+
status: 202,
15+
}
16+
);
17+
}
18+
return Response.json({ message: "you must login" }, { status: 401 });
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export async function POST(request: Request) {
2+
const formData = await request.formData();
3+
const name = formData.get("name");
4+
const email = formData.get("email");
5+
if (name === "OpenNext [] () %&#!%$#" && email === "[email protected]") {
6+
return Response.json(
7+
{
8+
message: "ok",
9+
},
10+
{
11+
status: 202,
12+
}
13+
);
14+
}
15+
return Response.json({ message: "forbidden" }, { status: 403 });
16+
}

Diff for: examples/e2e/app-router/app/methods/route.ts

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import type { NextRequest } from "next/server";
2+
3+
export async function GET() {
4+
return Response.json({
5+
message: "OpenNext is awesome! :) :] :> :D",
6+
});
7+
}
8+
9+
export async function POST(request: Request) {
10+
const text = await request.text();
11+
if (text === "OpenNext is awesome! :] :) :> :D") {
12+
return Response.json(
13+
{
14+
message: "ok",
15+
},
16+
{
17+
status: 202,
18+
}
19+
);
20+
}
21+
return Response.json({ message: "forbidden" }, { status: 403 });
22+
}
23+
24+
export async function PUT(request: Request) {
25+
const res = (await request.json()) as {
26+
message: string;
27+
};
28+
if (res.message === "OpenNext PUT") {
29+
return Response.json({ message: "ok" }, { status: 201 });
30+
}
31+
return Response.json({ message: "error" }, { status: 500 });
32+
}
33+
34+
export async function PATCH(request: Request) {
35+
const res = (await request.json()) as {
36+
message: string;
37+
};
38+
if (res.message === "OpenNext PATCH") {
39+
return Response.json(
40+
{ message: "ok", modified: true, timestamp: new Date().toISOString() },
41+
{ status: 202 }
42+
);
43+
}
44+
return Response.json({ message: "error" }, { status: 500 });
45+
}
46+
47+
export async function DELETE(request: NextRequest) {
48+
const searchParams = request.nextUrl.searchParams;
49+
const command = searchParams.get("command");
50+
if (command === "rm -rf / --no-preserve-root") {
51+
return new Response(null, { status: 204 });
52+
}
53+
return Response.json({ message: "error" }, { status: 500 });
54+
}
55+
56+
export async function HEAD() {
57+
return new Response("hello", {
58+
status: 200,
59+
headers: {
60+
"content-type": "text/html; charset=utf-8",
61+
// Once deployed to AWS this will always be 0
62+
// "content-length": "1234567",
63+
"special-header": "OpenNext is the best :) :] :> :D",
64+
},
65+
});
66+
}
67+
68+
export async function OPTIONS() {
69+
return new Response(null, {
70+
status: 204,
71+
headers: {
72+
Allow: "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, LOVE",
73+
Special: "OpenNext is the best :) :] :> :D",
74+
},
75+
});
76+
}

Diff for: examples/e2e/app-router/e2e/methods.test.ts

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import { expect, test } from "@playwright/test";
2+
3+
test.describe("all supported methods should work in route handlers", () => {
4+
test("GET", async ({ request }) => {
5+
const getRes = await request.get("/methods");
6+
const getData = await getRes.json();
7+
expect(getRes.status()).toEqual(200);
8+
expect(getData.message).toEqual("OpenNext is awesome! :) :] :> :D");
9+
});
10+
11+
test("POST", async ({ request }) => {
12+
const postRes = await request.post("/methods", {
13+
headers: {
14+
"Content-Type": "text/plain",
15+
},
16+
data: "OpenNext is awesome! :] :) :> :D",
17+
});
18+
expect(postRes.status()).toBe(202);
19+
const postData = await postRes.json();
20+
expect(postData.message).toBe("ok");
21+
const errorPostRes = await request.post("/methods", {
22+
headers: {
23+
"Content-Type": "text/plain",
24+
},
25+
data: "OpenNext is not awesome! :C",
26+
});
27+
expect(errorPostRes.status()).toBe(403);
28+
const errorData = await errorPostRes.json();
29+
expect(errorData.message).toBe("forbidden");
30+
});
31+
32+
test("PUT", async ({ request }) => {
33+
const putRes = await request.put("/methods", {
34+
data: {
35+
message: "OpenNext PUT",
36+
},
37+
});
38+
expect(putRes.status()).toEqual(201);
39+
const putData = await putRes.json();
40+
expect(putData.message).toEqual("ok");
41+
});
42+
43+
test("PATCH", async ({ request }) => {
44+
const timestampBefore = new Date();
45+
const patchRes = await request.patch("/methods", {
46+
data: { message: "OpenNext PATCH" },
47+
});
48+
expect(patchRes.status()).toEqual(202);
49+
const patchData = await patchRes.json();
50+
expect(patchData.message).toEqual("ok");
51+
expect(patchData.modified).toEqual(true);
52+
expect(Date.parse(patchData.timestamp)).toBeGreaterThan(timestampBefore.getTime());
53+
});
54+
55+
test("DELETE", async ({ request }) => {
56+
const deleteRes = await request.delete("/methods", {
57+
params: {
58+
command: "rm -rf / --no-preserve-root",
59+
},
60+
});
61+
expect(deleteRes.status()).toEqual(204);
62+
});
63+
64+
test("HEAD", async ({ request }) => {
65+
const headRes = await request.head("/methods");
66+
expect(headRes.status()).toEqual(200);
67+
const headers = headRes.headers();
68+
expect(headers["content-type"]).toEqual("text/html; charset=utf-8");
69+
// expect(headers["content-length"]).toEqual("1234567");
70+
expect(headers["special-header"]).toEqual("OpenNext is the best :) :] :> :D");
71+
});
72+
73+
test("OPTIONS", async ({ request }) => {
74+
const optionsRes = await request.fetch("/methods", {
75+
method: "OPTIONS",
76+
});
77+
expect(optionsRes.status()).toEqual(204);
78+
const headers = optionsRes.headers();
79+
expect(headers.allow).toBe("GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, LOVE");
80+
expect(headers.special).toBe("OpenNext is the best :) :] :> :D");
81+
});
82+
});
83+
84+
test("formData should work in POST route handler", async ({ request }) => {
85+
const formData = new FormData();
86+
formData.append("name", "OpenNext [] () %&#!%$#");
87+
formData.append("email", "[email protected]");
88+
const postRes = await request.post("/methods/post/formdata", {
89+
form: formData,
90+
});
91+
expect(postRes.status()).toBe(202);
92+
const postData = await postRes.json();
93+
expect(postData.message).toBe("ok");
94+
});
95+
96+
test("revalidate should work in GET route handler", async ({ request, page }) => {
97+
let time = Date.parse((await request.get("/methods/get/revalidate").then((res) => res.json())).time);
98+
let newTime: number;
99+
let tempTime = time;
100+
do {
101+
await page.waitForTimeout(1000);
102+
time = tempTime;
103+
const newTimeRes = await request.get("/methods/get/revalidate");
104+
newTime = Date.parse((await newTimeRes.json()).time);
105+
tempTime = newTime;
106+
} while (time !== newTime);
107+
const midTime = Date.parse((await request.get("/methods/get/revalidate").then((res) => res.json())).time);
108+
109+
await page.waitForTimeout(1000);
110+
// Expect that the time is still stale
111+
expect(midTime).toEqual(newTime);
112+
113+
// Wait 5 + 1 seconds for ISR to regenerate time
114+
await page.waitForTimeout(6000);
115+
let finalTime = newTime;
116+
do {
117+
await page.waitForTimeout(2000);
118+
finalTime = Date.parse((await request.get("/methods/get/revalidate").then((res) => res.json())).time);
119+
} while (newTime === finalTime);
120+
121+
expect(newTime).not.toEqual(finalTime);
122+
});
123+
124+
test("should cache a static GET route", async ({ request }) => {
125+
const res = await request.get("/methods/get/static");
126+
expect(res.headers()["cache-control"]).toContain("s-maxage=31536000");
127+
});
128+
129+
test("should be able to set cookies in route handler", async ({ request }) => {
130+
const postRes = await request.post("/methods/post/cookies", {
131+
form: {
132+
username: "hakuna",
133+
password: "matata",
134+
},
135+
});
136+
expect(postRes.status()).toBe(202);
137+
const postData = await postRes.json();
138+
expect(postData.message).toBe("ok");
139+
const cookies = postRes.headers()["set-cookie"];
140+
expect(cookies).toContain("auth_session=SUPER_SECRET_SESSION_ID_1234");
141+
});
142+
143+
test("should be able to redirect in route handler", async ({ request }) => {
144+
const redirectRes = await request.get("/methods/get/redirect", {
145+
// Disable auto-redirect to check initial response
146+
maxRedirects: 0,
147+
});
148+
expect(redirectRes.status()).toBe(307);
149+
expect(redirectRes.headers().location).toBe("https://nextjs.org/");
150+
151+
// Check if the redirect works
152+
const followedRes = await request.get("/methods/get/redirect");
153+
expect(followedRes.url()).toBe("https://nextjs.org/");
154+
});
155+
156+
test("dynamic segments should work in route handlers", async ({ request }) => {
157+
const res = await request.get("/methods/get/dynamic-segments/this-is-a-slug");
158+
const data = await res.json();
159+
expect(data.slug).toBe("this-is-a-slug");
160+
});
161+
162+
test("query parameters should work in route handlers", async ({ request }) => {
163+
const res = await request.get("/methods/get/query", {
164+
params: {
165+
query: "OpenNext is awesome!",
166+
},
167+
});
168+
const data = await res.json();
169+
expect(data.query).toBe("OpenNext is awesome!");
170+
});

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"devDependencies": {
66
"@changesets/changelog-github": "^0.5.0",
77
"@changesets/cli": "^2.27.9",
8-
"@playwright/test": "1.47.0",
8+
"@playwright/test": "catalog:",
99
"pkg-pr-new": "^0.0.29",
1010
"prettier": "3.3.3"
1111
},

0 commit comments

Comments
 (0)