diff --git a/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts b/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts index 7d3a39e2a7797..f2f952c682560 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts @@ -2,69 +2,73 @@ import { sandbox } from './helpers' import { createNext, FileRef } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' import path from 'path' +import { getSnapshotTestDescribe } from 'next-test-utils' // TODO-APP: Investigate snapshot mismatch -describe('ReactRefreshLogBox 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, +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', - ` + 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', - ` + ], + [ + 'node_modules/my-package/package.json', + ` { "name": "my-package", "version": "0.0.1" } `, - ], - ]) - ) + ], + ]) + ) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import pkg from 'my-package' export default function Hello() { return (pkg ?

Package loaded

:

Package did not load

) } ` - ) - 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 cleanup() - }) + await cleanup() + }) - test('Module not found', async () => { - const { session, cleanup } = await sandbox(next) + test('Module not found', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - `import Comp from 'b' + await session.patch( + 'index.js', + `import Comp from 'b' export default function Oops() { return (
@@ -73,22 +77,22 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Module not found empty import trace', async () => { - const { session, cleanup } = await sandbox(next) + test('Module not found empty import trace', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'app/page.js', - `'use client' + await session.patch( + 'app/page.js', + `'use client' import Comp from 'b' export default function Oops() { return ( @@ -98,49 +102,50 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Module not found missing global CSS', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'app/page.js', - `'use client' + test('Module not found missing global CSS', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'app/page.js', + `'use client' import './non-existent.css' export default function Page(props) { return

index page

} `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await session.patch( - 'app/page.js', - `'use client' + await session.patch( + 'app/page.js', + `'use client' export default function Page(props) { return

index page

} ` - ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.documentElement.innerHTML) - ).toContain('index page') + ) + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.documentElement.innerHTML) + ).toContain('index page') - await cleanup() + await cleanup() + }) }) -}) +} diff --git a/test/development/acceptance-app/ReactRefreshLogBox.test.ts b/test/development/acceptance-app/ReactRefreshLogBox.test.ts index dddf003d1bc39..20f7262256af9 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox.test.ts @@ -2,30 +2,33 @@ import { sandbox } from './helpers' import { createNext, FileRef } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' -import { check } from 'next-test-utils' +import { check, getSnapshotTestDescribe } from 'next-test-utils' import path from 'path' -describe('ReactRefreshLogBox 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, +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()) + afterAll(() => next.destroy()) - test('should strip whitespace correctly with newline', async () => { - const { session, cleanup } = await sandbox(next) + test('should strip whitespace correctly with newline', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` export default function Page() { return ( <> @@ -41,21 +44,21 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) - 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() + }) - test('logbox: can recover from a syntax error without losing state', async () => { - const { session, cleanup } = await sandbox(next) + test('logbox: can recover from a syntax error without losing state', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import { useCallback, useState } from 'react' export default function Index() { @@ -69,21 +72,21 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') - await session.patch('index.js', `export default () =>
{ ) } ` - ) + ) - await check( - () => session.evaluate(() => document.querySelector('p').textContent), - /Count: 1/ - ) + await check( + () => session.evaluate(() => document.querySelector('p').textContent), + /Count: 1/ + ) - expect(await session.hasRedbox()).toBe(false) + expect(await session.hasRedbox()).toBe(false) - await cleanup() - }) + await cleanup() + }) - test('logbox: can recover from a event handler error', async () => { - const { session, cleanup } = await sandbox(next) + test('logbox: can recover from a event handler error', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import { useCallback, useState } from 'react' export default function Index() { @@ -131,26 +134,26 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) - - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('0') - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('1') + ) - await session.waitForAndOpenRuntimeError() - if (process.platform === 'win32') { - expect(await session.getRedboxSource()).toMatchSnapshot() - } else { - expect(await session.getRedboxSource()).toMatchSnapshot() - } + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('0') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') + + await session.waitForAndOpenRuntimeError() + if (process.platform === 'win32') { + expect(await session.getRedboxSource()).toMatchSnapshot() + } else { + expect(await session.getRedboxSource()).toMatchSnapshot() + } - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import { useCallback, useState } from 'react' export default function Index() { @@ -164,40 +167,40 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - expect(await session.hasRedbox()).toBe(false) - expect(await session.hasErrorToast()).toBe(false) + expect(await session.hasRedbox()).toBe(false) + expect(await session.hasErrorToast()).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Count: 1') - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Count: 2') + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 2') - expect(await session.hasRedbox()).toBe(false) - expect(await session.hasErrorToast()).toBe(false) + expect(await session.hasRedbox()).toBe(false) + expect(await session.hasErrorToast()).toBe(false) - await cleanup() - }) + await cleanup() + }) - test('logbox: can recover from a component error', async () => { - const { session, cleanup } = await sandbox(next) + test('logbox: can recover from a component error', async () => { + const { session, cleanup } = await sandbox(next) - await session.write( - 'child.js', - ` + await session.write( + 'child.js', + ` export default function Child() { return

Hello

; } ` - ) + ) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import Child from './child' export default function Index() { @@ -208,135 +211,139 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Hello') + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Hello') - await session.patch( - 'child.js', - ` + await session.patch( + 'child.js', + ` // hello export default function Child() { throw new Error('oops') } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - // TODO-APP: re-enable when error recovery doesn't reload the page. - /* const didNotReload = */ await session.patch( - 'child.js', - ` + // TODO-APP: re-enable when error recovery doesn't reload the page. + /* const didNotReload = */ await session.patch( + 'child.js', + ` export default function Child() { return

Hello

; } ` - ) + ) - // TODO-APP: re-enable when error recovery doesn't reload the page. - // expect(didNotReload).toBe(true) - expect(await session.hasRedbox()).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Hello') + // TODO-APP: re-enable when error recovery doesn't reload the page. + // expect(didNotReload).toBe(true) + expect(await session.hasRedbox()).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Hello') - await cleanup() - }) + await cleanup() + }) - test('server component can recover from syntax error', async () => { - const { session, browser, cleanup } = await sandbox( - next, - new Map([ - [ - 'app/page.js', - ` + test('server component can recover from syntax error', async () => { + const { session, browser, cleanup } = await sandbox( + next, + new Map([ + [ + 'app/page.js', + ` export default function Page() { return

Hello world

} `, - ], - ]) - ) + ], + ]) + ) - // Add syntax error - await session.patch( - 'app/page.js', - ` + // Add syntax error + await session.patch( + 'app/page.js', + ` export default function Page() { return

Hello world

` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Fix syntax error - await session.patch( - 'app/page.js', - ` + // Fix syntax error + await session.patch( + 'app/page.js', + ` export default function Page() { return

Hello world 2

} ` - ) + ) - expect(await browser.waitForElementByCss('p').text()).toBe('Hello world 2') + expect(await browser.waitForElementByCss('p').text()).toBe( + 'Hello world 2' + ) - await cleanup() - }) + await cleanup() + }) - test('server component can recover from component error', async () => { - const { session, browser, cleanup } = await sandbox( - next, - new Map([ - [ - 'app/page.js', - ` + test('server component can recover from component error', async () => { + const { session, browser, cleanup } = await sandbox( + next, + new Map([ + [ + 'app/page.js', + ` export default function Page() { return

Hello world

} `, - ], - ]) - ) + ], + ]) + ) - // Add component error - await session.patch( - 'app/page.js', - ` + // Add component error + await session.patch( + 'app/page.js', + ` export default function Page() { throw new Error("boom") return

Hello world

} ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Fix component error - await session.patch( - 'app/page.js', - ` + // Fix component error + await session.patch( + 'app/page.js', + ` export default function Page() { return

Hello world 2

} ` - ) + ) - expect(await browser.waitForElementByCss('p').text()).toBe('Hello world 2') + expect(await browser.waitForElementByCss('p').text()).toBe( + 'Hello world 2' + ) - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137262 - test('render error not shown right after syntax error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137262 + test('render error not shown right after syntax error', async () => { + const { session, cleanup } = await sandbox(next) - // Starting here: - await session.patch( - 'index.js', - ` + // Starting here: + await session.patch( + 'index.js', + ` import * as React from 'react'; class ClassDefault extends React.Component { render() { @@ -346,16 +353,16 @@ describe('ReactRefreshLogBox app', () => { 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') - // Break it with a syntax error: - await session.patch( - 'index.js', - ` + // Break it with a syntax error: + await session.patch( + 'index.js', + ` import * as React from 'react'; class ClassDefault extends React.Component { @@ -366,13 +373,13 @@ describe('ReactRefreshLogBox app', () => { export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Now change the code to introduce a runtime error without fixing the syntax error: - await session.patch( - 'index.js', - ` + // Now change the code to introduce a runtime error without fixing the syntax error: + await session.patch( + 'index.js', + ` import * as React from 'react'; class ClassDefault extends React.Component { @@ -384,13 +391,13 @@ describe('ReactRefreshLogBox app', () => { export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Now fix the syntax error: - await session.patch( - 'index.js', - ` + // Now fix the syntax error: + await session.patch( + 'index.js', + ` import * as React from 'react'; class ClassDefault extends React.Component { @@ -402,28 +409,28 @@ describe('ReactRefreshLogBox app', () => { export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - await check(async () => { - const source = await session.getRedboxSource() - return source?.length > 1 ? 'success' : source - }, 'success') + await check(async () => { + const source = await session.getRedboxSource() + return source?.length > 1 ? 'success' : source + }, 'success') - expect(await session.getRedboxSource()).toMatchSnapshot() + 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', + ` import * as React from 'react'; class ClassDefault extends React.Component { render() { @@ -432,16 +439,16 @@ describe('ReactRefreshLogBox app', () => { } 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', + ` // top offset for snapshot import * as React from 'react'; throw new Error('no') @@ -452,26 +459,26 @@ describe('ReactRefreshLogBox app', () => { } 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-554144016 - test('stuck error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554144016 + test('stuck error', async () => { + const { session, cleanup } = await sandbox(next) - // We start here. - await session.patch( - 'index.js', - ` + // We start here. + await session.patch( + 'index.js', + ` import * as React from 'react'; function FunctionDefault() { @@ -480,23 +487,23 @@ describe('ReactRefreshLogBox app', () => { export default FunctionDefault; ` - ) + ) - // We add a new file. Let's call it Foo.js. - await session.write( - 'Foo.js', - ` + // We add a new file. Let's call it Foo.js. + await session.write( + 'Foo.js', + ` // intentionally skips export export default function Foo() { return React.createElement('h1', null, 'Foo'); } ` - ) + ) - // We edit our first file to use it. - await session.patch( - 'index.js', - ` + // We edit our first file to use it. + await session.patch( + 'index.js', + ` import * as React from 'react'; import Foo from './Foo'; function FunctionDefault() { @@ -504,48 +511,48 @@ describe('ReactRefreshLogBox app', () => { } export default FunctionDefault; ` - ) + ) - // We get an error because Foo didn't import React. Fair. - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + // We get an error because Foo didn't import React. Fair. + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - // Let's add that to Foo. - await session.patch( - 'Foo.js', - ` + // Let's add that to Foo. + await session.patch( + 'Foo.js', + ` import * as React from 'react'; export default function Foo() { return React.createElement('h1', null, 'Foo'); } ` - ) + ) - // Expected: this fixes the problem - expect(await session.hasRedbox()).toBe(false) + // Expected: this fixes the problem + expect(await session.hasRedbox()).toBe(false) - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554150098 - test('syntax > runtime error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554150098 + test('syntax > runtime error', async () => { + const { session, cleanup } = await sandbox(next) - // Start here. - await session.patch( - 'index.js', - ` + // Start here. + await session.patch( + 'index.js', + ` import * as React from 'react'; export default function FunctionNamed() { return
} ` - ) - // TODO: this acts weird without above step - await session.patch( - 'index.js', - ` + ) + // TODO: this acts weird without above step + await session.patch( + 'index.js', + ` import * as React from 'react'; let i = 0 setInterval(() => { @@ -556,20 +563,20 @@ describe('ReactRefreshLogBox app', () => { return
} ` - ) + ) - await new Promise((resolve) => setTimeout(resolve, 1000)) - await session.waitForAndOpenRuntimeError() - if (process.platform === 'win32') { - expect(await session.getRedboxSource()).toMatchSnapshot() - } else { - expect(await session.getRedboxSource()).toMatchSnapshot() - } + await new Promise((resolve) => setTimeout(resolve, 1000)) + await session.waitForAndOpenRuntimeError() + if (process.platform === 'win32') { + expect(await session.getRedboxSource()).toMatchSnapshot() + } else { + expect(await session.getRedboxSource()).toMatchSnapshot() + } - // Make a syntax error. - await session.patch( - 'index.js', - ` + // Make a syntax error. + await session.patch( + 'index.js', + ` import * as React from 'react'; let i = 0 setInterval(() => { @@ -577,35 +584,35 @@ describe('ReactRefreshLogBox app', () => { throw Error('no ' + i) }, 1000) export default function FunctionNamed() {` - ) + ) - await new Promise((resolve) => setTimeout(resolve, 1000)) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + await new Promise((resolve) => setTimeout(resolve, 1000)) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - // Test that runtime error does not take over: - await new Promise((resolve) => setTimeout(resolve, 2000)) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + // Test that runtime error does not take over: + await new Promise((resolve) => setTimeout(resolve, 2000)) + expect(await session.hasRedbox(true)).toBe(true) + 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', + ` export default function FunctionDefault() { return

hello

} ` - ) - await session.patch( - 'index.js', - ` + ) + await session.patch( + 'index.js', + ` import FunctionDefault from './FunctionDefault.js' import * as React from 'react' class ErrorBoundary extends React.Component { @@ -635,58 +642,58 @@ describe('ReactRefreshLogBox app', () => { } export default App; ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('h2').textContent) - ).toBe('hello') + expect( + await session.evaluate(() => document.querySelector('h2').textContent) + ).toBe('hello') - await session.write( - 'FunctionDefault.js', - `export default function FunctionDefault() { throw new Error('no'); }` - ) + await session.write( + 'FunctionDefault.js', + `export default function FunctionDefault() { throw new Error('no'); }` + ) - await session.waitForAndOpenRuntimeError() - expect(await session.getRedboxSource()).toMatchSnapshot() - expect( - await session.evaluate(() => document.querySelector('h2').textContent) - ).toBe('error') + await session.waitForAndOpenRuntimeError() + expect(await session.getRedboxSource()).toMatchSnapshot() + expect( + await session.evaluate(() => document.querySelector('h2').textContent) + ).toBe('error') - await cleanup() - }) + await cleanup() + }) - // TODO: investigate why this fails when running outside of the Next.js - // monorepo e.g. fails when using yarn create next-app - // https://github.com/vercel/next.js/pull/23203 - test.skip('internal package errors', async () => { - const { session, cleanup } = await sandbox(next) + // TODO: investigate why this fails when running outside of the Next.js + // monorepo e.g. fails when using yarn create next-app + // https://github.com/vercel/next.js/pull/23203 + test.skip('internal package errors', async () => { + const { session, cleanup } = await sandbox(next) - // Make a react build-time error. - await session.patch( - 'index.js', - ` + // Make a react build-time error. + await session.patch( + 'index.js', + ` export default function FunctionNamed() { return
{{}}
}` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - // We internally only check the script path, not including the line number - // and error message because the error comes from an external library. - // This test ensures that the errored script path is correctly resolved. - expect(await session.getRedboxSource()).toContain( - `../../../../packages/next/dist/pages/_document.js` - ) + expect(await session.hasRedbox(true)).toBe(true) + // We internally only check the script path, not including the line number + // and error message because the error comes from an external library. + // This test ensures that the errored script path is correctly resolved. + expect(await session.getRedboxSource()).toContain( + `../../../../packages/next/dist/pages/_document.js` + ) - await cleanup() - }) + await cleanup() + }) - test('unterminated JSX', async () => { - const { session, cleanup } = await sandbox(next) + test('unterminated JSX', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` export default () => { return (
@@ -695,13 +702,13 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - expect(await session.hasRedbox()).toBe(false) + expect(await session.hasRedbox()).toBe(false) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` export default () => { return (
@@ -710,32 +717,32 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - 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', + ` export default function ClickCount() { return

hello

} ` - ) + ) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import Child from './Child'; export default function Home() { @@ -746,16 +753,16 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - expect(await session.hasRedbox()).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('hello') + expect(await session.hasRedbox()).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('hello') - await session.patch( - 'Child.js', - ` + await session.patch( + 'Child.js', + ` import { Component } from 'react'; export default class ClickCount extends Component { render() { @@ -763,14 +770,14 @@ describe('ReactRefreshLogBox app', () => { } } ` - ) + ) - 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', - ` + await session.patch( + 'Child.js', + ` import { Component } from 'react'; export default class ClickCount extends Component { render() { @@ -778,23 +785,23 @@ describe('ReactRefreshLogBox app', () => { } } ` - ) + ) - expect(await session.hasRedbox()).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('hello new') + expect(await session.hasRedbox()).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', + ` import './index.module.css'; export default () => { return ( @@ -804,35 +811,35 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - expect(await session.hasRedbox()).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()).toBe(false) - test('logbox: anchors links in error messages', async () => { - const { session, cleanup } = await sandbox(next) + // 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 session.patch( - 'index.js', - ` + await cleanup() + }) + + test('logbox: anchors links in error messages', async () => { + const { session, cleanup } = await sandbox(next) + + await session.patch( + 'index.js', + ` import { useCallback } from 'react' export default function Index() { @@ -846,38 +853,38 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - 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( - () => - ( + 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.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() + .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() - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import { useCallback } from 'react' export default function Index() { @@ -891,38 +898,38 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) + + await session.evaluate(() => document.querySelector('button').click()) + await session.waitForAndOpenRuntimeError() - 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( - () => - ( + const header2 = await session.getRedboxDescription() + expect(header2).toMatchSnapshot() + 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.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() - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import { useCallback } from 'react' export default function Index() { @@ -936,38 +943,38 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) + + await session.evaluate(() => document.querySelector('button').click()) + await session.waitForAndOpenRuntimeError() - 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( - () => - ( + const header3 = await session.getRedboxDescription() + expect(header3).toMatchSnapshot() + 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.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() - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import { useCallback } from 'react' export default function Index() { @@ -981,59 +988,59 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - 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( - () => - 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( - () => - ( + 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.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(2)' - ) as any - ).href - ) - ).toMatchSnapshot() + .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() - 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', + ` export default () => { throw {'a': 1, 'b': 'x'}; return ( @@ -1041,28 +1048,28 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) + ) - 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', + ` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - ` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + ` class Hello {} export default () => { @@ -1072,27 +1079,27 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toContain( - `Error: class Hello {` - ) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toContain( + `Error: class Hello {` + ) - // fix previous error - await session.patch( - 'index.js', - ` + // fix previous error + await session.patch( + 'index.js', + ` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - ` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + ` export default () => { throw "string error" return ( @@ -1100,27 +1107,27 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: string error"` - ) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: string error"` + ) - // fix previous error - await session.patch( - 'index.js', - ` + // fix previous error + await session.patch( + 'index.js', + ` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - ` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + ` export default () => { throw null return ( @@ -1128,21 +1135,21 @@ describe('ReactRefreshLogBox app', () => { ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toContain( - `Error: A null error was thrown` - ) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toContain( + `Error: A null error was thrown` + ) - await cleanup() - }) + await cleanup() + }) - test('Should not show __webpack_exports__ when exporting anonymous arrow function', async () => { - const { session, cleanup } = await sandbox(next) + test('Should not show __webpack_exports__ when exporting anonymous arrow function', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` export default () => { if (typeof window !== 'undefined') { throw new Error('test') @@ -1152,18 +1159,18 @@ describe('ReactRefreshLogBox app', () => { } ` - ) + ) - 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 cleanup() - }) + await cleanup() + }) - test('Unhandled errors and rejections opens up in the minimized state', async () => { - const { session, browser, cleanup } = await sandbox(next) + test('Unhandled errors and rejections opens up in the minimized state', async () => { + const { session, browser, cleanup } = await sandbox(next) - const file = ` + const file = ` export default function Index() { // setTimeout(() => { @@ -1195,62 +1202,62 @@ describe('ReactRefreshLogBox app', () => { } ` - await session.patch('index.js', file) + await session.patch('index.js', file) - // Unhandled error and rejection in setTimeout - expect( - await browser.waitForElementByCss('.nextjs-toast-errors').text() - ).toBe('2 errors') + // Unhandled error and rejection in setTimeout + expect( + await browser.waitForElementByCss('.nextjs-toast-errors').text() + ).toBe('2 errors') - // Unhandled error in event handler - await browser.elementById('unhandled-error').click() - await check( - () => browser.elementByCss('.nextjs-toast-errors').text(), - /3 errors/ - ) - - // Unhandled rejection in event handler - await browser.elementById('unhandled-rejection').click() - await check( - () => browser.elementByCss('.nextjs-toast-errors').text(), - /4 errors/ - ) - expect(await session.hasRedbox()).toBe(false) + // Unhandled error in event handler + await browser.elementById('unhandled-error').click() + await check( + () => browser.elementByCss('.nextjs-toast-errors').text(), + /3 errors/ + ) - // Add Component error - await session.patch( - 'index.js', - file.replace( - '//', - "if (typeof window !== 'undefined') throw new Error('Component error')" + // Unhandled rejection in event handler + await browser.elementById('unhandled-rejection').click() + await check( + () => browser.elementByCss('.nextjs-toast-errors').text(), + /4 errors/ + ) + expect(await session.hasRedbox()).toBe(false) + + // Add Component error + await session.patch( + 'index.js', + file.replace( + '//', + "if (typeof window !== 'undefined') throw new Error('Component error')" + ) ) - ) - // Render error should "win" and show up in fullscreen - expect(await session.hasRedbox(true)).toBe(true) + // Render error should "win" and show up in fullscreen + expect(await session.hasRedbox(true)).toBe(true) - await cleanup() - }) + await cleanup() + }) - test.each([['server'], ['client']])( - 'Call stack count is correct for %s error', - async (pageType: string) => { - const fixture = - pageType === 'server' - ? new Map([ - [ - 'app/page.js', - ` + test.each([['server'], ['client']])( + 'Call stack count is correct for %s error', + async (pageType: string) => { + const fixture = + pageType === 'server' + ? new Map([ + [ + 'app/page.js', + ` export default function Page() { throw new Error('Server error') } `, - ], - ]) - : new Map([ - [ - 'app/page.js', - ` + ], + ]) + : new Map([ + [ + 'app/page.js', + ` 'use client' export default function Page() { if (typeof window !== 'undefined') { @@ -1259,106 +1266,107 @@ describe('ReactRefreshLogBox app', () => { return null } `, - ], - ]) + ], + ]) - const { session, browser, cleanup } = await sandbox(next, fixture) + const { session, browser, cleanup } = await sandbox(next, fixture) - const getCallStackCount = async () => - (await browser.elementsByCss('[data-nextjs-call-stack-frame]')).length + const getCallStackCount = async () => + (await browser.elementsByCss('[data-nextjs-call-stack-frame]')).length - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - // Open full Call Stack - await browser - .elementByCss('[data-nextjs-data-runtime-error-collapsed-action]') - .click() + // Open full Call Stack + await browser + .elementByCss('[data-nextjs-data-runtime-error-collapsed-action]') + .click() - const collapsedFrameworkGroups = await browser.elementsByCss( - "[data-nextjs-call-stack-framework-button][data-state='closed']" - ) - for (const collapsedFrameworkButton of collapsedFrameworkGroups) { - // Open the collapsed framework groups, the callstack count should increase with each opened group - const callStackCountBeforeGroupOpened = await getCallStackCount() - await collapsedFrameworkButton.click() - expect(await getCallStackCount()).toBeGreaterThan( - callStackCountBeforeGroupOpened + const collapsedFrameworkGroups = await browser.elementsByCss( + "[data-nextjs-call-stack-framework-button][data-state='closed']" ) - } + for (const collapsedFrameworkButton of collapsedFrameworkGroups) { + // Open the collapsed framework groups, the callstack count should increase with each opened group + const callStackCountBeforeGroupOpened = await getCallStackCount() + await collapsedFrameworkButton.click() + expect(await getCallStackCount()).toBeGreaterThan( + callStackCountBeforeGroupOpened + ) + } - // Expect more than the default amount of frames - // The default stackTraceLimit results in max 9 [data-nextjs-call-stack-frame] elements - expect(await getCallStackCount()).toBeGreaterThan(9) + // Expect more than the default amount of frames + // The default stackTraceLimit results in max 9 [data-nextjs-call-stack-frame] elements + expect(await getCallStackCount()).toBeGreaterThan(9) - await cleanup() - } - ) - - 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', - ` + await cleanup() + } + ) + + 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', + ` export default function Page() { throw new Error('Server component error') return

Hello world

} `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Remove error - await session.patch( - 'app/page.js', - ` + // Remove error + await session.patch( + 'app/page.js', + ` export default function Page() { return

Hello world

} ` - ) - expect(await browser.waitForElementByCss('#text').text()).toBe( - 'Hello world' - ) - expect(await session.hasRedbox()).toBe(false) + ) + expect(await browser.waitForElementByCss('#text').text()).toBe( + 'Hello world' + ) + expect(await session.hasRedbox()).toBe(false) - // Re-add error - await session.patch( - 'app/page.js', - ` + // Re-add error + await session.patch( + 'app/page.js', + ` export default function Page() { throw new Error('Server component error!') return

Hello world

} ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - await cleanup() - }) + await cleanup() + }) - test('Hydration errors should get error link', async () => { - const { session, browser, cleanup } = await sandbox(next) + test('Hydration errors should get error link', async () => { + const { session, browser, cleanup } = await sandbox(next) - await session.patch( - 'app/page.js', - ` + await session.patch( + 'app/page.js', + ` "use client" export default function Page() { return

{typeof window === 'undefined' ? "hello" : "world"}

} ` - ) + ) - await browser.refresh() - await session.waitForAndOpenRuntimeError() - expect(await session.getRedboxDescription()).toMatchSnapshot() + await browser.refresh() + await session.waitForAndOpenRuntimeError() + expect(await session.getRedboxDescription()).toMatchSnapshot() - await cleanup() + await cleanup() + }) }) -}) +} diff --git a/test/development/acceptance-app/__snapshots__/ReactRefreshLogBox-builtins.test.ts.snap b/test/development/acceptance-app/__snapshots__/ReactRefreshLogBox-builtins.test.ts.snap index fc2a1bc5c1e4c..6b6d37ce81914 100644 --- a/test/development/acceptance-app/__snapshots__/ReactRefreshLogBox-builtins.test.ts.snap +++ b/test/development/acceptance-app/__snapshots__/ReactRefreshLogBox-builtins.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ReactRefreshLogBox app Module not found 1`] = ` +exports[`ReactRefreshLogBox app default Module not found 1`] = ` "./index.js:1:0 Module not found: Can't resolve 'b' > 1 | import Comp from 'b' @@ -14,7 +14,7 @@ Import trace for requested module: https://nextjs.org/docs/messages/module-not-found" `; -exports[`ReactRefreshLogBox app Module not found empty import trace 1`] = ` +exports[`ReactRefreshLogBox app default Module not found empty import trace 1`] = ` "./app/page.js:2:6 Module not found: Can't resolve 'b' 1 | 'use client' @@ -27,7 +27,7 @@ Module not found: Can't resolve 'b' https://nextjs.org/docs/messages/module-not-found" `; -exports[`ReactRefreshLogBox app Module not found missing global CSS 1`] = ` +exports[`ReactRefreshLogBox app default Module not found missing global CSS 1`] = ` "./app/page.js:2:10 Module not found: Can't resolve './non-existent.css' 1 | 'use client' @@ -40,7 +40,7 @@ Module not found: Can't resolve './non-existent.css' https://nextjs.org/docs/messages/module-not-found" `; -exports[`ReactRefreshLogBox app Node.js builtins 1`] = ` +exports[`ReactRefreshLogBox app default Node.js builtins 1`] = ` "./node_modules/my-package/index.js:2:0 Module not found: Can't resolve 'dns' diff --git a/test/development/acceptance-app/__snapshots__/ReactRefreshLogBox.test.ts.snap b/test/development/acceptance-app/__snapshots__/ReactRefreshLogBox.test.ts.snap index 4bf8d3b9a83dc..83a71a3365965 100644 --- a/test/development/acceptance-app/__snapshots__/ReactRefreshLogBox.test.ts.snap +++ b/test/development/acceptance-app/__snapshots__/ReactRefreshLogBox.test.ts.snap @@ -1,12 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ReactRefreshLogBox app Hydration errors should get error link 1`] = ` +exports[`ReactRefreshLogBox app default Hydration errors should get error link 1`] = ` "Error: Text content does not match server-rendered HTML. See more info here: https://nextjs.org/docs/messages/react-hydration-error" `; -exports[`ReactRefreshLogBox app Should not show __webpack_exports__ when exporting anonymous arrow function 1`] = ` +exports[`ReactRefreshLogBox app default Should not show __webpack_exports__ when exporting anonymous arrow function 1`] = ` "index.js (4:16) @ default 2 | export default () => { @@ -18,14 +18,14 @@ exports[`ReactRefreshLogBox app Should not show __webpack_exports__ when exporti 7 | return null" `; -exports[`ReactRefreshLogBox app boundaries 1`] = ` +exports[`ReactRefreshLogBox app default boundaries 1`] = ` "FunctionDefault.js (1:50) @ FunctionDefault > 1 | export default function FunctionDefault() { throw new Error('no'); } | ^" `; -exports[`ReactRefreshLogBox app conversion to class component (1) 1`] = ` +exports[`ReactRefreshLogBox app default conversion to class component (1) 1`] = ` "Child.js (5:18) @ ClickCount.render 3 | export default class ClickCount extends Component { @@ -37,7 +37,7 @@ exports[`ReactRefreshLogBox app conversion to class component (1) 1`] = ` 8 | " `; -exports[`ReactRefreshLogBox app css syntax errors 1`] = ` +exports[`ReactRefreshLogBox app default css syntax errors 1`] = ` "./index.module.css:1:1 Syntax error: Selector \\"button\\" is not pure (pure selectors must contain at least one local class or id) @@ -45,23 +45,23 @@ Syntax error: Selector \\"button\\" is not pure (pure selectors must contain at | ^" `; -exports[`ReactRefreshLogBox app logbox: anchors links in error messages 1`] = `"Error: end http://nextjs.org"`; +exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 1`] = `"Error: end http://nextjs.org"`; -exports[`ReactRefreshLogBox app logbox: anchors links in error messages 2`] = `"http://nextjs.org/"`; +exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 2`] = `"http://nextjs.org/"`; -exports[`ReactRefreshLogBox app logbox: anchors links in error messages 3`] = `"Error: http://nextjs.org start"`; +exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 3`] = `"Error: http://nextjs.org start"`; -exports[`ReactRefreshLogBox app logbox: anchors links in error messages 4`] = `"http://nextjs.org/"`; +exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 4`] = `"http://nextjs.org/"`; -exports[`ReactRefreshLogBox app logbox: anchors links in error messages 5`] = `"Error: middle http://nextjs.org end"`; +exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 5`] = `"Error: middle http://nextjs.org end"`; -exports[`ReactRefreshLogBox app logbox: anchors links in error messages 6`] = `"http://nextjs.org/"`; +exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 6`] = `"http://nextjs.org/"`; -exports[`ReactRefreshLogBox app logbox: anchors links in error messages 8`] = `"http://nextjs.org/"`; +exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 8`] = `"http://nextjs.org/"`; -exports[`ReactRefreshLogBox app logbox: anchors links in error messages 9`] = `"http://example.com/"`; +exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 9`] = `"http://example.com/"`; -exports[`ReactRefreshLogBox app logbox: can recover from a component error 1`] = ` +exports[`ReactRefreshLogBox app default logbox: can recover from a component error 1`] = ` "child.js (4:16) @ Child 2 | // hello @@ -72,7 +72,7 @@ exports[`ReactRefreshLogBox app logbox: can recover from a component error 1`] = 6 | " `; -exports[`ReactRefreshLogBox app logbox: can recover from a event handler error 1`] = ` +exports[`ReactRefreshLogBox app default logbox: can recover from a event handler error 1`] = ` "index.js (8:18) @ eval 6 | const increment = useCallback(() => { @@ -84,7 +84,7 @@ exports[`ReactRefreshLogBox app logbox: can recover from a event handler error 1 11 |
" `; -exports[`ReactRefreshLogBox app logbox: can recover from a syntax error without losing state 1`] = ` +exports[`ReactRefreshLogBox app default logbox: can recover from a syntax error without losing state 1`] = ` "./index.js Error: x Unexpected eof @@ -101,7 +101,7 @@ Import trace for requested module: ./app/page.js" `; -exports[`ReactRefreshLogBox app module init error not shown 1`] = ` +exports[`ReactRefreshLogBox app default module init error not shown 1`] = ` "index.js (4:14) @ eval 2 | // top offset for snapshot @@ -113,7 +113,7 @@ exports[`ReactRefreshLogBox app module init error not shown 1`] = ` 7 | return

Default Export

;" `; -exports[`ReactRefreshLogBox app render error not shown right after syntax error 1`] = ` +exports[`ReactRefreshLogBox app default render error not shown right after syntax error 1`] = ` "index.js (6:18) @ ClassDefault.render 4 | class ClassDefault extends React.Component { @@ -125,7 +125,7 @@ exports[`ReactRefreshLogBox app render error not shown right after syntax error 9 | }" `; -exports[`ReactRefreshLogBox app should strip whitespace correctly with newline 1`] = ` +exports[`ReactRefreshLogBox app default should strip whitespace correctly with newline 1`] = ` "index.js (9:34) @ onClick 7 | @@ -137,7 +137,7 @@ exports[`ReactRefreshLogBox app should strip whitespace correctly with newline 1 12 | " `; -exports[`ReactRefreshLogBox app stuck error 1`] = ` +exports[`ReactRefreshLogBox app default stuck error 1`] = ` "Foo.js (4:10) @ Foo 2 | // intentionally skips export @@ -148,7 +148,7 @@ exports[`ReactRefreshLogBox app stuck error 1`] = ` 6 | " `; -exports[`ReactRefreshLogBox app syntax > runtime error 1`] = ` +exports[`ReactRefreshLogBox app default syntax > runtime error 1`] = ` "index.js (6:16) @ Error 4 | setInterval(() => { @@ -160,7 +160,7 @@ exports[`ReactRefreshLogBox app syntax > runtime error 1`] = ` 9 | return
" `; -exports[`ReactRefreshLogBox app syntax > runtime error 2`] = ` +exports[`ReactRefreshLogBox app default syntax > runtime error 2`] = ` "./index.js Error: x Expected '}', got '' @@ -182,7 +182,7 @@ Import trace for requested module: ./app/page.js" `; -exports[`ReactRefreshLogBox app syntax > runtime error 3`] = ` +exports[`ReactRefreshLogBox app default syntax > runtime error 3`] = ` "./index.js Error: x Expected '}', got '' @@ -204,7 +204,7 @@ Import trace for requested module: ./app/page.js" `; -exports[`ReactRefreshLogBox app unterminated JSX 1`] = ` +exports[`ReactRefreshLogBox app default unterminated JSX 1`] = ` "./index.js Error: x Unexpected token. Did you mean \`{'}'}\` or \`}\`? diff --git a/test/development/acceptance/ReactRefreshLogBox-app-doc.test.ts b/test/development/acceptance/ReactRefreshLogBox-app-doc.test.ts index 755ecdb416d2f..6edbe5938cd30 100644 --- a/test/development/acceptance/ReactRefreshLogBox-app-doc.test.ts +++ b/test/development/acceptance/ReactRefreshLogBox-app-doc.test.ts @@ -1,68 +1,70 @@ import { sandbox } from './helpers' import { createNext } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' +import { getSnapshotTestDescribe } from 'next-test-utils' -describe('ReactRefreshLogBox', () => { - let next: NextInstance +for (const variant of ['default', 'turbo']) { + getSnapshotTestDescribe(variant)(`ReactRefreshLogBox ${variant}`, () => { + let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: {}, - skipStart: true, + beforeAll(async () => { + next = await createNext({ + files: {}, + skipStart: true, + }) }) - }) - afterAll(() => next.destroy()) - - test('empty _app shows logbox', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'pages/_app.js', - ` - + afterAll(() => next.destroy()) + + test('empty _app shows logbox', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'pages/_app.js', + ` + `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: The default export is not a React Component in page: \\"/_app\\""` - ) - - await session.patch( - 'pages/_app.js', - ` + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: The default export is not a React Component in page: \\"/_app\\""` + ) + + await session.patch( + 'pages/_app.js', + ` function MyApp({ Component, pageProps }) { return ; } export default MyApp ` - ) - expect(await session.hasRedbox()).toBe(false) - await cleanup() - }) + ) + expect(await session.hasRedbox()).toBe(false) + await cleanup() + }) + + test('empty _document shows logbox', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'pages/_document.js', + ` - test('empty _document shows logbox', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'pages/_document.js', - ` - `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: The default export is not a React Component in page: \\"/_document\\""` - ) - - await session.patch( - 'pages/_document.js', - ` + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: The default export is not a React Component in page: \\"/_document\\""` + ) + + await session.patch( + 'pages/_document.js', + ` import Document, { Html, Head, Main, NextScript } from 'next/document' class MyDocument extends Document { @@ -86,49 +88,49 @@ describe('ReactRefreshLogBox', () => { export default MyDocument ` - ) - expect(await session.hasRedbox()).toBe(false) - await cleanup() - }) + ) + expect(await session.hasRedbox()).toBe(false) + await cleanup() + }) - test('_app syntax error shows logbox', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'pages/_app.js', - ` + test('_app syntax error shows logbox', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'pages/_app.js', + ` function MyApp({ Component, pageProps }) { return <; } export default MyApp `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() - - await session.patch( - 'pages/_app.js', - ` + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() + + await session.patch( + 'pages/_app.js', + ` function MyApp({ Component, pageProps }) { return ; } export default MyApp ` - ) - expect(await session.hasRedbox()).toBe(false) - await cleanup() - }) + ) + expect(await session.hasRedbox()).toBe(false) + await cleanup() + }) - test('_document syntax error shows logbox', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'pages/_document.js', - ` + test('_document syntax error shows logbox', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'pages/_document.js', + ` import Document, { Html, Head, Main, NextScript } from 'next/document' class MyDocument extends Document {{ @@ -152,15 +154,15 @@ describe('ReactRefreshLogBox', () => { export default MyDocument `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() - - await session.patch( - 'pages/_document.js', - ` + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() + + await session.patch( + 'pages/_document.js', + ` import Document, { Html, Head, Main, NextScript } from 'next/document' class MyDocument extends Document { @@ -184,8 +186,9 @@ describe('ReactRefreshLogBox', () => { export default MyDocument ` - ) - expect(await session.hasRedbox()).toBe(false) - await cleanup() + ) + expect(await session.hasRedbox()).toBe(false) + await cleanup() + }) }) -}) +} diff --git a/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts b/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts index 87a2439ea3add..f3df273fae0f3 100644 --- a/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts +++ b/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts @@ -1,64 +1,66 @@ import { sandbox } from './helpers' import { createNext } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' +import { getSnapshotTestDescribe } from 'next-test-utils' -describe('ReactRefreshLogBox', () => { - let next: NextInstance +for (const variant of ['default', 'turbo']) { + getSnapshotTestDescribe(variant)(`ReactRefreshLogBox ${variant}`, () => { + let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: {}, - skipStart: true, + beforeAll(async () => { + next = await createNext({ + files: {}, + 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', - ` + 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', - ` + ], + [ + 'node_modules/my-package/package.json', + ` { "name": "my-package", "version": "0.0.1" } `, - ], - ]) - ) + ], + ]) + ) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import pkg from 'my-package' export default function Hello() { return (pkg ?

Package loaded

:

Package did not load

) } ` - ) - 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 cleanup() - }) + await cleanup() + }) - test('Module not found', async () => { - const { session, cleanup } = await sandbox(next) + test('Module not found', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - `import Comp from 'b' + await session.patch( + 'index.js', + `import Comp from 'b' export default function Oops() { return (
@@ -67,22 +69,22 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Module not found (empty import trace)', async () => { - const { session, cleanup } = await sandbox(next) + test('Module not found (empty import trace)', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'pages/index.js', - `import Comp from 'b' + await session.patch( + 'pages/index.js', + `import Comp from 'b' export default function Oops() { return (
@@ -91,58 +93,59 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Module not found (missing global CSS)', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'pages/_app.js', - ` + test('Module not found (missing global CSS)', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'pages/_app.js', + ` import './non-existent.css' - + export default function App({ Component, pageProps }) { return } `, - ], - [ - 'pages/index.js', - ` + ], + [ + 'pages/index.js', + ` export default function Page(props) { return

index page

} `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await session.patch( - 'pages/_app.js', - ` + await session.patch( + 'pages/_app.js', + ` export default function App({ Component, pageProps }) { return } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.documentElement.innerHTML) - ).toContain('index page') + ) + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.documentElement.innerHTML) + ).toContain('index page') - await cleanup() + await cleanup() + }) }) -}) +} diff --git a/test/development/acceptance/ReactRefreshLogBox.test.ts b/test/development/acceptance/ReactRefreshLogBox.test.ts index d9aced58dba6f..7fdb666390d70 100644 --- a/test/development/acceptance/ReactRefreshLogBox.test.ts +++ b/test/development/acceptance/ReactRefreshLogBox.test.ts @@ -2,29 +2,30 @@ import { sandbox } from './helpers' import { createNext } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' -import { check } from 'next-test-utils' +import { check, getSnapshotTestDescribe } from 'next-test-utils' -describe('ReactRefreshLogBox', () => { - let next: NextInstance +for (const variant of ['default', 'turbo']) { + getSnapshotTestDescribe(variant)(`ReactRefreshLogBox ${variant}`, () => { + let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: {}, - skipStart: true, + beforeAll(async () => { + next = await createNext({ + files: {}, + skipStart: true, + }) }) - }) - afterAll(() => next.destroy()) + afterAll(() => next.destroy()) - test('should strip whitespace correctly with newline', async () => { - const { session, cleanup } = await sandbox(next) + test('should strip whitespace correctly with newline', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` export default function Page() { return ( <> - +

index page

{ @@ -36,21 +37,21 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) - await session.evaluate(() => document.querySelector('a').click()) + ) + await session.evaluate(() => document.querySelector('a').click()) - 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 cleanup() - }) + await cleanup() + }) - test('logbox: can recover from a syntax error without losing state', async () => { - const { session, cleanup } = await sandbox(next) + test('logbox: can recover from a syntax error without losing state', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import { useCallback, useState } from 'react' export default function Index() { @@ -64,21 +65,21 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) + ) - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') - await session.patch('index.js', `export default () =>
{ ) } ` - ) + ) - await check( - () => session.evaluate(() => document.querySelector('p').textContent), - /Count: 1/ - ) + await check( + () => session.evaluate(() => document.querySelector('p').textContent), + /Count: 1/ + ) - expect(await session.hasRedbox()).toBe(false) + expect(await session.hasRedbox()).toBe(false) - await cleanup() - }) + await cleanup() + }) - test('logbox: can recover from a event handler error', async () => { - const { session, cleanup } = await sandbox(next) + test('logbox: can recover from a event handler error', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import { useCallback, useState } from 'react' export default function Index() { @@ -126,26 +127,26 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) - - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('0') - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('1') - - expect(await session.hasRedbox(true)).toBe(true) - if (process.platform === 'win32') { - expect(await session.getRedboxSource()).toMatchSnapshot() - } else { - expect(await session.getRedboxSource()).toMatchSnapshot() - } + ) - await session.patch( - 'index.js', - ` + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('0') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') + + expect(await session.hasRedbox(true)).toBe(true) + if (process.platform === 'win32') { + expect(await session.getRedboxSource()).toMatchSnapshot() + } else { + expect(await session.getRedboxSource()).toMatchSnapshot() + } + + await session.patch( + 'index.js', + ` import { useCallback, useState } from 'react' export default function Index() { @@ -159,38 +160,38 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) + ) - expect(await session.hasRedbox()).toBe(false) + expect(await session.hasRedbox()).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Count: 1') - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Count: 2') + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 2') - expect(await session.hasRedbox()).toBe(false) + expect(await session.hasRedbox()).toBe(false) - await cleanup() - }) + await cleanup() + }) - test('logbox: can recover from a component error', async () => { - const { session, cleanup } = await sandbox(next) + test('logbox: can recover from a component error', async () => { + const { session, cleanup } = await sandbox(next) - await session.write( - 'child.js', - ` + await session.write( + 'child.js', + ` export default function Child() { return

Hello

; } ` - ) + ) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import Child from './child' export default function Index() { @@ -201,51 +202,51 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Hello') + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Hello') - await session.patch( - 'child.js', - ` + await session.patch( + 'child.js', + ` // hello export default function Child() { throw new Error('oops') } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - const didNotReload = await session.patch( - 'child.js', - ` + const didNotReload = await session.patch( + 'child.js', + ` export default function Child() { return

Hello

; } ` - ) + ) - expect(didNotReload).toBe(true) - expect(await session.hasRedbox()).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Hello') + expect(didNotReload).toBe(true) + expect(await session.hasRedbox()).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Hello') - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137262 - test('render error not shown right after syntax error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137262 + test('render error not shown right after syntax error', async () => { + const { session, cleanup } = await sandbox(next) - // Starting here: - await session.patch( - 'index.js', - ` + // Starting here: + await session.patch( + 'index.js', + ` import * as React from 'react'; class ClassDefault extends React.Component { render() { @@ -255,16 +256,16 @@ describe('ReactRefreshLogBox', () => { 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') - // Break it with a syntax error: - await session.patch( - 'index.js', - ` + // Break it with a syntax error: + await session.patch( + 'index.js', + ` import * as React from 'react'; class ClassDefault extends React.Component { @@ -275,13 +276,13 @@ describe('ReactRefreshLogBox', () => { export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Now change the code to introduce a runtime error without fixing the syntax error: - await session.patch( - 'index.js', - ` + // Now change the code to introduce a runtime error without fixing the syntax error: + await session.patch( + 'index.js', + ` import * as React from 'react'; class ClassDefault extends React.Component { @@ -293,13 +294,13 @@ describe('ReactRefreshLogBox', () => { export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Now fix the syntax error: - await session.patch( - 'index.js', - ` + // Now fix the syntax error: + await session.patch( + 'index.js', + ` import * as React from 'react'; class ClassDefault extends React.Component { @@ -311,22 +312,22 @@ describe('ReactRefreshLogBox', () => { export default ClassDefault; ` - ) - 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 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', + ` import * as React from 'react'; class ClassDefault extends React.Component { render() { @@ -335,16 +336,16 @@ describe('ReactRefreshLogBox', () => { } 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', + ` // top offset for snapshot import * as React from 'react'; throw new Error('no') @@ -355,22 +356,22 @@ describe('ReactRefreshLogBox', () => { } export default ClassDefault; ` - ) + ) - 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 cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554144016 - test('stuck error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554144016 + test('stuck error', async () => { + const { session, cleanup } = await sandbox(next) - // We start here. - await session.patch( - 'index.js', - ` + // We start here. + await session.patch( + 'index.js', + ` import * as React from 'react'; function FunctionDefault() { @@ -379,23 +380,23 @@ describe('ReactRefreshLogBox', () => { export default FunctionDefault; ` - ) + ) - // We add a new file. Let's call it Foo.js. - await session.write( - 'Foo.js', - ` + // We add a new file. Let's call it Foo.js. + await session.write( + 'Foo.js', + ` // intentionally skips export export default function Foo() { return React.createElement('h1', null, 'Foo'); } ` - ) + ) - // We edit our first file to use it. - await session.patch( - 'index.js', - ` + // We edit our first file to use it. + await session.patch( + 'index.js', + ` import * as React from 'react'; import Foo from './Foo'; function FunctionDefault() { @@ -403,48 +404,48 @@ describe('ReactRefreshLogBox', () => { } export default FunctionDefault; ` - ) + ) - // We get an error because Foo didn't import React. Fair. - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + // We get an error because Foo didn't import React. Fair. + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - // Let's add that to Foo. - await session.patch( - 'Foo.js', - ` + // Let's add that to Foo. + await session.patch( + 'Foo.js', + ` import * as React from 'react'; export default function Foo() { return React.createElement('h1', null, 'Foo'); } ` - ) + ) - // Expected: this fixes the problem - expect(await session.hasRedbox()).toBe(false) + // Expected: this fixes the problem + expect(await session.hasRedbox()).toBe(false) - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554150098 - test('syntax > runtime error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554150098 + test('syntax > runtime error', async () => { + const { session, cleanup } = await sandbox(next) - // Start here. - await session.patch( - 'index.js', - ` + // Start here. + await session.patch( + 'index.js', + ` import * as React from 'react'; export default function FunctionNamed() { return
} ` - ) - // TODO: this acts weird without above step - await session.patch( - 'index.js', - ` + ) + // TODO: this acts weird without above step + await session.patch( + 'index.js', + ` import * as React from 'react'; let i = 0 setInterval(() => { @@ -455,20 +456,20 @@ describe('ReactRefreshLogBox', () => { return
} ` - ) - - await new Promise((resolve) => setTimeout(resolve, 1000)) - expect(await session.hasRedbox(true)).toBe(true) - if (process.platform === 'win32') { - expect(await session.getRedboxSource()).toMatchSnapshot() - } else { - expect(await session.getRedboxSource()).toMatchSnapshot() - } + ) - // Make a syntax error. - await session.patch( - 'index.js', - ` + await new Promise((resolve) => setTimeout(resolve, 1000)) + expect(await session.hasRedbox(true)).toBe(true) + if (process.platform === 'win32') { + expect(await session.getRedboxSource()).toMatchSnapshot() + } else { + expect(await session.getRedboxSource()).toMatchSnapshot() + } + + // Make a syntax error. + await session.patch( + 'index.js', + ` import * as React from 'react'; let i = 0 setInterval(() => { @@ -476,35 +477,35 @@ describe('ReactRefreshLogBox', () => { throw Error('no ' + i) }, 1000) export default function FunctionNamed() {` - ) + ) - await new Promise((resolve) => setTimeout(resolve, 1000)) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + await new Promise((resolve) => setTimeout(resolve, 1000)) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - // Test that runtime error does not take over: - await new Promise((resolve) => setTimeout(resolve, 2000)) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + // Test that runtime error does not take over: + await new Promise((resolve) => setTimeout(resolve, 2000)) + expect(await session.hasRedbox(true)).toBe(true) + 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', + ` export default function FunctionDefault() { return

hello

} ` - ) - await session.patch( - 'index.js', - ` + ) + await session.patch( + 'index.js', + ` import FunctionDefault from './FunctionDefault.js' import * as React from 'react' class ErrorBoundary extends React.Component { @@ -534,58 +535,58 @@ describe('ReactRefreshLogBox', () => { } export default App; ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('h2').textContent) - ).toBe('hello') + expect( + await session.evaluate(() => document.querySelector('h2').textContent) + ).toBe('hello') - await session.write( - 'FunctionDefault.js', - `export default function FunctionDefault() { throw new Error('no'); }` - ) + await session.write( + 'FunctionDefault.js', + `export default function FunctionDefault() { throw new Error('no'); }` + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() - expect( - await session.evaluate(() => document.querySelector('h2').textContent) - ).toBe('error') + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() + expect( + await session.evaluate(() => document.querySelector('h2').textContent) + ).toBe('error') - await cleanup() - }) + await cleanup() + }) - // TODO: investigate why this fails when running outside of the Next.js - // monorepo e.g. fails when using yarn create next-app - // https://github.com/vercel/next.js/pull/23203 - test.skip('internal package errors', async () => { - const { session, cleanup } = await sandbox(next) + // TODO: investigate why this fails when running outside of the Next.js + // monorepo e.g. fails when using yarn create next-app + // https://github.com/vercel/next.js/pull/23203 + test.skip('internal package errors', async () => { + const { session, cleanup } = await sandbox(next) - // Make a react build-time error. - await session.patch( - 'index.js', - ` + // Make a react build-time error. + await session.patch( + 'index.js', + ` export default function FunctionNamed() { return
{{}}
}` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - // We internally only check the script path, not including the line number - // and error message because the error comes from an external library. - // This test ensures that the errored script path is correctly resolved. - expect(await session.getRedboxSource()).toContain( - `../../../../packages/next/dist/pages/_document.js` - ) + expect(await session.hasRedbox(true)).toBe(true) + // We internally only check the script path, not including the line number + // and error message because the error comes from an external library. + // This test ensures that the errored script path is correctly resolved. + expect(await session.getRedboxSource()).toContain( + `../../../../packages/next/dist/pages/_document.js` + ) - await cleanup() - }) + await cleanup() + }) - test('unterminated JSX', async () => { - const { session, cleanup } = await sandbox(next) + test('unterminated JSX', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` export default () => { return (
@@ -594,13 +595,13 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) + ) - expect(await session.hasRedbox()).toBe(false) + expect(await session.hasRedbox()).toBe(false) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` export default () => { return (
@@ -609,32 +610,32 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - 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', + ` export default function ClickCount() { return

hello

} ` - ) + ) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` import Child from './Child'; export default function Home() { @@ -645,16 +646,16 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) + ) - expect(await session.hasRedbox()).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('hello') + expect(await session.hasRedbox()).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('hello') - await session.patch( - 'Child.js', - ` + await session.patch( + 'Child.js', + ` import { Component } from 'react'; export default class ClickCount extends Component { render() { @@ -662,14 +663,14 @@ describe('ReactRefreshLogBox', () => { } } ` - ) + ) - 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', - ` + await session.patch( + 'Child.js', + ` import { Component } from 'react'; export default class ClickCount extends Component { render() { @@ -677,23 +678,23 @@ describe('ReactRefreshLogBox', () => { } } ` - ) + ) - expect(await session.hasRedbox()).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('hello new') + expect(await session.hasRedbox()).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', + ` import './index.module.css'; export default () => { return ( @@ -703,35 +704,35 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) - - expect(await session.hasRedbox()).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) + expect(await session.hasRedbox()).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() + }) - await session.patch( - 'index.js', - ` + test('logbox: anchors links in error messages', async () => { + const { session, cleanup } = await sandbox(next) + + await session.patch( + 'index.js', + ` import { useCallback } from 'react' export default function Index() { @@ -745,39 +746,39 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) - - expect(await session.hasRedbox()).toBe(false) - await session.evaluate(() => document.querySelector('button').click()) - expect(await session.hasRedbox(true)).toBe(true) - - 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( - () => - ( + + expect(await session.hasRedbox()).toBe(false) + await session.evaluate(() => document.querySelector('button').click()) + expect(await session.hasRedbox(true)).toBe(true) + + const header = await session.getRedboxDescription() + expect(header).toMatchSnapshot() + expect( + await session.evaluate( + () => document .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() - - await session.patch( - 'index.js', - ` + .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() + + await session.patch( + 'index.js', + ` import { useCallback } from 'react' export default function Index() { @@ -791,39 +792,39 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) - - expect(await session.hasRedbox()).toBe(false) - await session.evaluate(() => document.querySelector('button').click()) - expect(await session.hasRedbox(true)).toBe(true) - - 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( - () => - ( + + expect(await session.hasRedbox()).toBe(false) + await session.evaluate(() => document.querySelector('button').click()) + expect(await session.hasRedbox(true)).toBe(true) + + const header2 = await session.getRedboxDescription() + expect(header2).toMatchSnapshot() + expect( + await session.evaluate( + () => document .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() - - await session.patch( - 'index.js', - ` + .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() + + await session.patch( + 'index.js', + ` import { useCallback } from 'react' export default function Index() { @@ -837,39 +838,39 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) - - expect(await session.hasRedbox()).toBe(false) - await session.evaluate(() => document.querySelector('button').click()) - expect(await session.hasRedbox(true)).toBe(true) - - 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( - () => - ( + + expect(await session.hasRedbox()).toBe(false) + await session.evaluate(() => document.querySelector('button').click()) + expect(await session.hasRedbox(true)).toBe(true) + + const header3 = await session.getRedboxDescription() + expect(header3).toMatchSnapshot() + expect( + await session.evaluate( + () => document .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() - - await session.patch( - 'index.js', - ` + .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() + + await session.patch( + 'index.js', + ` import { useCallback } from 'react' export default function Index() { @@ -883,59 +884,59 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) - - expect(await session.hasRedbox()).toBe(false) - await session.evaluate(() => document.querySelector('button').click()) - expect(await session.hasRedbox(true)).toBe(true) - - 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.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href + + expect(await session.hasRedbox()).toBe(false) + await session.evaluate(() => document.querySelector('button').click()) + expect(await session.hasRedbox(true)).toBe(true) + + const header4 = await session.getRedboxDescription() + expect(header4).toMatchInlineSnapshot( + `"Error: multiple http://nextjs.org links http://example.com"` ) - ).toMatchSnapshot() - expect( - await session.evaluate( - () => - ( + 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() - }) + .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() + + await cleanup() + }) - test('non-Error errors are handled properly', async () => { - const { session, cleanup } = await sandbox(next) + test('non-Error errors are handled properly', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - ` + await session.patch( + 'index.js', + ` export default () => { throw {'a': 1, 'b': 'x'}; return ( @@ -943,30 +944,30 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) + ) - 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', + ` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - ` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + ` class Hello {} - + export default () => { throw Hello return ( @@ -974,27 +975,27 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toContain( - `Error: class Hello {` - ) - - // fix previous error - await session.patch( - 'index.js', - ` + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toContain( + `Error: class Hello {` + ) + + // fix previous error + await session.patch( + 'index.js', + ` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - ` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + ` export default () => { throw "string error" return ( @@ -1002,27 +1003,27 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: string error"` - ) - - // fix previous error - await session.patch( - 'index.js', - ` + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: string error"` + ) + + // fix previous error + await session.patch( + 'index.js', + ` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - ` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + ` export default () => { throw null return ( @@ -1030,12 +1031,13 @@ describe('ReactRefreshLogBox', () => { ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toContain( - `Error: A null error was thrown` - ) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toContain( + `Error: A null error was thrown` + ) - await cleanup() + await cleanup() + }) }) -}) +} diff --git a/test/development/acceptance/__snapshots__/ReactRefreshLogBox-app-doc.test.ts.snap b/test/development/acceptance/__snapshots__/ReactRefreshLogBox-app-doc.test.ts.snap index 8e58bcd0ee232..5079310b3fb98 100644 --- a/test/development/acceptance/__snapshots__/ReactRefreshLogBox-app-doc.test.ts.snap +++ b/test/development/acceptance/__snapshots__/ReactRefreshLogBox-app-doc.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ReactRefreshLogBox _app syntax error shows logbox 1`] = ` +exports[`ReactRefreshLogBox default _app syntax error shows logbox 1`] = ` "./pages/_app.js Error: x Expression expected @@ -30,7 +30,7 @@ Caused by: 1: Syntax Error" `; -exports[`ReactRefreshLogBox _document syntax error shows logbox 1`] = ` +exports[`ReactRefreshLogBox default _document syntax error shows logbox 1`] = ` "./pages/_document.js Error: x Unexpected token \`{\`. Expected identifier, string literal, numeric literal or [ for the computed key diff --git a/test/development/acceptance/__snapshots__/ReactRefreshLogBox-builtins.test.ts.snap b/test/development/acceptance/__snapshots__/ReactRefreshLogBox-builtins.test.ts.snap index 910dd68ed5f2b..5b11d2ab4fb75 100644 --- a/test/development/acceptance/__snapshots__/ReactRefreshLogBox-builtins.test.ts.snap +++ b/test/development/acceptance/__snapshots__/ReactRefreshLogBox-builtins.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ReactRefreshLogBox Module not found (empty import trace) 1`] = ` +exports[`ReactRefreshLogBox default Module not found (empty import trace) 1`] = ` "./pages/index.js:1:0 Module not found: Can't resolve 'b' > 1 | import Comp from 'b' @@ -11,20 +11,20 @@ Module not found: Can't resolve 'b' https://nextjs.org/docs/messages/module-not-found" `; -exports[`ReactRefreshLogBox Module not found (missing global CSS) 1`] = ` +exports[`ReactRefreshLogBox default Module not found (missing global CSS) 1`] = ` "./pages/_app.js:2:8 Module not found: Can't resolve './non-existent.css' 1 | > 2 | import './non-existent.css' | ^ - 3 | + 3 | 4 | export default function App({ Component, pageProps }) { 5 | return https://nextjs.org/docs/messages/module-not-found" `; -exports[`ReactRefreshLogBox Module not found 1`] = ` +exports[`ReactRefreshLogBox default Module not found 1`] = ` "./index.js:1:0 Module not found: Can't resolve 'b' > 1 | import Comp from 'b' @@ -38,7 +38,7 @@ Import trace for requested module: https://nextjs.org/docs/messages/module-not-found" `; -exports[`ReactRefreshLogBox Node.js builtins 1`] = ` +exports[`ReactRefreshLogBox default Node.js builtins 1`] = ` "./node_modules/my-package/index.js:2:0 Module not found: Can't resolve 'dns' diff --git a/test/development/acceptance/__snapshots__/ReactRefreshLogBox.test.ts.snap b/test/development/acceptance/__snapshots__/ReactRefreshLogBox.test.ts.snap index db4cb35f4564e..5905eed5b1162 100644 --- a/test/development/acceptance/__snapshots__/ReactRefreshLogBox.test.ts.snap +++ b/test/development/acceptance/__snapshots__/ReactRefreshLogBox.test.ts.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ReactRefreshLogBox boundaries 1`] = ` +exports[`ReactRefreshLogBox default boundaries 1`] = ` "FunctionDefault.js (1:50) @ FunctionDefault > 1 | export default function FunctionDefault() { throw new Error('no'); } | ^" `; -exports[`ReactRefreshLogBox conversion to class component (1) 1`] = ` +exports[`ReactRefreshLogBox default conversion to class component (1) 1`] = ` "Child.js (5:18) @ ClickCount.render 3 | export default class ClickCount extends Component { @@ -19,7 +19,7 @@ exports[`ReactRefreshLogBox conversion to class component (1) 1`] = ` 8 | " `; -exports[`ReactRefreshLogBox css syntax errors 1`] = ` +exports[`ReactRefreshLogBox default css syntax errors 1`] = ` "./index.module.css:1:1 Syntax error: Selector \\"button\\" is not pure (pure selectors must contain at least one local class or id) @@ -27,23 +27,23 @@ Syntax error: Selector \\"button\\" is not pure (pure selectors must contain at | ^" `; -exports[`ReactRefreshLogBox logbox: anchors links in error messages 1`] = `"Error: end http://nextjs.org"`; +exports[`ReactRefreshLogBox default logbox: anchors links in error messages 1`] = `"Error: end http://nextjs.org"`; -exports[`ReactRefreshLogBox logbox: anchors links in error messages 2`] = `"http://nextjs.org/"`; +exports[`ReactRefreshLogBox default logbox: anchors links in error messages 2`] = `"http://nextjs.org/"`; -exports[`ReactRefreshLogBox logbox: anchors links in error messages 3`] = `"Error: http://nextjs.org start"`; +exports[`ReactRefreshLogBox default logbox: anchors links in error messages 3`] = `"Error: http://nextjs.org start"`; -exports[`ReactRefreshLogBox logbox: anchors links in error messages 4`] = `"http://nextjs.org/"`; +exports[`ReactRefreshLogBox default logbox: anchors links in error messages 4`] = `"http://nextjs.org/"`; -exports[`ReactRefreshLogBox logbox: anchors links in error messages 5`] = `"Error: middle http://nextjs.org end"`; +exports[`ReactRefreshLogBox default logbox: anchors links in error messages 5`] = `"Error: middle http://nextjs.org end"`; -exports[`ReactRefreshLogBox logbox: anchors links in error messages 6`] = `"http://nextjs.org/"`; +exports[`ReactRefreshLogBox default logbox: anchors links in error messages 6`] = `"http://nextjs.org/"`; -exports[`ReactRefreshLogBox logbox: anchors links in error messages 8`] = `"http://nextjs.org/"`; +exports[`ReactRefreshLogBox default logbox: anchors links in error messages 8`] = `"http://nextjs.org/"`; -exports[`ReactRefreshLogBox logbox: anchors links in error messages 9`] = `"http://example.com/"`; +exports[`ReactRefreshLogBox default logbox: anchors links in error messages 9`] = `"http://example.com/"`; -exports[`ReactRefreshLogBox logbox: can recover from a component error 1`] = ` +exports[`ReactRefreshLogBox default logbox: can recover from a component error 1`] = ` "child.js (4:16) @ Child 2 | // hello @@ -54,7 +54,7 @@ exports[`ReactRefreshLogBox logbox: can recover from a component error 1`] = ` 6 | " `; -exports[`ReactRefreshLogBox logbox: can recover from a event handler error 1`] = ` +exports[`ReactRefreshLogBox default logbox: can recover from a event handler error 1`] = ` "index.js (8:18) @ eval 6 | const increment = useCallback(() => { @@ -66,7 +66,7 @@ exports[`ReactRefreshLogBox logbox: can recover from a event handler error 1`] = 11 |
" `; -exports[`ReactRefreshLogBox logbox: can recover from a syntax error without losing state 1`] = ` +exports[`ReactRefreshLogBox default logbox: can recover from a syntax error without losing state 1`] = ` "./index.js Error: x Unexpected eof @@ -82,7 +82,7 @@ Import trace for requested module: ./index.js" `; -exports[`ReactRefreshLogBox module init error not shown 1`] = ` +exports[`ReactRefreshLogBox default module init error not shown 1`] = ` "index.js (4:14) @ eval 2 | // top offset for snapshot @@ -94,7 +94,7 @@ exports[`ReactRefreshLogBox module init error not shown 1`] = ` 7 | return

Default Export

;" `; -exports[`ReactRefreshLogBox render error not shown right after syntax error 1`] = ` +exports[`ReactRefreshLogBox default render error not shown right after syntax error 1`] = ` "index.js (6:18) @ ClassDefault.render 4 | class ClassDefault extends React.Component { @@ -106,7 +106,7 @@ exports[`ReactRefreshLogBox render error not shown right after syntax error 1`] 9 | }" `; -exports[`ReactRefreshLogBox should strip whitespace correctly with newline 1`] = ` +exports[`ReactRefreshLogBox default should strip whitespace correctly with newline 1`] = ` "index.js (9:34) @ onClick 7 | @@ -118,7 +118,7 @@ exports[`ReactRefreshLogBox should strip whitespace correctly with newline 1`] = 12 |
" `; -exports[`ReactRefreshLogBox stuck error 1`] = ` +exports[`ReactRefreshLogBox default stuck error 1`] = ` "Foo.js (4:10) @ Foo 2 | // intentionally skips export @@ -129,7 +129,7 @@ exports[`ReactRefreshLogBox stuck error 1`] = ` 6 | " `; -exports[`ReactRefreshLogBox syntax > runtime error 1`] = ` +exports[`ReactRefreshLogBox default syntax > runtime error 1`] = ` "index.js (6:16) @ Error 4 | setInterval(() => { @@ -141,7 +141,7 @@ exports[`ReactRefreshLogBox syntax > runtime error 1`] = ` 9 | return
" `; -exports[`ReactRefreshLogBox syntax > runtime error 2`] = ` +exports[`ReactRefreshLogBox default syntax > runtime error 2`] = ` "./index.js Error: x Expected '}', got '' @@ -162,7 +162,7 @@ Import trace for requested module: ./index.js" `; -exports[`ReactRefreshLogBox syntax > runtime error 3`] = ` +exports[`ReactRefreshLogBox default syntax > runtime error 3`] = ` "./index.js Error: x Expected '}', got '' @@ -183,7 +183,7 @@ Import trace for requested module: ./index.js" `; -exports[`ReactRefreshLogBox unterminated JSX 1`] = ` +exports[`ReactRefreshLogBox default unterminated JSX 1`] = ` "./index.js Error: x Unexpected token. Did you mean \`{'}'}\` or \`}\`? diff --git a/test/lib/next-test-utils.js b/test/lib/next-test-utils.js index 117d7db2a0d6e..860fdedf23818 100644 --- a/test/lib/next-test-utils.js +++ b/test/lib/next-test-utils.js @@ -940,3 +940,20 @@ export function shouldRunTurboDevTest() { // If the test path matches the glob pattern, add additional case to run the test with `--turbo` flag. return isMatch } + +// WEB-168: There are some differences / incompletes in turbopack implementation enforces jest requires to update +// test snapshot when run against turbo. This fn returns describe, or describe.skip dependes on the running context +// to avoid force-snapshot update per each runs until turbopack update includes all the changes. +export function getSnapshotTestDescribe(variant) { + const runningEnv = variant ?? 'default' + if (runningEnv !== 'default' && runningEnv !== 'turbo') { + throw new Error(`Check if test env passed correctly ${variant}`) + } + + const shouldRunTurboDev = shouldRunTurboDevTest() + const shouldSkip = + (runningEnv === 'turbo' && !shouldRunTurboDev) || + (runningEnv === 'default' && shouldRunTurboDev) + + return shouldSkip ? describe.skip : describe +}