Skip to content

Commit faefe3e

Browse files
committed
test: add case for handling invalid element
1 parent 0169324 commit faefe3e

File tree

8 files changed

+207
-0
lines changed

8 files changed

+207
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use client'
2+
3+
import Foo from '../foo'
4+
5+
export default function BrowserOnly() {
6+
return (
7+
<div>
8+
<Foo />
9+
</div>
10+
)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use client'
2+
3+
import dynamic from 'next/dynamic'
4+
5+
const BrowserOnly = dynamic(() => import('./browser-only'), {
6+
ssr: false,
7+
})
8+
9+
export default function Page() {
10+
return <BrowserOnly />
11+
}

test/development/app-dir/invalid-element-type/app/foo.js

Whitespace-only changes.
Lines changed: 8 additions & 0 deletions
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+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Foo from '../foo'
2+
3+
export default function Page() {
4+
return (
5+
<div>
6+
<Foo />
7+
</div>
8+
)
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use client'
2+
3+
import Foo from '../foo'
4+
5+
export default function Page() {
6+
return (
7+
<div>
8+
<Foo />
9+
</div>
10+
)
11+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
import { assertHasRedbox, getRedboxSource } from 'next-test-utils'
3+
4+
async function getStackFramesContent(browser) {
5+
const stackFrameElements = await browser.elementsByCss(
6+
'[data-nextjs-call-stack-frame]'
7+
)
8+
const stackFramesContent = (
9+
await Promise.all(
10+
stackFrameElements.map(async (frame) => {
11+
const functionNameEl = await frame.$('[data-nextjs-frame-expanded]')
12+
const sourceEl = await frame.$('[data-has-source]')
13+
const functionName = functionNameEl
14+
? await functionNameEl.innerText()
15+
: ''
16+
const source = sourceEl ? await sourceEl.innerText() : ''
17+
18+
if (!functionName) {
19+
return ''
20+
}
21+
return `at ${functionName} (${source})`
22+
})
23+
)
24+
)
25+
.filter(Boolean)
26+
.join('\n')
27+
28+
return stackFramesContent
29+
}
30+
31+
describe('app-dir - invalid-element-type', () => {
32+
const { next } = nextTestSetup({
33+
files: __dirname,
34+
})
35+
36+
it('should catch invalid element from on client-only component', async () => {
37+
const browser = await next.browser('/browser')
38+
39+
await assertHasRedbox(browser)
40+
const source = await getRedboxSource(browser)
41+
42+
const stackFramesContent = await getStackFramesContent(browser)
43+
if (process.env.TURBOPACK) {
44+
expect(stackFramesContent).toMatchInlineSnapshot(
45+
`"at Page (app/browser/page.js (10:10))"`
46+
)
47+
expect(source).toMatchInlineSnapshot(`
48+
"app/browser/browser-only.js (8:7) @ BrowserOnly
49+
50+
6 | return (
51+
7 | <div>
52+
> 8 | <Foo />
53+
| ^
54+
9 | </div>
55+
10 | )
56+
11 | }"
57+
`)
58+
} else {
59+
expect(stackFramesContent).toMatchInlineSnapshot(
60+
`"at BrowserOnly (app/browser/page.js (10:11))"`
61+
)
62+
expect(source).toMatchInlineSnapshot(`
63+
"app/browser/browser-only.js (8:8) @ Foo
64+
65+
6 | return (
66+
7 | <div>
67+
> 8 | <Foo />
68+
| ^
69+
9 | </div>
70+
10 | )
71+
11 | }"
72+
`)
73+
}
74+
})
75+
76+
it('should catch invalid element from on rsc component', async () => {
77+
const browser = await next.browser('/rsc')
78+
79+
await assertHasRedbox(browser)
80+
const stackFramesContent = await getStackFramesContent(browser)
81+
const source = await getRedboxSource(browser)
82+
83+
if (process.env.TURBOPACK) {
84+
expect(stackFramesContent).toMatchInlineSnapshot(`""`)
85+
expect(source).toMatchInlineSnapshot(`
86+
"app/rsc/page.js (6:7) @ Page
87+
88+
4 | return (
89+
5 | <div>
90+
> 6 | <Foo />
91+
| ^
92+
7 | </div>
93+
8 | )
94+
9 | }"
95+
`)
96+
} else {
97+
expect(stackFramesContent).toMatchInlineSnapshot(`""`)
98+
expect(source).toMatchInlineSnapshot(`
99+
"app/rsc/page.js (6:8) @ Foo
100+
101+
4 | return (
102+
5 | <div>
103+
> 6 | <Foo />
104+
| ^
105+
7 | </div>
106+
8 | )
107+
9 | }"
108+
`)
109+
}
110+
})
111+
112+
it('should catch invalid element from on ssr client component', async () => {
113+
const browser = await next.browser('/ssr')
114+
115+
await assertHasRedbox(browser)
116+
117+
const stackFramesContent = await getStackFramesContent(browser)
118+
const source = await getRedboxSource(browser)
119+
if (process.env.TURBOPACK) {
120+
expect(stackFramesContent).toMatchInlineSnapshot(`""`)
121+
expect(source).toMatchInlineSnapshot(`
122+
"app/ssr/page.js (8:7) @ Page
123+
124+
6 | return (
125+
7 | <div>
126+
> 8 | <Foo />
127+
| ^
128+
9 | </div>
129+
10 | )
130+
11 | }"
131+
`)
132+
} else {
133+
expect(stackFramesContent).toMatchInlineSnapshot(`""`)
134+
expect(source).toMatchInlineSnapshot(`
135+
"app/ssr/page.js (8:8) @ Foo
136+
137+
6 | return (
138+
7 | <div>
139+
> 8 | <Foo />
140+
| ^
141+
9 | </div>
142+
10 | )
143+
11 | }"
144+
`)
145+
}
146+
})
147+
})
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* @type {import('next').NextConfig}
3+
*/
4+
const nextConfig = {
5+
experimental: {
6+
reactOwnerStack: true,
7+
},
8+
}
9+
10+
module.exports = nextConfig

0 commit comments

Comments
 (0)