Skip to content

Commit 7a8d2e7

Browse files
authored
E2E: Add a new app to test experimental features (#832)
* add experimental e2e app * add e2e test * lint * add node middleware * lint * bump to latest canary * review fix and package update
1 parent de53c4d commit 7a8d2e7

22 files changed

+913
-2
lines changed

.github/workflows/e2e.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ jobs:
111111
- name: Install Nextjs in app-pages-router
112112
working-directory: examples/app-pages-router
113113
run: pnpm add next@${{ needs.check_next_version.outputs.previousNextVersion }}
114+
# We do not install the latest canary of Next in the experimental app.
114115

115116
- name: Get Playwright version
116117
id: playwright-version
@@ -157,6 +158,8 @@ jobs:
157158
echo "PAGES_ROUTER_URL=$PAGES_ROUTER_URL" >> $GITHUB_ENV
158159
APP_PAGES_ROUTER_URL=$(jq -r '.["e2e-example-AppPagesRouter"].url' .sst/outputs.json)
159160
echo "APP_PAGES_ROUTER_URL=$APP_PAGES_ROUTER_URL" >> $GITHUB_ENV
161+
EXPERIMENTAL_APP_URL=$(jq -r '.["e2e-example-Experimental"].url' .sst/outputs.json)
162+
echo "EXPERIMENTAL_APP_URL=$EXPERIMENTAL_APP_URL" >> $GITHUB_ENV
160163
161164
- name: Run e2e Test
162165
run: npm run e2e:test

examples/experimental/.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.*
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
13+
# testing
14+
/coverage
15+
16+
# next.js
17+
/.next/
18+
/out/
19+
20+
# production
21+
/build
22+
23+
# misc
24+
.DS_Store
25+
*.pem
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
.pnpm-debug.log*
32+
33+
# env files (can opt-in for committing if needed)
34+
.env*
35+
36+
# vercel
37+
.vercel
38+
39+
# typescript
40+
*.tsbuildinfo
41+
next-env.d.ts

examples/experimental/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Experimental
2+
3+
This project is meant to test experimental features that are only available on canary builds of Next.js.

examples/experimental/next.config.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { NextConfig } from "next";
2+
3+
const nextConfig: NextConfig = {
4+
/* config options here */
5+
cleanDistDir: true,
6+
output: "standalone",
7+
eslint: {
8+
ignoreDuringBuilds: true,
9+
},
10+
experimental: {
11+
ppr: "incremental",
12+
nodeMiddleware: true,
13+
},
14+
};
15+
16+
export default nextConfig;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const config = {
2+
default: {
3+
override: {
4+
wrapper: "aws-lambda-streaming",
5+
queue: "sqs-lite",
6+
incrementalCache: "s3-lite",
7+
tagCache: "dynamodb-lite",
8+
},
9+
},
10+
functions: {},
11+
buildCommand: "npx turbo build",
12+
};
13+
14+
export default config;

examples/experimental/package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "experimental",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"openbuild": "node ../../packages/open-next/dist/index.js build",
7+
"dev": "next dev --turbopack --port 3004",
8+
"build": "next build",
9+
"start": "next start --port 3004",
10+
"lint": "next lint",
11+
"clean": "rm -rf .turbo node_modules .next .open-next"
12+
},
13+
"dependencies": {
14+
"next": "15.4.0-canary.14",
15+
"react": "catalog:",
16+
"react-dom": "catalog:"
17+
},
18+
"devDependencies": {
19+
"@types/node": "catalog:",
20+
"@types/react": "catalog:",
21+
"@types/react-dom": "catalog:",
22+
"typescript": "catalog:"
23+
}
24+
}
25.3 KB
Binary file not shown.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
:root {
2+
--background: #ffffff;
3+
--foreground: #171717;
4+
}
5+
6+
@media (prefers-color-scheme: dark) {
7+
:root {
8+
--background: #0a0a0a;
9+
--foreground: #ededed;
10+
}
11+
}
12+
13+
html,
14+
body {
15+
max-width: 100vw;
16+
overflow-x: hidden;
17+
}
18+
19+
body {
20+
color: var(--foreground);
21+
background: var(--background);
22+
font-family: Arial, Helvetica, sans-serif;
23+
-webkit-font-smoothing: antialiased;
24+
-moz-osx-font-smoothing: grayscale;
25+
}
26+
27+
* {
28+
box-sizing: border-box;
29+
padding: 0;
30+
margin: 0;
31+
}
32+
33+
a {
34+
color: inherit;
35+
text-decoration: none;
36+
}
37+
38+
@media (prefers-color-scheme: dark) {
39+
html {
40+
color-scheme: dark;
41+
}
42+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { Metadata } from "next";
2+
import { Geist, Geist_Mono } from "next/font/google";
3+
import "./globals.css";
4+
5+
const geistSans = Geist({
6+
variable: "--font-geist-sans",
7+
subsets: ["latin"],
8+
});
9+
10+
const geistMono = Geist_Mono({
11+
variable: "--font-geist-mono",
12+
subsets: ["latin"],
13+
});
14+
15+
export const metadata: Metadata = {
16+
title: "Create Next App",
17+
description: "Generated by create next app",
18+
};
19+
20+
export default function RootLayout({
21+
children,
22+
}: Readonly<{
23+
children: React.ReactNode;
24+
}>) {
25+
return (
26+
<html lang="en">
27+
<body className={`${geistSans.variable} ${geistMono.variable}`}>
28+
{children}
29+
</body>
30+
</html>
31+
);
32+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
.page {
2+
--gray-rgb: 0, 0, 0;
3+
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
4+
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
5+
6+
--button-primary-hover: #383838;
7+
--button-secondary-hover: #f2f2f2;
8+
9+
display: grid;
10+
grid-template-rows: 20px 1fr 20px;
11+
align-items: center;
12+
justify-items: center;
13+
min-height: 100svh;
14+
padding: 80px;
15+
gap: 64px;
16+
font-family: var(--font-geist-sans);
17+
}
18+
19+
@media (prefers-color-scheme: dark) {
20+
.page {
21+
--gray-rgb: 255, 255, 255;
22+
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
23+
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
24+
25+
--button-primary-hover: #ccc;
26+
--button-secondary-hover: #1a1a1a;
27+
}
28+
}
29+
30+
.main {
31+
display: flex;
32+
flex-direction: column;
33+
gap: 32px;
34+
grid-row-start: 2;
35+
}
36+
37+
.main ol {
38+
font-family: var(--font-geist-mono);
39+
padding-left: 0;
40+
margin: 0;
41+
font-size: 14px;
42+
line-height: 24px;
43+
letter-spacing: -0.01em;
44+
list-style-position: inside;
45+
}
46+
47+
.main li:not(:last-of-type) {
48+
margin-bottom: 8px;
49+
}
50+
51+
.main code {
52+
font-family: inherit;
53+
background: var(--gray-alpha-100);
54+
padding: 2px 4px;
55+
border-radius: 4px;
56+
font-weight: 600;
57+
}
58+
59+
.ctas {
60+
display: flex;
61+
gap: 16px;
62+
}
63+
64+
.ctas a {
65+
appearance: none;
66+
border-radius: 128px;
67+
height: 48px;
68+
padding: 0 20px;
69+
border: none;
70+
border: 1px solid transparent;
71+
transition: background 0.2s, color 0.2s, border-color 0.2s;
72+
cursor: pointer;
73+
display: flex;
74+
align-items: center;
75+
justify-content: center;
76+
font-size: 16px;
77+
line-height: 20px;
78+
font-weight: 500;
79+
}
80+
81+
a.primary {
82+
background: var(--foreground);
83+
color: var(--background);
84+
gap: 8px;
85+
}
86+
87+
a.secondary {
88+
border-color: var(--gray-alpha-200);
89+
min-width: 158px;
90+
}
91+
92+
.footer {
93+
grid-row-start: 3;
94+
display: flex;
95+
gap: 24px;
96+
}
97+
98+
.footer a {
99+
display: flex;
100+
align-items: center;
101+
gap: 8px;
102+
}
103+
104+
.footer img {
105+
flex-shrink: 0;
106+
}
107+
108+
/* Enable hover only on non-touch devices */
109+
@media (hover: hover) and (pointer: fine) {
110+
a.primary:hover {
111+
background: var(--button-primary-hover);
112+
border-color: transparent;
113+
}
114+
115+
a.secondary:hover {
116+
background: var(--button-secondary-hover);
117+
border-color: transparent;
118+
}
119+
120+
.footer a:hover {
121+
text-decoration: underline;
122+
text-underline-offset: 4px;
123+
}
124+
}
125+
126+
@media (max-width: 600px) {
127+
.page {
128+
padding: 32px;
129+
padding-bottom: 80px;
130+
}
131+
132+
.main {
133+
align-items: center;
134+
}
135+
136+
.main ol {
137+
text-align: center;
138+
}
139+
140+
.ctas {
141+
flex-direction: column;
142+
}
143+
144+
.ctas a {
145+
font-size: 14px;
146+
height: 40px;
147+
padding: 0 16px;
148+
}
149+
150+
a.secondary {
151+
min-width: auto;
152+
}
153+
154+
.footer {
155+
flex-wrap: wrap;
156+
align-items: center;
157+
justify-content: center;
158+
}
159+
}
160+
161+
@media (prefers-color-scheme: dark) {
162+
.logo {
163+
filter: invert();
164+
}
165+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Link from "next/link";
2+
import styles from "./page.module.css";
3+
4+
export default function Home() {
5+
return (
6+
<div className={styles.page}>
7+
<main className={styles.main}>
8+
<Link href="/ppr">
9+
<h1 className={styles.title}>Incremental PPR</h1>
10+
</Link>
11+
</main>
12+
</div>
13+
);
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { DynamicComponent } from "@/components/dynamic";
2+
import { StaticComponent } from "@/components/static";
3+
import { Suspense } from "react";
4+
5+
export const experimental_ppr = true;
6+
7+
export default function PPRPage() {
8+
return (
9+
<div>
10+
<StaticComponent />
11+
<Suspense fallback={<div>Loading...</div>}>
12+
<DynamicComponent />
13+
</Suspense>
14+
</div>
15+
);
16+
}

0 commit comments

Comments
 (0)