Files
Arian Tron 61f56f997c
Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled
first commit
2026-03-10 19:37:31 +03:30

644 lines
20 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { waitForNoRedbox, check, getDistDir, retry } from 'next-test-utils'
import stripAnsi from 'strip-ansi'
import { nextTestSetup } from 'e2e-utils'
describe('middleware - development errors', () => {
const { next, isTurbopack, isRspack } = nextTestSetup({
files: __dirname,
patchFileDelay: 500,
})
beforeEach(async () => {
await next.stop()
})
describe('when middleware throws synchronously', () => {
beforeEach(async () => {
await next.patchFile(
'middleware.js',
`
export default function () {
throw new Error('boom')
}`
)
await next.start()
})
it('logs the error correctly', async () => {
await next.fetch('/')
await retry(() => {
expect(stripAnsi(next.cliOutput)).toContain('boom')
})
expect(stripAnsi(next.cliOutput)).toContain(
isTurbopack
? '\n Error: boom' +
// TODO(veil): Sourcemap to original name i.e. "default"
'\n at __TURBOPACK__default__export__ (middleware.js:3:15)' +
'\n 1 |'
: isRspack
? '\n Error: boom' +
'\n at __rspack_default_export (middleware.js:3:15)' +
'\n 1 |'
: '\n Error: boom' +
'\n at default (middleware.js:3:15)' +
'\n 1 |'
)
expect(stripAnsi(next.cliOutput)).toContain(
'' +
"\n> 3 | throw new Error('boom')" +
'\n | ^'
)
})
it('renders the error correctly and recovers', async () => {
const browser = await next.browser('/')
if (isTurbopack) {
await expect(browser).toDisplayRedbox(`
{
"code": "E394",
"description": "boom",
"environmentLabel": null,
"label": "Runtime Error",
"source": "middleware.js (3:15) @ {default export}
> 3 | throw new Error('boom')
| ^",
"stack": [
"{default export} middleware.js (3:15)",
],
}
`)
} else if (isRspack) {
await expect(browser).toDisplayRedbox(`
{
"code": "E394",
"description": "boom",
"environmentLabel": null,
"label": "Runtime Error",
"source": "middleware.js (3:15) @ __rspack_default_export
> 3 | throw new Error('boom')
| ^",
"stack": [
"__rspack_default_export middleware.js (3:15)",
],
}
`)
} else {
await expect(browser).toDisplayRedbox(`
{
"code": "E394",
"description": "boom",
"environmentLabel": null,
"label": "Runtime Error",
"source": "middleware.js (3:15) @ default
> 3 | throw new Error('boom')
| ^",
"stack": [
"default middleware.js (3:15)",
],
}
`)
}
await next.patchFile('middleware.js', `export default function () {}`)
await waitForNoRedbox(browser)
})
})
describe('when middleware contains an unhandled rejection', () => {
beforeEach(async () => {
await next.patchFile(
'middleware.js',
`
import { NextResponse } from 'next/server'
async function throwError() {
throw new Error('async boom!')
}
export default function () {
throwError()
return NextResponse.next()
}`
)
await next.start()
})
it('logs the error correctly', async () => {
await next.fetch('/')
await retry(() => {
expect(stripAnsi(next.cliOutput)).toContain(
'unhandledRejection: Error: async boom!'
)
})
expect(stripAnsi(next.cliOutput)).toContain(
isTurbopack
? ' unhandledRejection: Error: async boom!' +
'\n at throwError (middleware.js:4:15)' +
// TODO(veil): Sourcemap to original name i.e. "default"
'\n at __TURBOPACK__default__export__ (middleware.js:7:9)' +
"\n 2 | import { NextResponse } from 'next/server'"
: isRspack
? '\n unhandledRejection: Error: async boom!' +
'\n at throwError (middleware.js:4:15)' +
'\n at __rspack_default_export (middleware.js:7:9)' +
"\n 2 | import { NextResponse } from 'next/server'"
: '\n unhandledRejection: Error: async boom!' +
'\n at throwError (middleware.js:4:15)' +
'\n at default (middleware.js:7:9)' +
"\n 2 | import { NextResponse } from 'next/server'"
)
expect(stripAnsi(next.cliOutput)).toContain(
'' +
"\n> 4 | throw new Error('async boom!')" +
'\n | ^'
)
})
it('does not render the error', async () => {
const browser = await next.browser('/')
await waitForNoRedbox(browser)
expect(await browser.elementByCss('#page-title')).toBeTruthy()
})
})
describe('when running invalid dynamic code with eval', () => {
beforeEach(async () => {
await next.patchFile(
'middleware.js',
`
import { NextResponse } from 'next/server'
export default function () {
eval('test')
return NextResponse.next()
}`
)
await next.start()
})
it('logs the error correctly', async () => {
await next.fetch('/')
await retry(() => {
expect(stripAnsi(next.cliOutput)).toContain('Dynamic Code Evaluation')
})
if (isTurbopack) {
// Locally, prefixes the "test is not defined".
// In CI, it prefixes "Dynamic Code Evaluation".
expect(stripAnsi(next.cliOutput)).toContain(
// TODO(veil): Should be sourcemapped
'\n at __TURBOPACK__default__export__ (.next/'
)
}
expect(stripAnsi(next.cliOutput)).toContain(
isTurbopack
? '\n Error [ReferenceError]: test is not defined' +
'\n at eval (middleware.js:4:9)' +
'\n at <unknown> (middleware.js:4:9)' +
// TODO(veil): Should be sourcemapped
'\n at __TURBOPACK__default__export__ ('
: '\n Error [ReferenceError]: test is not defined' +
// TODO(veil): Redundant and not clickable
'\n at eval (file://webpack-internal:///(middleware)/./middleware.js)' +
'\n at eval (middleware.js:4:9)' +
'\n at default (middleware.js:4:9)' +
"\n 2 | import { NextResponse } from 'next/server'"
)
expect(stripAnsi(next.cliOutput)).toContain(
isTurbopack
? "\n⚠ DynamicCodeEvaluationWarning: Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime" +
'\nLearn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation' +
// TODO(veil): Should be sourcemapped
'\n at __TURBOPACK__default__export__ ('
: "\n⚠ DynamicCodeEvaluationWarning: Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime" +
'\nLearn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation' +
'\n at default (middleware.js:4:9)' +
"\n 2 | import { NextResponse } from 'next/server'"
)
})
it('renders the error correctly and recovers', async () => {
const browser = await next.browser('/')
if (isTurbopack) {
await expect(browser).toDisplayRedbox(`
{
"code": "E394",
"description": "test is not defined",
"environmentLabel": null,
"label": "Runtime ReferenceError",
"source": "middleware.js (4:9) @ eval
> 4 | eval('test')
| ^",
"stack": [
"eval middleware.js (4:9)",
"<unknown> middleware.js (4:9)",
"{default export} middleware.js (3:22)",
],
}
`)
} else if (isRspack) {
await expect(browser).toDisplayRedbox(`
{
"code": "E394",
"description": "test is not defined",
"environmentLabel": null,
"label": "Runtime ReferenceError",
"source": "middleware.js (4:9) @ __rspack_default_export
> 4 | eval('test')
| ^",
"stack": [
"<FIXME-file-protocol>",
"__rspack_default_export middleware.js (4:9)",
],
}
`)
} else {
await expect(browser).toDisplayRedbox(`
{
"code": "E394",
"description": "test is not defined",
"environmentLabel": null,
"label": "Runtime ReferenceError",
"source": "middleware.js (4:9) @ eval
> 4 | eval('test')
| ^",
"stack": [
"<FIXME-file-protocol>",
"eval middleware.js (4:9)",
"default middleware.js (4:9)",
],
}
`)
}
const lengthOfLogs = next.cliOutput.length
await next.patchFile('middleware.js', `export default function () {}`)
retry(() => {
expect(next.cliOutput.slice(lengthOfLogs)).toContain('GET / 200')
}, 10000) // middleware rebuild takes a while in CI
await waitForNoRedbox(browser)
})
})
describe('when throwing while loading the module', () => {
beforeEach(async () => {
await next.patchFile(
'middleware.js',
`
import { NextResponse } from 'next/server'
throw new Error('booooom!')
export default function () {
return NextResponse.next()
}`
)
await next.start()
})
it('logs the error correctly', async () => {
await next.fetch('/')
await retry(() => {
expect(stripAnsi(next.cliOutput)).toContain(`Error: booooom!`)
})
expect(stripAnsi(next.cliOutput)).toContain(
isTurbopack
? '\n Error: booooom!' +
// TODO(veil): Should be sourcemapped
'\n at module evaluation (middleware.js:3:13)'
: isRspack
? '\n Error: booooom!' +
`\n at <unknown> (${getDistDir()}/server/edge-runtime-webpack.js:35)` +
'\n at eval (middleware.js:3:13)' +
`\n at (middleware)/./middleware.js (${getDistDir()}/server/middleware.js:26:1)` +
'\n at __webpack_require__ '
: '\n Error: booooom!' +
// TODO: Should be anonymous method without a method name
'\n at <unknown> (middleware.js:3)' +
// TODO: Should be ignore-listed
'\n at eval (middleware.js:3:13)' +
`\n at (middleware)/./middleware.js (${getDistDir()}/server/middleware.js:18:1)` +
'\n at __webpack_require__ '
)
})
it('renders the error correctly and recovers', async () => {
const browser = await next.browser('/')
if (isTurbopack) {
await expect(browser).toDisplayRedbox(`
{
"code": "E394",
"description": "booooom!",
"environmentLabel": null,
"label": "Runtime Error",
"source": "middleware.js (3:13) @ module evaluation
> 3 | throw new Error('booooom!')
| ^",
"stack": [
"module evaluation middleware.js (3:13)",
],
}
`)
} else if (isRspack) {
await expect(browser).toDisplayRedbox(`
{
"code": "E394",
"description": "booooom!",
"environmentLabel": null,
"label": "Runtime Error",
"source": "middleware.js (3:13) @ eval
> 3 | throw new Error('booooom!')
| ^",
"stack": [
"<FIXME-next-dist-dir>",
"eval middleware.js (3:13)",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
],
}
`)
} else {
await expect(browser).toDisplayRedbox(`
{
"code": "E394",
"description": "booooom!",
"environmentLabel": null,
"label": "Runtime Error",
"source": "middleware.js (3:13) @ eval
> 3 | throw new Error('booooom!')
| ^",
"stack": [
"<unknown> middleware.js (3)",
"eval middleware.js (3:13)",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
"<FIXME-next-dist-dir>",
],
}
`)
}
await next.patchFile('middleware.js', `export default function () {}`)
await waitForNoRedbox(browser)
})
})
describe('when there is an unhandled rejection while loading the module', () => {
beforeEach(async () => {
await next.patchFile(
'middleware.js',
`
import { NextResponse } from 'next/server'
(async function(){
throw new Error('you shall see me')
})()
export default function () {
return NextResponse.next()
}`
)
await next.start()
})
it('logs the error correctly', async () => {
await next.fetch('/')
await check(
() => stripAnsi(next.cliOutput),
new RegExp(`unhandledRejection: Error: you shall see me`, 'm')
)
// expect(output).not.toContain(
// 'webpack-internal:///(middleware)/./middleware.js'
// )
})
it('does not render the error', async () => {
const browser = await next.browser('/')
await waitForNoRedbox(browser)
expect(await browser.elementByCss('#page-title')).toBeTruthy()
})
})
describe('when there is an unhandled rejection while loading a dependency', () => {
beforeEach(async () => {
await next.patchFile(
'middleware.js',
`
import { NextResponse } from 'next/server'
import './lib/unhandled'
export default function () {
return NextResponse.next()
}`
)
await next.start()
})
it('logs the error correctly', async () => {
await next.fetch('/')
const output = stripAnsi(next.cliOutput)
await check(
() => stripAnsi(next.cliOutput),
new RegExp(
` uncaughtException: Error: This file asynchronously fails while loading`,
'm'
)
)
expect(output).not.toContain(
'webpack-internal:///(middleware)/./middleware.js'
)
})
it('does not render the error', async () => {
const browser = await next.browser('/')
await waitForNoRedbox(browser)
expect(await browser.elementByCss('#page-title')).toBeTruthy()
})
})
describe('when there is a compilation error from boot', () => {
beforeEach(async () => {
await next.patchFile('middleware.js', `export default function () }`)
await next.start()
})
it('logs the error correctly', async () => {
await next.fetch('/')
await check(async () => {
expect(next.cliOutput).toContain(`Expected '{', got '}'`)
expect(
next.cliOutput.split(`Expected '{', got '}'`).length
).toBeGreaterThanOrEqual(2)
return 'success'
}, 'success')
})
it('renders the error correctly and recovers', async () => {
const browser = await next.browser('/')
if (isTurbopack) {
await expect(browser).toDisplayRedbox(`
{
"description": "Expected '{', got '}'",
"environmentLabel": null,
"label": "Build Error",
"source": "./middleware.js (1:28)
Expected '{', got '}'
> 1 | export default function () }
| ^",
"stack": [],
}
`)
} else if (isRspack) {
await expect(browser).toDisplayRedbox(`
{
"description": " ╰─▶ × Error: x Expected '{', got '}'",
"environmentLabel": null,
"label": "Build Error",
"source": "./middleware.js
╰─▶ × Error: x Expected '{', got '}'
│ ,----
│ 1 | export default function () }
│ : ^
\`----
│ Caused by:
│ Syntax Error",
"stack": [],
}
`)
} else {
await expect(browser).toDisplayRedbox(`
{
"description": " x Expected '{', got '}'",
"environmentLabel": null,
"label": "Build Error",
"source": "./middleware.js
Error: x Expected '{', got '}'
,----
1 | export default function () }
: ^
\`----
Caused by:
Syntax Error",
"stack": [],
}
`)
}
await next.patchFile('middleware.js', `export default function () {}`)
await waitForNoRedbox(browser)
expect(await browser.elementByCss('#page-title')).toBeTruthy()
})
})
describe('when there is a compilation error after boot', () => {
beforeEach(async () => {
await next.patchFile('middleware.js', `export default function () {}`)
await next.start()
})
it('logs the error correctly', async () => {
await next.patchFile('middleware.js', `export default function () }`)
await next.fetch('/')
await check(() => {
expect(next.cliOutput).toContain(`Expected '{', got '}'`)
expect(
next.cliOutput.split(`Expected '{', got '}'`).length
).toBeGreaterThanOrEqual(2)
return 'success'
}, 'success')
})
it('renders the error correctly and recovers', async () => {
const browser = await next.browser('/')
await waitForNoRedbox(browser)
await next.patchFile('middleware.js', `export default function () }`)
if (isTurbopack) {
await expect(browser).toDisplayRedbox(`
{
"description": "Expected '{', got '}'",
"environmentLabel": null,
"label": "Build Error",
"source": "./middleware.js (1:28)
Expected '{', got '}'
> 1 | export default function () }
| ^",
"stack": [],
}
`)
} else if (isRspack) {
await expect(browser).toDisplayRedbox(`
{
"description": " ╰─▶ × Error: x Expected '{', got '}'",
"environmentLabel": null,
"label": "Build Error",
"source": "./middleware.js
╰─▶ × Error: x Expected '{', got '}'
│ ,----
│ 1 | export default function () }
│ : ^
\`----
│ Caused by:
│ Syntax Error",
"stack": [],
}
`)
} else {
await expect(browser).toDisplayRedbox(`
{
"description": " x Expected '{', got '}'",
"environmentLabel": null,
"label": "Build Error",
"source": "./middleware.js
Error: x Expected '{', got '}'
,----
1 | export default function () }
: ^
\`----
Caused by:
Syntax Error",
"stack": [],
}
`)
}
await next.patchFile('middleware.js', `export default function () {}`)
await waitForNoRedbox(browser)
expect(await browser.elementByCss('#page-title')).toBeTruthy()
})
})
})