From faefe3e32428eb4b6933826d0500d1b75b5a9616 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Wed, 30 Oct 2024 23:13:15 +0100 Subject: [PATCH] test: add case for handling invalid element --- .../app/browser/browser-only.js | 11 ++ .../invalid-element-type/app/browser/page.js | 11 ++ .../app-dir/invalid-element-type/app/foo.js | 0 .../invalid-element-type/app/layout.tsx | 8 + .../invalid-element-type/app/rsc/page.js | 9 ++ .../invalid-element-type/app/ssr/page.js | 11 ++ .../invalid-element-type.test.ts | 147 ++++++++++++++++++ .../invalid-element-type/next.config.js | 10 ++ 8 files changed, 207 insertions(+) create mode 100644 test/development/app-dir/invalid-element-type/app/browser/browser-only.js create mode 100644 test/development/app-dir/invalid-element-type/app/browser/page.js create mode 100644 test/development/app-dir/invalid-element-type/app/foo.js create mode 100644 test/development/app-dir/invalid-element-type/app/layout.tsx create mode 100644 test/development/app-dir/invalid-element-type/app/rsc/page.js create mode 100644 test/development/app-dir/invalid-element-type/app/ssr/page.js create mode 100644 test/development/app-dir/invalid-element-type/invalid-element-type.test.ts create mode 100644 test/development/app-dir/invalid-element-type/next.config.js diff --git a/test/development/app-dir/invalid-element-type/app/browser/browser-only.js b/test/development/app-dir/invalid-element-type/app/browser/browser-only.js new file mode 100644 index 0000000000000..dc00bde91399b --- /dev/null +++ b/test/development/app-dir/invalid-element-type/app/browser/browser-only.js @@ -0,0 +1,11 @@ +'use client' + +import Foo from '../foo' + +export default function BrowserOnly() { + return ( +
+ +
+ ) +} diff --git a/test/development/app-dir/invalid-element-type/app/browser/page.js b/test/development/app-dir/invalid-element-type/app/browser/page.js new file mode 100644 index 0000000000000..77875a1db1096 --- /dev/null +++ b/test/development/app-dir/invalid-element-type/app/browser/page.js @@ -0,0 +1,11 @@ +'use client' + +import dynamic from 'next/dynamic' + +const BrowserOnly = dynamic(() => import('./browser-only'), { + ssr: false, +}) + +export default function Page() { + return +} diff --git a/test/development/app-dir/invalid-element-type/app/foo.js b/test/development/app-dir/invalid-element-type/app/foo.js new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/development/app-dir/invalid-element-type/app/layout.tsx b/test/development/app-dir/invalid-element-type/app/layout.tsx new file mode 100644 index 0000000000000..888614deda3ba --- /dev/null +++ b/test/development/app-dir/invalid-element-type/app/layout.tsx @@ -0,0 +1,8 @@ +import { ReactNode } from 'react' +export default function Root({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} diff --git a/test/development/app-dir/invalid-element-type/app/rsc/page.js b/test/development/app-dir/invalid-element-type/app/rsc/page.js new file mode 100644 index 0000000000000..3b835737b3983 --- /dev/null +++ b/test/development/app-dir/invalid-element-type/app/rsc/page.js @@ -0,0 +1,9 @@ +import Foo from '../foo' + +export default function Page() { + return ( +
+ +
+ ) +} diff --git a/test/development/app-dir/invalid-element-type/app/ssr/page.js b/test/development/app-dir/invalid-element-type/app/ssr/page.js new file mode 100644 index 0000000000000..297ed0e3ef260 --- /dev/null +++ b/test/development/app-dir/invalid-element-type/app/ssr/page.js @@ -0,0 +1,11 @@ +'use client' + +import Foo from '../foo' + +export default function Page() { + return ( +
+ +
+ ) +} diff --git a/test/development/app-dir/invalid-element-type/invalid-element-type.test.ts b/test/development/app-dir/invalid-element-type/invalid-element-type.test.ts new file mode 100644 index 0000000000000..dfdc08aab00b1 --- /dev/null +++ b/test/development/app-dir/invalid-element-type/invalid-element-type.test.ts @@ -0,0 +1,147 @@ +import { nextTestSetup } from 'e2e-utils' +import { assertHasRedbox, getRedboxSource } from 'next-test-utils' + +async function getStackFramesContent(browser) { + const stackFrameElements = await browser.elementsByCss( + '[data-nextjs-call-stack-frame]' + ) + const stackFramesContent = ( + await Promise.all( + stackFrameElements.map(async (frame) => { + const functionNameEl = await frame.$('[data-nextjs-frame-expanded]') + const sourceEl = await frame.$('[data-has-source]') + const functionName = functionNameEl + ? await functionNameEl.innerText() + : '' + const source = sourceEl ? await sourceEl.innerText() : '' + + if (!functionName) { + return '' + } + return `at ${functionName} (${source})` + }) + ) + ) + .filter(Boolean) + .join('\n') + + return stackFramesContent +} + +describe('app-dir - invalid-element-type', () => { + const { next } = nextTestSetup({ + files: __dirname, + }) + + it('should catch invalid element from on client-only component', async () => { + const browser = await next.browser('/browser') + + await assertHasRedbox(browser) + const source = await getRedboxSource(browser) + + const stackFramesContent = await getStackFramesContent(browser) + if (process.env.TURBOPACK) { + expect(stackFramesContent).toMatchInlineSnapshot( + `"at Page (app/browser/page.js (10:10))"` + ) + expect(source).toMatchInlineSnapshot(` + "app/browser/browser-only.js (8:7) @ BrowserOnly + + 6 | return ( + 7 |
+ > 8 | + | ^ + 9 |
+ 10 | ) + 11 | }" + `) + } else { + expect(stackFramesContent).toMatchInlineSnapshot( + `"at BrowserOnly (app/browser/page.js (10:11))"` + ) + expect(source).toMatchInlineSnapshot(` + "app/browser/browser-only.js (8:8) @ Foo + + 6 | return ( + 7 |
+ > 8 | + | ^ + 9 |
+ 10 | ) + 11 | }" + `) + } + }) + + it('should catch invalid element from on rsc component', async () => { + const browser = await next.browser('/rsc') + + await assertHasRedbox(browser) + const stackFramesContent = await getStackFramesContent(browser) + const source = await getRedboxSource(browser) + + if (process.env.TURBOPACK) { + expect(stackFramesContent).toMatchInlineSnapshot(`""`) + expect(source).toMatchInlineSnapshot(` + "app/rsc/page.js (6:7) @ Page + + 4 | return ( + 5 |
+ > 6 | + | ^ + 7 |
+ 8 | ) + 9 | }" + `) + } else { + expect(stackFramesContent).toMatchInlineSnapshot(`""`) + expect(source).toMatchInlineSnapshot(` + "app/rsc/page.js (6:8) @ Foo + + 4 | return ( + 5 |
+ > 6 | + | ^ + 7 |
+ 8 | ) + 9 | }" + `) + } + }) + + it('should catch invalid element from on ssr client component', async () => { + const browser = await next.browser('/ssr') + + await assertHasRedbox(browser) + + const stackFramesContent = await getStackFramesContent(browser) + const source = await getRedboxSource(browser) + if (process.env.TURBOPACK) { + expect(stackFramesContent).toMatchInlineSnapshot(`""`) + expect(source).toMatchInlineSnapshot(` + "app/ssr/page.js (8:7) @ Page + + 6 | return ( + 7 |
+ > 8 | + | ^ + 9 |
+ 10 | ) + 11 | }" + `) + } else { + expect(stackFramesContent).toMatchInlineSnapshot(`""`) + expect(source).toMatchInlineSnapshot(` + "app/ssr/page.js (8:8) @ Foo + + 6 | return ( + 7 |
+ > 8 | + | ^ + 9 |
+ 10 | ) + 11 | }" + `) + } + }) +}) diff --git a/test/development/app-dir/invalid-element-type/next.config.js b/test/development/app-dir/invalid-element-type/next.config.js new file mode 100644 index 0000000000000..d14b1bf8fdc37 --- /dev/null +++ b/test/development/app-dir/invalid-element-type/next.config.js @@ -0,0 +1,10 @@ +/** + * @type {import('next').NextConfig} + */ +const nextConfig = { + experimental: { + reactOwnerStack: true, + }, +} + +module.exports = nextConfig