import { nextTestSetup } from 'e2e-utils' import { waitForRedbox } from 'next-test-utils' // Filter CLI output to only keep Turbopack error header lines (starting with тип or // standalone file:line:col) to accurately count error occurrences without counting // console.error, stack traces, or forwarded browser logs. function filterToErrorHeaders(output: string): string { return output .split('\n') .filter( (line) => !line.includes('[browser]') && !line.includes('console.error') && !line.includes('at ') && !line.includes('at Object.') && !line.includes('at DevServer.') && !line.includes('at DevBundlerService.') && !line.includes('at async ') ) .join('\n') } // Only Turbopack runs the transform on the layout once in edge and non-edge contexts // so we only test this on Turbopack ;(process.env.IS_TURBOPACK_TEST ? describe : describe.skip)( 'cache-components-edge-deduplication', () => { const { next, skipped, isNextDev } = nextTestSetup({ files: __dirname + '/fixtures/edge-deduplication', skipStart: true, skipDeployment: true, }) if (skipped) { return } it('should not duplicate errors when layout is compiled for both edge and non-edge contexts', async () => { try { await next.start() } catch { // we expect the build to fail } if (isNextDev) { const browser = await next.browser('/edge-with-layout/edge') waitForRedbox(browser) await expect(browser).toDisplayRedbox(` { "description": "Route segment config "dynamic" is not compatible with \`nextConfig.cacheComponents\`. Please remove it.", "environmentLabel": null, "label": "Build Error", "source": "./app/edge-with-layout/layout.tsx (1:14) Route segment config "dynamic" is not compatible with \`nextConfig.cacheComponents\`. Please remove it. > 1 | export const dynamic = 'force-dynamic' | ^^^^^^^", "stack": [], } `) // Count occurrences of the layout error at the specific location // Filter out browser logs to avoid counting forwarded browser errors const filteredOutput = filterToErrorHeaders(next.cliOutput) const layoutErrorMatches = filteredOutput.match( /\.\/app\/edge-with-layout\/layout\.tsx:1:14/g ) // We don't show an error stack, just the individual error messages at each location expect(layoutErrorMatches.length).toBe(1) } else { // Check that both the layout and edge page errors appear expect(next.cliOutput).toContain('./app/edge-with-layout/layout.tsx') expect(next.cliOutput).toContain('./app/edge-with-layout/edge/page.tsx') expect(next.cliOutput).toContain( '"dynamic" is not compatible with `nextConfig.cacheComponents`. Please remove it.' ) expect(next.cliOutput).toContain( '"runtime" is not compatible with `nextConfig.cacheComponents`. Please remove it.' ) // Count occurrences of the layout error at the specific location const layoutErrorMatches = next.cliOutput.match( /\.\/app\/layout\.tsx:1:14/g ) // Should appear exactly twice: once in the formatted error message, once in the stack trace expect(layoutErrorMatches.length).toBe(2) } }) } )