Skip to content

Commit 0327d9e

Browse files
committed
tests
1 parent 9eed74c commit 0327d9e

File tree

6 files changed

+144
-0
lines changed

6 files changed

+144
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react'
2+
3+
export default function RootLayout({ children }: LayoutProps<'/'>) {
4+
return (
5+
<html>
6+
<head></head>
7+
<body>{children}</body>
8+
</html>
9+
)
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Link from 'next/link'
2+
3+
export default function Page() {
4+
return (
5+
<main>
6+
<h1>Home</h1>
7+
<Link href="/with-loading" prefetch={false}>
8+
Go to /with-loading (no prefetch)
9+
</Link>
10+
</main>
11+
)
12+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Loading() {
2+
return <div id="loading-component">Loading...</div>
3+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { connection } from 'next/server'
2+
import { setTimeout } from 'timers/promises'
3+
4+
export default async function Page() {
5+
await connection()
6+
await setTimeout(1000)
7+
return (
8+
<main>
9+
<h1>Delayed page</h1>
10+
<p id="page-component">This is a dynamic page with a loading boundary.</p>
11+
</main>
12+
)
13+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** @type {import('next').NextConfig} */
2+
module.exports = {
3+
experimental: {
4+
clientSegmentCache: true,
5+
clientParamParsing: true,
6+
},
7+
productionBrowserSourceMaps: true,
8+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
import { retry } from 'next-test-utils'
3+
import { Playwright as NextBrowser } from '../../../../lib/next-webdriver'
4+
import * as Playwright from 'playwright'
5+
6+
describe('navigating without a prefetch', () => {
7+
const { next } = nextTestSetup({
8+
files: __dirname,
9+
})
10+
11+
it('can show a loading boundary from the dynamic response', async () => {
12+
const browser = await next.browser('/')
13+
const navigationTracker = await trackMpaNavs(browser)
14+
15+
// Navigate to a dynamic page with a `loading.tsx` without a prefetch.
16+
await browser.elementByCss('a[href="/with-loading"]').click()
17+
18+
// The loading state should stream in and be displayed.
19+
await retry(async () => {
20+
expect(
21+
await browser
22+
.elementByCss('#loading-component', { state: 'visible' })
23+
.text()
24+
).toContain('Loading...')
25+
})
26+
27+
// Later, the page should be displayed as well.
28+
await retry(async () => {
29+
expect(
30+
await browser
31+
.elementByCss('#page-component', { state: 'visible' })
32+
.text()
33+
).toContain('This is a dynamic page with a loading boundary.')
34+
})
35+
36+
// As a sanity check, we shouldn't MPA-nav.
37+
expect(await navigationTracker.didMpaNavigate()).toBe(false)
38+
})
39+
40+
it('performs an MPA navigation when an RSC request fails', async () => {
41+
let page: Playwright.Page
42+
const browser = await next.browser('/', {
43+
beforePageLoad(_page) {
44+
page = _page
45+
},
46+
})
47+
const navigationTracker = await trackMpaNavs(browser)
48+
49+
// Block the RSC request for the page that will be performed when we navigate to it.
50+
await page.route('**/with-loading**', async (route, request) => {
51+
if (request.headers()['rsc'] === '1') {
52+
await route.fulfill({
53+
status: 500,
54+
body: 'Oops, the server exploded a bit',
55+
contentType: 'text/plain',
56+
})
57+
} else {
58+
// We don't want to block HTML requests (i.e. an MPA navigation)
59+
await route.continue()
60+
}
61+
})
62+
63+
// Navigate to a dynamic page with a `loading.tsx` without a prefetch.
64+
await browser.elementByCss('a[href="/with-loading"]').click()
65+
66+
// The loading state should stream in and be displayed.
67+
await retry(async () => {
68+
expect(
69+
await browser
70+
.elementByCss('#loading-component', { state: 'visible' })
71+
.text()
72+
).toContain('Loading...')
73+
})
74+
75+
// Later, the page should be displayed as well.
76+
await retry(async () => {
77+
expect(
78+
await browser
79+
.elementByCss('#page-component', { state: 'visible' })
80+
.text()
81+
).toContain('This is a dynamic page with a loading boundary.')
82+
})
83+
84+
// We made the RSC request fail, which should trigger a MPA navigation.
85+
expect(await navigationTracker.didMpaNavigate()).toBe(true)
86+
})
87+
})
88+
89+
async function trackMpaNavs(session: NextBrowser) {
90+
const id = Date.now()
91+
await session.eval(`window.__MPA_NAV_ID = ${id}`)
92+
return {
93+
async didMpaNavigate() {
94+
const maybeId = await session.eval(`window.__MPA_NAV_ID`)
95+
return id !== maybeId
96+
},
97+
}
98+
}

0 commit comments

Comments
 (0)