Skip to content

Commit

Permalink
add test
Browse files Browse the repository at this point in the history
  • Loading branch information
huozhi committed Oct 10, 2024
1 parent cec30e3 commit a4af654
Show file tree
Hide file tree
Showing 12 changed files with 319 additions and 21 deletions.
6 changes: 0 additions & 6 deletions crates/next-core/src/next_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1094,12 +1094,6 @@ impl NextConfig {
Vc::cell(self.experimental.taint.unwrap_or(false))
}

pub async fn enable_dynamic_io(self: Vc<Self>) -> Result<Vc<bool>> {
Ok(Vc::cell(
self.await?.experimental.dynamic_io.unwrap_or(false),
))
}

#[turbo_tasks::function]
pub async fn enable_react_owner_stack(self: Vc<Self>) -> Result<Vc<bool>> {
Ok(Vc::cell(
Expand Down
7 changes: 2 additions & 5 deletions crates/next-core/src/next_import_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,8 @@ pub async fn get_next_client_import_map(
match ty.into_value() {
ClientContextType::Pages { .. } => {}
ClientContextType::App { app_dir } => {
let react_flavor =
if *next_config.enable_ppr().await?
let react_flavor = if *next_config.enable_ppr().await?
|| *next_config.enable_taint().await?
|| *next_config.enable_dynamic_io().await?
|| *next_config.enable_react_owner_stack().await?
{
"-experimental"
Expand Down Expand Up @@ -688,9 +686,8 @@ async fn rsc_aliases(
) -> Result<()> {
let ppr = *next_config.enable_ppr().await?;
let taint = *next_config.enable_taint().await?;
let dynamic_io = *next_config.enable_dynamic_io().await?;
let react_owner_stack = *next_config.enable_react_owner_stack().await?;
let react_channel = if ppr || taint || dynamic_io || react_owner_stack {
let react_channel = if ppr || taint || react_owner_stack {
"-experimental"
} else {
""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ export function patchConsoleError() {
const originConsoleError = window.console.error
window.console.error = (...args) => {
// See https://github.com/facebook/react/blob/d50323eb845c5fde0d720cae888bf35dedd05506/packages/react-reconciler/src/ReactFiberErrorLogger.js#L78
// const error =
// process.env.NODE_ENV !== 'production'
// ? process.env.__NEXT_REACT_OWNER_STACK
// ? args[1] // || args[0]
// : args[1]
// : args[0]
const error = process.env.NODE_ENV !== 'production' ? args[1] : args[0]
const error =
process.env.NODE_ENV !== 'production'
? process.env.__NEXT_REACT_OWNER_STACK
? args[1] // || args[0]
: args[1]
: args[0]

if (!isNextRouterError(error)) {
if (process.env.NODE_ENV !== 'production') {
Expand Down
1 change: 0 additions & 1 deletion packages/next/src/client/react-client-callbacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const reportGlobalError =
// emulating an uncaught JavaScript error.
reportError
: (error: any) => {
console.log('No report - Unhandled error:', error)
window.console.error(error)
}

Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/lib/needs-experimental-react.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { NextConfig } from '../server/config-shared'

export function needsExperimentalReact(config: NextConfig) {
const { ppr, taint, dynamicIO, reactOwnerStack } = config.experimental || {}
return Boolean(ppr || taint || dynamicIO || reactOwnerStack)
const { ppr, taint, reactOwnerStack } = config.experimental || {}
return Boolean(ppr || taint || reactOwnerStack)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use client'

import { Component } from 'react'

class MyErrorBoundary extends Component {
static getDerivedStateFromError(error) {
return { error }
}

state = { error: null }

render() {
if (this.state.error) {
return 'failed'
}
return this.props.children
}
}

function Inner() {
return (
<MyErrorBoundary>
<Thrower />
</MyErrorBoundary>
)
}

function Thrower() {
useErrorHook()
}

function useThrowError() {
if (typeof window !== 'undefined') {
throw new Error('browser error')
}
}

function useErrorHook() {
useThrowError()
}

export default function Page() {
return <Inner />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client'

function useThrowError() {
if (typeof window !== 'undefined') {
throw new Error('browser error')
}
}

function useErrorHook() {
useThrowError()
}

export default function Page() {
useErrorHook()
return <p>hello world</p>
}
7 changes: 7 additions & 0 deletions test/development/app-dir/stitching-errors/app/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Root({ children }) {
return (
<html>
<body>{children}</body>
</html>
)
}
14 changes: 14 additions & 0 deletions test/development/app-dir/stitching-errors/app/ssr/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use client'

function useThrowError() {
throw new Error('ssr error')
}

function useErrorHook() {
useThrowError()
}

export default function Page() {
useErrorHook()
return <p>hello world</p>
}
10 changes: 10 additions & 0 deletions test/development/app-dir/stitching-errors/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
experimental: {
reactOwnerStack: true,
},
}

module.exports = nextConfig
194 changes: 194 additions & 0 deletions test/development/app-dir/stitching-errors/stitching-errors.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { nextTestSetup } from 'e2e-utils'
import { assertHasRedbox, assertNoRedbox } from 'next-test-utils'

// Remove the location `()` part in every line of stack trace;
// Remove the leading spaces in every line of stack trace;
// Remove the trailing spaces in every line of stack trace;
function normalizeStackTrace(trace: string) {
return trace
.replace(/\(.*\)/g, '')
.replace(/^\s+/gm, '')
.trim()
}

async function getStackFramesContent(browser) {
const stackFrameElements = await browser.elementsByCss(
'[data-nextjs-call-stack-frame]'
)
const stackFramesContent = (
await Promise.all(stackFrameElements.map((f) => f.innerText()))
)
.filter(Boolean)
.join('\n')

return normalizeStackTrace(stackFramesContent)
}

// Remove leading spaces in every line of stack trace

describe('stitching errors', () => {
const { next } = nextTestSetup({
files: __dirname,
})

it('should log stitched error for browser uncaught errors', async () => {
const browser = await next.browser('/browser/uncaught')

await assertHasRedbox(browser)

const stackFramesContent = await getStackFramesContent(browser)
if (process.env.TURBOPACK) {
expect(stackFramesContent).toMatchInlineSnapshot(`
"useErrorHook
app/browser/uncaught/page.js
Page
app/browser/uncaught/page.js"
`)
} else {
expect(stackFramesContent).toMatchInlineSnapshot(`
"useThrowError
app/browser/uncaught/page.js
useErrorHook
app/browser/uncaught/page.js
ReactDevOverlay
../src/client/components/react-dev-overlay/app/hot-reloader-client.tsx
assetPrefix
../src/client/components/app-router.tsx
actionQueue
../src/client/components/app-router.tsx
AppRouter
../src/client/app-index.tsx"
`)
}

const logs = await browser.log()
const errorLog = logs.find((log) => {
return log.message.includes('Error: browser error')
}).message

if (process.env.TURBOPACK) {
expect(normalizeStackTrace(errorLog)).toMatchInlineSnapshot(`
"Error: browser error
at useThrowError
at useErrorHook
at Page
at react-stack-bottom-frame
at renderWithHooks
at updateFunctionComponent
at beginWork
at runWithFiberInDEV
at performUnitOfWork
at workLoopSync
at renderRootSync
at recoverFromConcurrentError
at performConcurrentWorkOnRoot
at MessagePort.performWorkUntilDeadline
The above error occurred in the <Page> component. It was handled by the <ReactDevOverlay> error boundary."
`)
} else {
expect(normalizeStackTrace(errorLog)).toMatchInlineSnapshot(`
"Error: browser error
at useThrowError
at useErrorHook
at Page
at react-stack-bottom-frame
at renderWithHooks
at updateFunctionComponent
at beginWork
at runWithFiberInDEV
at performUnitOfWork
at workLoopSync
at renderRootSync
at recoverFromConcurrentError
at performConcurrentWorkOnRoot
at MessagePort.performWorkUntilDeadline
The above error occurred in the <Page> component. It was handled by the <ReactDevOverlay> error boundary."
`)
}
})

it('should log stitched error for browser caught errors', async () => {
const browser = await next.browser('/browser/caught')

await assertNoRedbox(browser)

const logs = await browser.log()
const errorLog = logs.find((log) => {
return log.message.includes('Error: browser error')
}).message

expect(normalizeStackTrace(errorLog)).toMatchInlineSnapshot(`
"Error: browser error
at useThrowError
at useErrorHook
at Thrower
at react-stack-bottom-frame
at renderWithHooks
at updateFunctionComponent
at beginWork
at runWithFiberInDEV
at performUnitOfWork
at workLoopSync
at renderRootSync
at recoverFromConcurrentError
at performConcurrentWorkOnRoot
at MessagePort.performWorkUntilDeadline
The above error occurred in the <Thrower> component. It was handled by the <MyErrorBoundary> error boundary."
`)
})

it('should log stitched error for SSR errors', async () => {
const browser = await next.browser('/ssr')

await assertHasRedbox(browser)

const stackFramesContent = await getStackFramesContent(browser)
if (process.env.TURBOPACK) {
expect(stackFramesContent).toMatchInlineSnapshot(`
"useErrorHook
app/ssr/page.js
Page
app/ssr/page.js"
`)
} else {
expect(stackFramesContent).toMatchInlineSnapshot(`
"useThrowError
app/ssr/page.js
useErrorHook
app/ssr/page.js
ReactDevOverlay
../src/client/components/react-dev-overlay/app/hot-reloader-client.tsx
assetPrefix
../src/client/components/app-router.tsx
actionQueue
../src/client/components/app-router.tsx
AppRouter
../src/client/app-index.tsx"
`)
}

const logs = await browser.log()
const errorLog = logs.find((log) => {
return log.message.includes('Error: ssr error')
}).message

expect(normalizeStackTrace(errorLog)).toMatchInlineSnapshot(`
"Error: ssr error
at useThrowError
at useErrorHook
at Page
at react-stack-bottom-frame
at renderWithHooks
at updateFunctionComponent
at beginWork
at runWithFiberInDEV
at performUnitOfWork
at workLoopSync
at renderRootSync
at recoverFromConcurrentError
at performConcurrentWorkOnRoot
at MessagePort.performWorkUntilDeadline
The above error occurred in the <Page> component. It was handled by the <ReactDevOverlay> error boundary."
`)
})
})
24 changes: 24 additions & 0 deletions test/development/app-dir/stitching-errors/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"noEmit": true,
"incremental": true,
"module": "esnext",
"esModuleInterop": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

0 comments on commit a4af654

Please sign in to comment.