diff --git a/package.json b/package.json index 62d7fdd69afba..974d2c2c4e70b 100644 --- a/package.json +++ b/package.json @@ -169,6 +169,7 @@ "npm-run-all": "4.1.5", "nprogress": "0.2.0", "open": "9.0.0", + "outdent": "0.8.0", "pixrem": "5.0.0", "playwright-chromium": "1.28.1", "plop": "3.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1723a51cd180..d538c857cf381 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,6 +128,7 @@ importers: npm-run-all: 4.1.5 nprogress: 0.2.0 open: 9.0.0 + outdent: 0.8.0 pixrem: 5.0.0 playwright-chromium: 1.28.1 plop: 3.0.5 @@ -295,6 +296,7 @@ importers: npm-run-all: 4.1.5 nprogress: 0.2.0 open: 9.0.0 + outdent: 0.8.0 pixrem: 5.0.0 playwright-chromium: 1.28.1 plop: 3.0.5 @@ -18878,6 +18880,10 @@ packages: os-tmpdir: 1.0.2 dev: true + /outdent/0.8.0: + resolution: {integrity: sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==} + dev: true + /p-cancelable/0.3.0: resolution: {integrity: sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==} engines: {node: '>=4'} diff --git a/test/development/acceptance-app/ReactRefresh.test.ts b/test/development/acceptance-app/ReactRefresh.test.ts index fafd38c09b6bb..cad69548c4f64 100644 --- a/test/development/acceptance-app/ReactRefresh.test.ts +++ b/test/development/acceptance-app/ReactRefresh.test.ts @@ -1,29 +1,24 @@ /* eslint-env jest */ -import { sandbox } from './helpers' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'test/lib/next-modes/base' +import { sandbox } from 'development-sandbox' +import { FileRef, nextTestSetup } from 'e2e-utils' import path from 'path' +import { outdent } from 'outdent' describe('ReactRefresh app', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), - dependencies: { - react: 'latest', - 'react-dom': 'latest', - }, - skipStart: true, - }) + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + dependencies: { + react: 'latest', + 'react-dom': 'latest', + }, + skipStart: true, }) - afterAll(() => next.destroy()) test('can edit a component without losing state', async () => { const { session, cleanup } = await sandbox(next) await session.patch( 'index.js', - ` + outdent` import { useCallback, useState } from 'react' export default function Index() { const [count, setCount] = useState(0) @@ -43,7 +38,7 @@ describe('ReactRefresh app', () => { ).toBe('1') await session.patch( 'index.js', - ` + outdent` import { useCallback, useState } from 'react' export default function Index() { const [count, setCount] = useState(0) @@ -72,7 +67,7 @@ describe('ReactRefresh app', () => { await session.write( 'NudgeOverview.js', - ` + outdent` import * as React from 'react'; import { foo } from './routes'; @@ -88,7 +83,7 @@ describe('ReactRefresh app', () => { await session.write( 'SurveyOverview.js', - ` + outdent` const SurveyOverview = () => { return 100; }; @@ -99,7 +94,7 @@ describe('ReactRefresh app', () => { await session.write( 'Milestones.js', - ` + outdent` import React from 'react'; import { fragment } from './DashboardPage'; @@ -115,7 +110,7 @@ describe('ReactRefresh app', () => { await session.write( 'DashboardPage.js', - ` + outdent` import React from 'react'; import Milestones from './Milestones'; @@ -140,7 +135,7 @@ describe('ReactRefresh app', () => { await session.write( 'routes.js', - ` + outdent` import DashboardPage from './DashboardPage'; export const foo = {}; @@ -154,7 +149,7 @@ describe('ReactRefresh app', () => { await session.patch( 'index.js', - ` + outdent` import * as React from 'react'; import DashboardPage from './routes'; @@ -173,7 +168,7 @@ describe('ReactRefresh app', () => { let didFullRefresh = !(await session.patch( 'SurveyOverview.js', - ` + outdent` const SurveyOverview = () => { return 200; }; @@ -189,7 +184,7 @@ describe('ReactRefresh app', () => { didFullRefresh = !(await session.patch( 'index.js', - ` + outdent` import * as React from 'react'; import DashboardPage from './routes'; @@ -209,7 +204,7 @@ describe('ReactRefresh app', () => { didFullRefresh = !(await session.patch( 'SurveyOverview.js', - ` + outdent` const SurveyOverview = () => { return 300; }; diff --git a/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts b/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts index f2f952c682560..2bfdd40e93c3e 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts @@ -1,151 +1,146 @@ -import { sandbox } from './helpers' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'test/lib/next-modes/base' +import { sandbox } from 'development-sandbox' +import { FileRef, nextTestSetup } from 'e2e-utils' import path from 'path' -import { getSnapshotTestDescribe } from 'next-test-utils' +import { describeVariants as describe } from 'next-test-utils' +import { outdent } from 'outdent' // TODO-APP: Investigate snapshot mismatch -for (const variant of ['default', 'turbo']) { - getSnapshotTestDescribe(variant)(`ReactRefreshLogBox app ${variant}`, () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef( - path.join(__dirname, 'fixtures', 'default-template') - ), - dependencies: { - react: 'latest', - 'react-dom': 'latest', - }, - skipStart: true, - }) - }) - afterAll(() => next.destroy()) - - // Module trace is only available with webpack 5 - test('Node.js builtins', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'node_modules/my-package/index.js', - ` - const dns = require('dns') - module.exports = dns - `, - ], - [ - 'node_modules/my-package/package.json', - ` - { - "name": "my-package", - "version": "0.0.1" - } - `, - ], - ]) - ) - - await session.patch( - 'index.js', - ` - import pkg from 'my-package' - - export default function Hello() { - return (pkg ?
index page
+ } + `, + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) + + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() + + await session.patch( + 'app/page.js', + outdent` + 'use client' export default function Page(props) { returnindex page
} - `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) - - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() - - await session.patch( - 'app/page.js', - `'use client' - export default function Page(props) { - returnindex page
- } - ` - ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.documentElement.innerHTML) - ).toContain('index page') - - await cleanup() - }) + ` + ) + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.documentElement.innerHTML) + ).toContain('index page') + + await cleanup() }) -} +}) diff --git a/test/development/acceptance-app/ReactRefreshLogBox-scss.test.ts b/test/development/acceptance-app/ReactRefreshLogBox-scss.test.ts index 2524348db8e82..b2ce9f861b7da 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox-scss.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox-scss.test.ts @@ -1,24 +1,19 @@ /* eslint-env jest */ -import { sandbox } from './helpers' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'test/lib/next-modes/base' +import { sandbox } from 'development-sandbox' +import { FileRef, nextTestSetup } from 'e2e-utils' import path from 'path' +import { outdent } from 'outdent' describe('ReactRefreshLogBox app', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), - skipStart: true, - dependencies: { - sass: 'latest', - react: 'latest', - 'react-dom': 'latest', - }, - }) + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + dependencies: { + sass: 'latest', + react: 'latest', + 'react-dom': 'latest', + }, + skipStart: true, }) - afterAll(() => next.destroy()) test('scss syntax errors', async () => { const { session, cleanup } = await sandbox(next) @@ -26,7 +21,7 @@ describe('ReactRefreshLogBox app', () => { await session.write('index.module.scss', `.button { font-size: 5px; }`) await session.patch( 'index.js', - ` + outdent` import './index.module.scss'; export default () => { return ( diff --git a/test/development/acceptance-app/ReactRefreshLogBox.test.ts b/test/development/acceptance-app/ReactRefreshLogBox.test.ts index f6f7e6b51d02b..ba71baa42f42f 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox.test.ts @@ -1,34 +1,26 @@ /* eslint-env jest */ -import { sandbox } from './helpers' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'test/lib/next-modes/base' -import { check, getSnapshotTestDescribe } from 'next-test-utils' +import { sandbox } from 'development-sandbox' +import { FileRef, nextTestSetup } from 'e2e-utils' +import { check, describeVariants as describe } from 'next-test-utils' import path from 'path' +import { outdent } from 'outdent' + +describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => { + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + dependencies: { + react: 'latest', + 'react-dom': 'latest', + }, + skipStart: true, + }) -for (const variant of ['default', 'turbo']) { - getSnapshotTestDescribe(variant)(`ReactRefreshLogBox app ${variant}`, () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef( - path.join(__dirname, 'fixtures', 'default-template') - ), - dependencies: { - react: 'latest', - 'react-dom': 'latest', - }, - skipStart: true, - }) - }) - afterAll(() => next.destroy()) - - test('should strip whitespace correctly with newline', async () => { - const { session, cleanup } = await sandbox(next) - - await session.patch( - 'index.js', - ` + test('should strip whitespace correctly with newline', async () => { + const { session, cleanup } = await sandbox(next) + + await session.patch( + 'index.js', + outdent` export default function Page() { return ( <> @@ -44,24 +36,24 @@ for (const variant of ['default', 'turbo']) { ) } ` - ) - await session.evaluate(() => document.querySelector('a').click()) + ) + await session.evaluate(() => document.querySelector('a').click()) - await session.waitForAndOpenRuntimeError() - expect(await session.getRedboxSource()).toMatchSnapshot() + await session.waitForAndOpenRuntimeError() + expect(await session.getRedboxSource()).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137807 - test('module init error not shown', async () => { - // Start here: - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137807 + test('module init error not shown', async () => { + // Start here: + const { session, cleanup } = await sandbox(next) - // We start here. - await session.patch( - 'index.js', - ` + // We start here. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { render() { @@ -70,16 +62,16 @@ for (const variant of ['default', 'turbo']) { } export default ClassDefault; ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('h1').textContent) - ).toBe('Default Export') + expect( + await session.evaluate(() => document.querySelector('h1').textContent) + ).toBe('Default Export') - // Add a throw in module init phase: - await session.patch( - 'index.js', - ` + // Add a throw in module init phase: + await session.patch( + 'index.js', + outdent` // top offset for snapshot import * as React from 'react'; throw new Error('no') @@ -90,33 +82,33 @@ for (const variant of ['default', 'turbo']) { } export default ClassDefault; ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - if (process.platform === 'win32') { - expect(await session.getRedboxSource()).toMatchSnapshot() - } else { - expect(await session.getRedboxSource()).toMatchSnapshot() - } + expect(await session.hasRedbox(true)).toBe(true) + if (process.platform === 'win32') { + expect(await session.getRedboxSource()).toMatchSnapshot() + } else { + expect(await session.getRedboxSource()).toMatchSnapshot() + } - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554152127 - test('boundaries', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554152127 + test('boundaries', async () => { + const { session, cleanup } = await sandbox(next) - await session.write( - 'FunctionDefault.js', - ` + await session.write( + 'FunctionDefault.js', + outdent` export default function FunctionDefault() { returnlol
- 6 | div - 7 | ) - 8 | } - : ^ - 9 | + ,-[TEST_DIR/index.js:4:1] + 4 |lol
+ 5 | div + 6 | ) + 7 | } + : ^ \`---- x Unexpected eof - ,-[TEST_DIR/index.js:6:1] - 6 | div - 7 | ) - 8 | } - 9 | + ,-[TEST_DIR/index.js:4:1] + 4 |lol
+ 5 | div + 6 | ) + 7 | } \`---- Caused by: @@ -255,27 +247,27 @@ for (const variant of ['default', 'turbo']) { ./index.js ./app/page.js" `) - ) + ) - await cleanup() - }) + await cleanup() + }) - // Module trace is only available with webpack 5 - test('conversion to class component (1)', async () => { - const { session, cleanup } = await sandbox(next) + // Module trace is only available with webpack 5 + test('conversion to class component (1)', async () => { + const { session, cleanup } = await sandbox(next) - await session.write( - 'Child.js', - ` + await session.write( + 'Child.js', + outdent` export default function ClickCount() { returnhello
} ` - ) + ) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + outdent` import Child from './Child'; export default function Home() { @@ -286,16 +278,16 @@ for (const variant of ['default', 'turbo']) { ) } ` - ) + ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('hello') + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('hello') - await session.patch( - 'Child.js', - ` + await session.patch( + 'Child.js', + outdent` import { Component } from 'react'; export default class ClickCount extends Component { render() { @@ -303,38 +295,38 @@ for (const variant of ['default', 'turbo']) { } } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - await session.patch( - 'Child.js', - ` - import { Component } from 'react'; + await session.patch( + 'Child.js', + outdent` + import { Component } from 'react'; export default class ClickCount extends Component { render() { returnhello new
} } ` - ) + ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('hello new') + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('hello new') - await cleanup() - }) + await cleanup() + }) - test('css syntax errors', async () => { - const { session, cleanup } = await sandbox(next) + test('css syntax errors', async () => { + const { session, cleanup } = await sandbox(next) - await session.write('index.module.css', `.button {}`) - await session.patch( - 'index.js', - ` + await session.write('index.module.css', `.button {}`) + await session.patch( + 'index.js', + outdent` import './index.module.css'; export default () => { return ( @@ -344,35 +336,35 @@ for (const variant of ['default', 'turbo']) { ) } ` - ) - - expect(await session.hasRedbox(false)).toBe(false) - - // Syntax error - await session.patch('index.module.css', `.button {`) - expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatch('./index.module.css (1:1)') - expect(source).toMatch('Syntax error: ') - expect(source).toMatch('Unclosed block') - expect(source).toMatch('> 1 | .button {') - expect(source).toMatch(' | ^') - - // Not local error - await session.patch('index.module.css', `button {}`) - expect(await session.hasRedbox(true)).toBe(true) - const source2 = await session.getRedboxSource() - expect(source2).toMatchSnapshot() + ) - await cleanup() - }) + expect(await session.hasRedbox(false)).toBe(false) + + // Syntax error + await session.patch('index.module.css', `.button {`) + expect(await session.hasRedbox(true)).toBe(true) + const source = await session.getRedboxSource() + expect(source).toMatch('./index.module.css (1:1)') + expect(source).toMatch('Syntax error: ') + expect(source).toMatch('Unclosed block') + expect(source).toMatch('> 1 | .button {') + expect(source).toMatch(' | ^') + + // Not local error + await session.patch('index.module.css', `button {}`) + expect(await session.hasRedbox(true)).toBe(true) + const source2 = await session.getRedboxSource() + expect(source2).toMatchSnapshot() + + await cleanup() + }) - test('logbox: anchors links in error messages', async () => { - const { session, cleanup } = await sandbox(next) + test('logbox: anchors links in error messages', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -386,38 +378,38 @@ for (const variant of ['default', 'turbo']) { ) } ` - ) - - await session.evaluate(() => document.querySelector('button').click()) - await session.waitForAndOpenRuntimeError() + ) - const header = await session.getRedboxDescription() - expect(header).toMatchSnapshot() - expect( - await session.evaluate( - () => + await session.evaluate(() => document.querySelector('button').click()) + await session.waitForAndOpenRuntimeError() + + const header = await session.getRedboxDescription() + expect(header).toMatchSnapshot() + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(1) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(1) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -431,38 +423,38 @@ for (const variant of ['default', 'turbo']) { ) } ` - ) - - await session.evaluate(() => document.querySelector('button').click()) - await session.waitForAndOpenRuntimeError() + ) - const header2 = await session.getRedboxDescription() - expect(header2).toMatchSnapshot() - expect( - await session.evaluate( - () => + await session.evaluate(() => document.querySelector('button').click()) + await session.waitForAndOpenRuntimeError() + + const header2 = await session.getRedboxDescription() + expect(header2).toMatchSnapshot() + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(1) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(1) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -476,38 +468,38 @@ for (const variant of ['default', 'turbo']) { ) } ` - ) - - await session.evaluate(() => document.querySelector('button').click()) - await session.waitForAndOpenRuntimeError() + ) - const header3 = await session.getRedboxDescription() - expect(header3).toMatchSnapshot() - expect( - await session.evaluate( - () => + await session.evaluate(() => document.querySelector('button').click()) + await session.waitForAndOpenRuntimeError() + + const header3 = await session.getRedboxDescription() + expect(header3).toMatchSnapshot() + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(1) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(1) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -521,59 +513,59 @@ for (const variant of ['default', 'turbo']) { ) } ` - ) + ) - await session.evaluate(() => document.querySelector('button').click()) - await session.waitForAndOpenRuntimeError() + await session.evaluate(() => document.querySelector('button').click()) + await session.waitForAndOpenRuntimeError() - const header4 = await session.getRedboxDescription() - expect(header4).toMatchInlineSnapshot( - `"Error: multiple http://nextjs.org links http://example.com"` - ) - expect( - await session.evaluate( - () => + const header4 = await session.getRedboxDescription() + expect(header4).toMatchInlineSnapshot( + `"Error: multiple http://nextjs.org links http://example.com"` + ) + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(2) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(2) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(2)' - ) as any - ).href - ) - ).toMatchSnapshot() + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() + expect( + await session.evaluate( + () => + ( + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(2)' + ) as any + ).href + ) + ).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - // TODO-APP: Catch errors that happen before useEffect - test.skip('non-Error errors are handled properly', async () => { - const { session, cleanup } = await sandbox(next) + // TODO-APP: Catch errors that happen before useEffect + test.skip('non-Error errors are handled properly', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + outdent` export default () => { throw {'a': 1, 'b': 'x'}; return ( @@ -581,28 +573,28 @@ for (const variant of ['default', 'turbo']) { ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: {\\"a\\":1,\\"b\\":\\"x\\"}"` - ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: {\\"a\\":1,\\"b\\":\\"x\\"}"` + ) - // fix previous error - await session.patch( - 'index.js', - ` + // fix previous error + await session.patch( + 'index.js', + outdent` export default () => { return (Hello world
+ } + `, + ], + ]) ) + expect(await session.hasRedbox(true)).toBe(true) - test('Server component errors should open up in fullscreen', async () => { - const { session, browser, cleanup } = await sandbox( - next, - new Map([ - // Start with error - [ - 'app/page.js', - ` + // Remove error + await session.patch( + 'app/page.js', + outdent` export default function Page() { - throw new Error('Server component error') returnHello world
} - `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) - - // Remove error - await session.patch( - 'app/page.js', - ` - export default function Page() { - returnHello world
- } ` - ) - expect(await browser.waitForElementByCss('#text').text()).toBe( - 'Hello world' - ) - expect(await session.hasRedbox(false)).toBe(false) + ) + expect(await browser.waitForElementByCss('#text').text()).toBe( + 'Hello world' + ) + expect(await session.hasRedbox(false)).toBe(false) - // Re-add error - await session.patch( - 'app/page.js', - ` - export default function Page() { - throw new Error('Server component error!') - returnHello world
- } + // Re-add error + await session.patch( + 'app/page.js', + outdent` + export default function Page() { + throw new Error('Server component error!') + returnHello world
+ } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - await cleanup() - }) + await cleanup() + }) - test('Import trace when module not found in layout', async () => { - const { session, cleanup } = await sandbox( - next, + test('Import trace when module not found in layout', async () => { + const { session, cleanup } = await sandbox( + next, - new Map([['app/module.js', `import "non-existing-module"`]]) - ) + new Map([['app/module.js', `import "non-existing-module"`]]) + ) - await session.patch( - 'app/layout.js', - ` + await session.patch( + 'app/layout.js', + outdent` import "./module" export default function RootLayout({ children }) { @@ -890,28 +881,27 @@ for (const variant of ['default', 'turbo']) {