Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[not for merge] Allow parens in Server stack frames #68120

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3483,7 +3483,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
proxyHandlers = {
get: function (target, name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3510,7 +3510,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
supportsRequestStorage = "function" === typeof AsyncLocalStorage,
requestStorage = supportsRequestStorage ? new AsyncLocalStorage() : null,
supportsComponentStorage = supportsRequestStorage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3533,7 +3533,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
requestStorage = new async_hooks.AsyncLocalStorage(),
componentStorage = new async_hooks.AsyncLocalStorage(),
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3499,7 +3499,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
requestStorage = new async_hooks.AsyncLocalStorage(),
componentStorage = new async_hooks.AsyncLocalStorage(),
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3115,7 +3115,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
proxyHandlers = {
get: function (target, name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3127,7 +3127,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
supportsRequestStorage = "function" === typeof AsyncLocalStorage,
requestStorage = supportsRequestStorage ? new AsyncLocalStorage() : null,
supportsComponentStorage = supportsRequestStorage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3159,7 +3159,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
requestStorage = new async_hooks.AsyncLocalStorage(),
componentStorage = new async_hooks.AsyncLocalStorage(),
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3125,7 +3125,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
requestStorage = new async_hooks.AsyncLocalStorage(),
componentStorage = new async_hooks.AsyncLocalStorage(),
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3492,7 +3492,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
proxyHandlers = {
get: function (target, name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3515,7 +3515,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
supportsRequestStorage = "function" === typeof AsyncLocalStorage,
requestStorage = supportsRequestStorage ? new AsyncLocalStorage() : null,
supportsComponentStorage = supportsRequestStorage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3538,7 +3538,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
requestStorage = new async_hooks.AsyncLocalStorage(),
componentStorage = new async_hooks.AsyncLocalStorage(),
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3503,7 +3503,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
requestStorage = new async_hooks.AsyncLocalStorage(),
componentStorage = new async_hooks.AsyncLocalStorage(),
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3124,7 +3124,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
proxyHandlers = {
get: function (target, name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3132,7 +3132,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
supportsRequestStorage = "function" === typeof AsyncLocalStorage,
requestStorage = supportsRequestStorage ? new AsyncLocalStorage() : null,
supportsComponentStorage = supportsRequestStorage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3164,7 +3164,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
requestStorage = new async_hooks.AsyncLocalStorage(),
componentStorage = new async_hooks.AsyncLocalStorage(),
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3129,7 +3129,7 @@
}
};
var frameRegExp =
/^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|(?:async )?([^\)]+):(\d+):(\d+))$/,
/^ {3} at (?:(.+) \((.+):(\d+):(\d+)\)|(?:async )?(.+):(\d+):(\d+))$/,
requestStorage = new async_hooks.AsyncLocalStorage(),
componentStorage = new async_hooks.AsyncLocalStorage(),
TEMPORARY_REFERENCE_TAG = Symbol.for("react.temporary.reference"),
Expand Down
160 changes: 106 additions & 54 deletions test/development/acceptance-app/ReactRefreshLogBox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -777,39 +777,24 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
await cleanup()
})

test.each([
[
'client',
new Map([
[
'app/page.js',
outdent`
'use client'
export default function Page() {
if (typeof window !== 'undefined') {
throw new Error('Client error')
}
return null
}
`,
],
]),
],
[
'server',
test('Call stack count for client error', async () => {
const { session, browser, cleanup } = await sandbox(
next,
new Map([
[
'app/page.js',
outdent`
export default function Page() {
throw new Error('Server error')
}
`,
'use client'
export default function Page() {
if (typeof window !== 'undefined') {
throw new Error('Client error')
}
return null
}
`,
],
]),
],
])('Call stack count is correct for %s error', async (_, fixture) => {
const { session, browser, cleanup } = await sandbox(next, fixture)
])
)

await session.assertHasRedbox()

Expand All @@ -832,6 +817,47 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
await cleanup()
})

test('Call stack for server error', async () => {
const { session, browser, cleanup } = await sandbox(
next,
new Map([
[
'app/page.js',
outdent`
export default function Page() {
throw new Error('Server error')
}
`,
],
])
)

try {
await session.assertHasRedbox()

// Should still show the errored line in source code
const source = await session.getRedboxSource()
expect(source).toContain('app/page.js')
expect(source).toContain(`throw new Error('Server error')`)

await expect(
browser.hasElementByCssSelector(
'[data-nextjs-data-runtime-error-collapsed-action]'
)
).resolves.toEqual(false)

const stackFrameElements = await browser.elementsByCss(
'[data-nextjs-call-stack-frame]'
)
const stackFrames = await Promise.all(
stackFrameElements.map((f) => f.innerText())
)
expect(stackFrames).toEqual([])
} finally {
await cleanup()
}
})

test('should hide unrelated frames in stack trace with unknown anonymous calls', async () => {
const { session, browser, cleanup } = await sandbox(
next,
Expand All @@ -852,18 +878,38 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
],
])
)
await session.assertHasRedbox()
await expandCallStack(browser)
let callStackFrames = await browser.elementsByCss(
'[data-nextjs-call-stack-frame]'
)
const text = (
await Promise.all(callStackFrames.map((f) => f.innerText()))
).join('')
expect(text).not.toContain('<anonymous>')
expect(text).toContain('app/page.js')

await cleanup()
try {
await session.assertHasRedbox()

// Should still show the errored line in source code
const source = await session.getRedboxSource()
expect(source).toContain('app/page.js')
expect(source).toContain(
`throw new Error("This is an error from an anonymous function")`
)

await expect(
browser.hasElementByCssSelector(
'[data-nextjs-data-runtime-error-collapsed-action]'
)
).resolves.toEqual(false)

const stackFrameElements = await browser.elementsByCss(
'[data-nextjs-call-stack-frame]'
)
const stackFrames = await Promise.all(
stackFrameElements.map((f) => f.innerText())
)
expect(stackFrames).toEqual([
outdent`
Page
app/page.js (5:5)
`,
])
} finally {
await cleanup()
}
})

test('should hide unrelated frames in stack trace with nodejs internal calls', async () => {
Expand All @@ -881,24 +927,30 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
])
)

await session.assertHasRedbox()
await expandCallStack(browser)

// Should still show the errored line in source code
const source = await session.getRedboxSource()
expect(source).toContain('app/page.js')
expect(source).toContain(`new URL("/", "invalid")`)
try {
await session.assertHasRedbox()

await expandCallStack(browser)
const callStackFrames = await browser.elementsByCss(
'[data-nextjs-call-stack-frame]'
)
const texts = await Promise.all(callStackFrames.map((f) => f.innerText()))
// Should still show the errored line in source code
const source = await session.getRedboxSource()
expect(source).toContain('app/page.js')
expect(source).toContain(`new URL("/", "invalid")`)

expect(texts.filter((t) => t.includes('node:internal'))).toHaveLength(0)
expect(texts.filter((t) => t.includes('node:async_hooks'))).toHaveLength(0)
await expect(
browser.hasElementByCssSelector(
'[data-nextjs-data-runtime-error-collapsed-action]'
)
).resolves.toEqual(false)

await cleanup()
const stackFrameElements = await browser.elementsByCss(
'[data-nextjs-call-stack-frame]'
)
const stackFrames = await Promise.all(
stackFrameElements.map((f) => f.innerText())
)
expect(stackFrames).toEqual([])
} finally {
await cleanup()
}
})

test('Server component errors should open up in fullscreen', async () => {
Expand Down
28 changes: 17 additions & 11 deletions test/development/app-dir/dynamic-error-trace/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { nextTestSetup } from 'e2e-utils'
import {
assertHasRedbox,
getRedboxCallStack,
shouldRunTurboDevTest,
expandCallStack,
getRedboxSource,
} from 'next-test-utils'

const isPPREnabled = process.env.__NEXT_EXPERIMENTAL_PPR === 'true'
const gateNotReactExperimental = isPPREnabled ? it.failing : it

describe('app dir - dynamic error trace', () => {
const { next, skipped } = nextTestSetup({
files: __dirname,
Expand All @@ -30,17 +25,28 @@ describe('app dir - dynamic error trace', () => {
})
if (skipped) return

gateNotReactExperimental('should show the error trace', async () => {
it('should show the error trace', async () => {
const browser = await next.browser('/')

await assertHasRedbox(browser)
await expandCallStack(browser)
const callStack = await getRedboxCallStack(browser)

// eslint-disable-next-line jest/no-standalone-expect -- this is a test
expect(callStack).toContain('node_modules/headers-lib/index.mjs')
await expect(
browser.hasElementByCssSelector(
'[data-nextjs-data-runtime-error-collapsed-action]'
)
).resolves.toEqual(false)

const stackFrameElements = await browser.elementsByCss(
'[data-nextjs-call-stack-frame]'
)
const stackFrames = await Promise.all(
stackFrameElements.map((f) => f.innerText())
)
expect(stackFrames).toEqual([])

const source = await getRedboxSource(browser)
// eslint-disable-next-line jest/no-standalone-expect -- this is a test
expect(source).toContain('app/lib.js')
expect(source).toContain('app/lib.js (4:13) @ useHeaders')
expect(source).toContain(`useHeaders()`)
})
})
Loading
Loading