Repository: vercel/next.js
Pull request: [test] Remove `getStackFramesContent`

Can be replaced with the existing `getRedboxCallstack` reducing the number of test utils to choose from.

Changed files:
filename: test/development/acceptance/ReactRefreshLogBox.test.ts
patch: @@ -3,7 +3,7 @@ import { createSandbox } from 'development-sandbox'
 import { FileRef, nextTestSetup } from 'e2e-utils'
 import {
   getRedboxTotalErrorCount,
-  getStackFramesContent,
+  getRedboxCallStack,
   retry,
   toggleCollapseCallStackFrames,
 } from 'next-test-utils'
@@ -1435,7 +1435,7 @@ describe('ReactRefreshLogBox', () => {
     }
   })
 
-  test('should collapse nodejs internal stack frames from stack trace', async () => {
+  test('should collapse nodejs internal stack frames from stack trace by default', async () => {
     await using sandbox = await createSandbox(
       next,
       new Map([
@@ -1474,7 +1474,15 @@ describe('ReactRefreshLogBox', () => {
     `)
 
     await toggleCollapseCallStackFrames(browser)
-    const stackCollapsed = await getStackFramesContent(browser)
-    expect(stackCollapsed).toContain('at new URL ()')
+    const stackExpanded = await getRedboxCallStack(browser)
+    expect(stackExpanded).toEqual(
+      expect.arrayContaining([
+        expect.stringContaining(
+          // ignore exact location.
+          // If this breaks, choose a different error that contains Node.js internals in its stack.
+          'new URL node:internal/url ('
+        ),
+      ])
+    )
   })
 })

filename: test/development/app-dir/error-overlay/error-ignored-frames/error-ignored-frames.test.ts
patch: @@ -1,7 +1,7 @@
 import { nextTestSetup } from 'e2e-utils'
 import {
   waitForRedbox,
-  getStackFramesContent,
+  getRedboxCallStack,
   toggleCollapseCallStackFrames,
 } from 'next-test-utils'
 
@@ -14,81 +14,101 @@ describe('error-ignored-frames', () => {
     const browser = await next.browser('/')
     await waitForRedbox(browser)
 
-    const defaultStack = await getStackFramesContent(browser)
-    expect(defaultStack).toMatchInlineSnapshot(`"at Page (app/page.tsx (2:9))"`)
+    const defaultStack = await getRedboxCallStack(browser)
+    expect(defaultStack).toMatchInlineSnapshot(`
+     [
+       "Page app/page.tsx (2:9)",
+     ]
+    `)
 
     await toggleCollapseCallStackFrames(browser)
 
-    const expandedStack = await getStackFramesContent(browser)
-    const ignoreListedStack = expandedStack.replace(defaultStack, '')
+    const expandedStack = await getRedboxCallStack(browser)
+    const ignoreListedStack = expandedStack.filter(
+      (line) => !defaultStack.includes(line)
+    )
     // We don't care about the exact stack trace that was ignore-listed.
     // It'll contain implementation details that may change and
     // shouldn't break this test.
-    expect(ignoreListedStack.trim()).toMatch(/at .*/)
+    expect(ignoreListedStack).not.toHaveLength(0)
   })
 
   it('should be able to collapse ignored frames in client component', async () => {
     const browser = await next.browser('/client')
     await waitForRedbox(browser)
 
-    const defaultStack = await getStackFramesContent(browser)
-    expect(defaultStack).toMatchInlineSnapshot(
-      `"at Page (app/client/page.tsx (4:9))"`
-    )
+    const defaultStack = await getRedboxCallStack(browser)
+    expect(defaultStack).toMatchInlineSnapshot(`
+     [
+       "Page app/client/page.tsx (4:9)",
+     ]
+    `)
 
     await toggleCollapseCallStackFrames(browser)
 
-    const expandedStack = await getStackFramesContent(browser)
-    const ignoreListedStack = expandedStack.replace(defaultStack, '')
+    const expandedStack = await getRedboxCallStack(browser)
+    const ignoreListedStack = expandedStack.filter(
+      (line) => !defaultStack.includes(line)
+    )
     // We don't care about the exact stack trace that was ignore-listed.
     // It'll contain implementation details that may change and
     // shouldn't break this test.
-    expect(ignoreListedStack.trim()).toMatch(/at .*/)
+    expect(ignoreListedStack).not.toHaveLength(0)
   })
 
   it('should be able to collapse ignored frames in interleaved call stack', async () => {
     const browser = await next.browser('/interleaved')
     await waitForRedbox(browser)
 
-    const defaultStack = await getStackFramesContent(browser)
+    const defaultStack = await getRedboxCallStack(browser)
     if (isTurbopack) {
       expect(defaultStack).toMatchInlineSnapshot(`
-       "at <unknown> (app/interleaved/page.tsx (7:11))
-       at Page (app/interleaved/page.tsx (6:36))"
+       [
+         "<unknown> app/interleaved/page.tsx (7:11)",
+         "Page app/interleaved/page.tsx (6:36)",
+       ]
       `)
     } else {
       expect(defaultStack).toMatchInlineSnapshot(`
-       "at eval (app/interleaved/page.tsx (7:11))
-       at Page (app/interleaved/page.tsx (6:36))"
+       [
+         "eval app/interleaved/page.tsx (7:11)",
+         "Page app/interleaved/page.tsx (6:36)",
+       ]
       `)
     }
 
     await toggleCollapseCallStackFrames(browser)
 
-    const expandedStack = await getStackFramesContent(browser)
-    const ignoreListedStack = expandedStack.replace(defaultStack, '')
+    const expandedStack = await getRedboxCallStack(browser)
+    const ignoreListedStack = expandedStack.filter(
+      (line) => !defaultStack.includes(line)
+    )
     // We don't care about the exact stack trace that was ignore-listed.
     // It'll contain implementation details that may change and
     // shouldn't break this test.
-    expect(ignoreListedStack.trim()).toMatch(/at .*/)
+    expect(ignoreListedStack).not.toHaveLength(0)
   })
 
   it('should be able to collapse pages router ignored frames', async () => {
     const browser = await next.browser('/pages')
     await waitForRedbox(browser)
 
-    const defaultStack = await getStackFramesContent(browser)
-    expect(defaultStack).toMatchInlineSnapshot(
-      `"at Page (pages/pages.tsx (2:9))"`
-    )
+    const defaultStack = await getRedboxCallStack(browser)
+    expect(defaultStack).toMatchInlineSnapshot(`
+     [
+       "Page pages/pages.tsx (2:9)",
+     ]
+    `)
 
     await toggleCollapseCallStackFrames(browser)
 
-    const expandedStack = await getStackFramesContent(browser)
-    const ignoreListedStack = expandedStack.replace(defaultStack, '')
+    const expandedStack = await getRedboxCallStack(browser)
+    const ignoreListedStack = expandedStack.filter(
+      (line) => !defaultStack.includes(line)
+    )
     // We don't care about the exact stack trace that was ignore-listed.
     // It'll contain implementation details that may change and
     // shouldn't break this test.
-    expect(ignoreListedStack.trim()).toMatch(/at .*/)
+    expect(ignoreListedStack).not.toHaveLength(0)
   })
 })

filename: test/development/app-dir/owner-stack-react-missing-key-prop/owner-stack-react-missing-key-prop.test.ts
patch: @@ -2,7 +2,7 @@ import { nextTestSetup } from 'e2e-utils'
 import {
   getRedboxSource,
   openRedbox,
-  getStackFramesContent,
+  getRedboxCallStack,
 } from 'next-test-utils'
 
 describe('app-dir - owner-stack-react-missing-key-prop', () => {
@@ -14,15 +14,17 @@ describe('app-dir - owner-stack-react-missing-key-prop', () => {
     const browser = await next.browser('/rsc')
     await openRedbox(browser)
 
-    const stackFramesContent = await getStackFramesContent(browser)
+    const stackFramesContent = await getRedboxCallStack(browser)
     const source = await getRedboxSource(browser)
 
     if (isTurbopack) {
       expect(stackFramesContent).toMatchInlineSnapshot(`
-       "at span ()
-       at <anonymous> (app/rsc/page.tsx (7:9))
-       at Array.map ()
-       at Page (app/rsc/page.tsx (6:13))"
+       [
+         "span <anonymous>",
+         "<anonymous> app/rsc/page.tsx (7:9)",
+         "Array.map <anonymous>",
+         "Page app/rsc/page.tsx (6:13)",
+       ]
       `)
       expect(source).toMatchInlineSnapshot(`
          "app/rsc/page.tsx (7:9) @ <anonymous>
@@ -37,10 +39,12 @@ describe('app-dir - owner-stack-react-missing-key-prop', () => {
         `)
     } else {
       expect(stackFramesContent).toMatchInlineSnapshot(`
-       "at span ()
-       at eval (app/rsc/page.tsx (7:9))
-       at Array.map ()
-       at Page (app/rsc/page.tsx (6:13))"
+       [
+         "span <anonymous>",
+         "eval app/rsc/page.tsx (7:9)",
+         "Array.map <anonymous>",
+         "Page app/rsc/page.tsx (6:13)",
+       ]
       `)
       expect(source).toMatchInlineSnapshot(`
           "app/rsc/page.tsx (7:9) @ eval
@@ -60,15 +64,17 @@ describe('app-dir - owner-stack-react-missing-key-prop', () => {
     const browser = await next.browser('/ssr')
     await openRedbox(browser)
 
-    const stackFramesContent = await getStackFramesContent(browser)
+    const stackFramesContent = await getRedboxCallStack(browser)
     const source = await getRedboxSource(browser)
     if (isTurbopack) {
       expect(stackFramesContent).toMatchInlineSnapshot(`
-         "at p ()
-         at <unknown> (app/ssr/page.tsx (9:9))
-         at Array.map ()
-         at Page (app/ssr/page.tsx (8:13))"
-        `)
+       [
+         "p <anonymous>",
+         "<unknown> app/ssr/page.tsx (9:9)",
+         "Array.map <anonymous>",
+         "Page app/ssr/page.tsx (8:13)",
+       ]
+      `)
       expect(source).toMatchInlineSnapshot(`
           "app/ssr/page.tsx (9:9) @ <unknown>
 
@@ -82,11 +88,13 @@ describe('app-dir - owner-stack-react-missing-key-prop', () => {
         `)
     } else {
       expect(stackFramesContent).toMatchInlineSnapshot(`
-         "at p ()
-         at eval (app/ssr/page.tsx (9:9))
-         at Array.map ()
-         at Page (app/ssr/page.tsx (8:13))"
-        `)
+       [
+         "p <anonymous>",
+         "eval app/ssr/page.tsx (9:9)",
+         "Array.map <anonymous>",
+         "Page app/ssr/page.tsx (8:13)",
+       ]
+      `)
       expect(source).toMatchInlineSnapshot(`
           "app/ssr/page.tsx (9:9) @ eval
 

filename: test/lib/next-test-utils.ts
patch: @@ -1858,33 +1858,6 @@ export const checkLink = (
   content: string | string[]
 ) => checkMeta(browser, rel, content, 'rel', 'link', 'href')
 
-export async function getStackFramesContent(browser) {
-  const stackFrameElements = await browser.elementsByCss(
-    '[data-nextjs-call-stack-frame]'
-  )
-  const stackFramesContent = (
-    await Promise.all(
-      stackFrameElements.map(async (frame) => {
-        const functionNameEl = await frame.$('.call-stack-frame-method-name')
-        const fileEl = await frame.$('[data-has-original-code-frame="true"]')
-        const functionName = functionNameEl
-          ? await functionNameEl.innerText()
-          : ''
-        const file = fileEl ? await fileEl.innerText() : ''
-
-        if (!functionName) {
-          return ''
-        }
-        return `at ${functionName} (${file})`
-      })
-    )
-  )
-    .filter(Boolean)
-    .join('\n')
-
-  return stackFramesContent
-}
-
 export async function toggleCollapseCallStackFrames(browser: Playwright) {
   const button = await browser.elementByCss(
     '[data-nextjs-call-stack-ignored-list-toggle-button]'