Skip to content

Commit d7ee6b4

Browse files
committed
Add test to assert on current build output
1 parent aae5319 commit d7ee6b4

File tree

18 files changed

+236
-0
lines changed

18 files changed

+236
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
import path from 'path'
3+
4+
describe('cli-build-output', () => {
5+
describe('with mixed static and dynamic pages and app router routes', () => {
6+
const { next } = nextTestSetup({
7+
files: path.join(__dirname, 'fixtures/mixed'),
8+
skipStart: true,
9+
})
10+
11+
beforeAll(() => next.build())
12+
13+
it('should show info about prerendered and dynamic routes in a tree view', async () => {
14+
// TODO: Show cache info (revalidate/expire) for app router, and use the
15+
// same for pages router instead of the ISR addendum.
16+
17+
// TODO: Fix double-listing of the /ppr/[slug] fallback.
18+
19+
expect(getTreeView(next.cliOutput)).toMatchInlineSnapshot(`
20+
"Route (app) Size First Load JS
21+
┌ ○ /_not-found ····· ······
22+
├ ƒ /api ····· ······
23+
├ ○ /api/force-static ····· ······
24+
├ ○ /app-static ····· ······
25+
├ ○ /cache-life ····· ······
26+
├ ƒ /dynamic ····· ······
27+
├ ◐ /ppr/[slug] ····· ······
28+
├ ├ /ppr/[slug]
29+
├ ├ /ppr/[slug]
30+
├ ├ /ppr/days
31+
├ └ /ppr/weeks
32+
└ ○ /revalidate ····· ······
33+
+ First Load JS shared by all ······
34+
├ chunks/main-app-················.js ······
35+
└ other shared chunks (total) ·······
36+
37+
Route (pages) Size First Load JS
38+
┌ ƒ /api/hello ··· ·······
39+
├ ● /gsp-revalidate (ISR: 300 Seconds) ····· ·······
40+
├ ƒ /gssp ····· ·······
41+
└ ○ /static ····· ·······
42+
+ First Load JS shared by all ·······
43+
├ chunks/framework-················.js ·······
44+
├ chunks/main-················.js ·······
45+
└ other shared chunks (total) ·······
46+
47+
○ (Static) prerendered as static content
48+
● (SSG) prerendered as static HTML (uses generateStaticParams)
49+
(ISR) incremental static regeneration (uses revalidate in generateStaticParams)
50+
◐ (Partial Prerender) prerendered as static HTML with dynamic server-streamed content
51+
ƒ (Dynamic) server-rendered on demand"
52+
`)
53+
})
54+
})
55+
56+
describe('with only a few static routes', () => {
57+
const { next } = nextTestSetup({
58+
files: path.join(__dirname, 'fixtures/minimal-static'),
59+
skipStart: true,
60+
})
61+
62+
beforeAll(() => next.build())
63+
64+
it('should show info about prerendered routes in a compact tree view', async () => {
65+
expect(getTreeView(next.cliOutput)).toMatchInlineSnapshot(`
66+
"Route (app) Size First Load JS
67+
┌ ○ / ····· ······
68+
└ ○ /_not-found ····· ······
69+
+ First Load JS shared by all ······
70+
├ chunks/main-app-················.js ······
71+
└ other shared chunks (total) ·······
72+
73+
Route (pages) Size First Load JS
74+
─ ○ /static ····· ·······
75+
+ First Load JS shared by all ·······
76+
├ chunks/framework-················.js ·······
77+
├ chunks/main-················.js ·······
78+
└ other shared chunks (total) ·······
79+
80+
○ (Static) prerendered as static content"
81+
`)
82+
})
83+
})
84+
})
85+
86+
function getTreeView(cliOutput: string): string {
87+
let foundBuildTracesLine = false
88+
const lines: string[] = []
89+
90+
for (const line of cliOutput.split('\n')) {
91+
if (foundBuildTracesLine) {
92+
lines.push(normalizeCliOutputLine(line))
93+
}
94+
95+
foundBuildTracesLine ||= line.includes('Collecting build traces')
96+
}
97+
98+
while (lines.at(0) === '') lines.shift()
99+
while (lines.at(-1) === '') lines.pop()
100+
101+
return lines.join('\n')
102+
}
103+
104+
function normalizeCliOutputLine(line: string): string {
105+
// Replace file sizes with a placeholder.
106+
line = line.replace(
107+
/\b\d+(?:\.\d+)?\s*(B|kB|MB|GB|TB|PB|EB|ZB|YB)\b/g,
108+
(match) => '·'.repeat(match.length)
109+
)
110+
111+
// Replace file hashes with a placeholder.
112+
line = line.replace(
113+
/-([a-f0-9]{8,})(\.js)/g,
114+
(match, p1, p2) => '-' + '·'.repeat(p1.length) + p2
115+
)
116+
117+
return line
118+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ReactNode } from 'react'
2+
export default function Root({ children }: { children: ReactNode }) {
3+
return (
4+
<html>
5+
<body>{children}</body>
6+
</html>
7+
)
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <p>hello world</p>
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* @type {import('next').NextConfig}
3+
*/
4+
const nextConfig = {}
5+
6+
module.exports = nextConfig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <p>hello world</p>
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const dynamic = 'force-static'
2+
3+
export async function GET() {
4+
return Response.json({ message: 'hello world' })
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export async function GET() {
2+
return Response.json({ message: 'hello world' })
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <p>hello world</p>
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use cache'
2+
3+
import { unstable_cacheLife } from 'next/cache'
4+
5+
export default async function Page() {
6+
unstable_cacheLife('weeks')
7+
8+
return <p>hello world</p>
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { headers } from 'next/headers'
2+
3+
export default async function Page() {
4+
await headers()
5+
6+
return <p>hello world</p>
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ReactNode } from 'react'
2+
export default function Root({ children }: { children: ReactNode }) {
3+
return (
4+
<html>
5+
<body>{children}</body>
6+
</html>
7+
)
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { unstable_cacheLife } from 'next/cache'
2+
3+
type CacheLife = Parameters<typeof unstable_cacheLife>[0]
4+
5+
async function getCachedValue(cacheLife: CacheLife) {
6+
'use cache'
7+
8+
unstable_cacheLife(cacheLife)
9+
10+
return Math.random()
11+
}
12+
13+
export default async function Page({
14+
params,
15+
}: {
16+
params: Promise<{ slug: CacheLife }>
17+
}) {
18+
const { slug } = await params
19+
20+
return <p>hello world {await getCachedValue(slug)}</p>
21+
}
22+
23+
export function generateStaticParams() {
24+
return [{ slug: 'days' }, { slug: 'weeks' }]
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const revalidate = 900
2+
3+
export default function Page() {
4+
return <p>hello world</p>
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* @type {import('next').NextConfig}
3+
*/
4+
const nextConfig = {
5+
experimental: {
6+
useCache: true,
7+
ppr: true,
8+
},
9+
}
10+
11+
module.exports = nextConfig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { NextApiRequest, NextApiResponse } from 'next'
2+
3+
export default function handler(req: NextApiRequest, res: NextApiResponse) {
4+
res.status(200).json({ message: 'hello world' })
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Page() {
2+
return <p>hello world</p>
3+
}
4+
5+
export async function getStaticProps() {
6+
return { props: {}, revalidate: 300 }
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Page() {
2+
return <p>hello world</p>
3+
}
4+
5+
export async function getServerSideProps() {
6+
return { props: {} }
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <p>hello world</p>
3+
}

0 commit comments

Comments
 (0)