Skip to content

Commit 405e7f7

Browse files
authored
chore(webkit): update error stack parsing and related system tests (#23730)
* chore(webkit): update error stack parsing and related system tests * Adding better comment * Putting column back. Indexing at 1. * Let's wait for WebKit fixes to land for this * Using default name/location values already used in stack enhancing logic * Incorrect bracket in regex * Trying without location, as the fake location causes more problems downstream. * Loosening regex around locations in stack replacement * Defaulting location sans row/column * Parsing stack lines for reporter with unique regex * D'oh * Making the validation against '<unknown>' more specific * Don't want a capture group here * Updating spec_isolation system tests * Consolidating regex pattern to errors package * Can just keep this global now * Simplifying regex. Removing lineAndColumn numbers from unknown locations. * Updating system test stack regex * Getting better baseline * Revert "Updating system test stack regex" This reverts commit 2b91eff. * Forking normalization for webkit to track down diffs * Ensure line or column are set before rendering in enhanced stack * Need to be a little more flexible * Tweaking leading newline detection * Trying out new composed regex * Few more tweaks for multiple leading newlines and file locations without function name * Updating remainderOfStack pattern with proper escaping * Cleaning up comments * Filtering native code from absolute path logic * Rebuild CI after outage
1 parent 282e6c0 commit 405e7f7

20 files changed

+1023
-50
lines changed

packages/driver/src/cypress/stack_utils.ts

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import $utils from './utils'
88
import $sourceMapUtils from './source_map_utils'
99

1010
// Intentionally deep-importing from @packages/errors so as to not bundle the entire @packages/errors in the client unnecessarily
11-
import { getStackLines, replacedStack, stackWithoutMessage, splitStack, unsplitStack } from '@packages/errors/src/stackUtils'
11+
import { getStackLines, replacedStack, stackWithoutMessage, splitStack, unsplitStack, stackLineRegex } from '@packages/errors/src/stackUtils'
1212

1313
const whitespaceRegex = /^(\s*)*/
14-
const stackLineRegex = /^\s*(at )?.*@?\(?.*\:\d+\:\d+\)?$/
1514
const customProtocolRegex = /^[^:\/]+:\/{1,3}/
1615
const percentNotEncodedRegex = /%(?![0-9A-F][0-9A-F])/g
16+
const webkitStackLineRegex = /(.*)@(.*)(\n?)/g
17+
1718
const STACK_REPLACEMENT_MARKER = '__stackReplacementMarker'
1819

1920
const hasCrossFrameStacks = (specWindow) => {
@@ -244,9 +245,7 @@ const cleanFunctionName = (functionName) => {
244245
}
245246

246247
const parseLine = (line) => {
247-
const isStackLine = stackLineRegex.test(line)
248-
249-
if (!isStackLine) return
248+
if (!stackLineRegex.test(line)) return
250249

251250
const parsed = errorStackParser.parse({ stack: line } as any)[0]
252251

@@ -318,7 +317,14 @@ const getSourceDetailsForLine = (projectRoot, line): LineDetail => {
318317

319318
let absoluteFile
320319

321-
if (relativeFile && projectRoot) {
320+
// WebKit stacks may include an `<unknown>` or `[native code]` location that is not navigable.
321+
// We ensure that the absolute path is not set in this case.
322+
323+
const canBuildAbsolutePath = relativeFile && projectRoot && (
324+
!Cypress.isBrowser('webkit') || (relativeFile !== '<unknown>' && relativeFile !== '[native code]')
325+
)
326+
327+
if (canBuildAbsolutePath) {
322328
absoluteFile = path.resolve(projectRoot, relativeFile)
323329

324330
// rollup-plugin-node-builtins/src/es6/path.js only support POSIX, we have
@@ -356,7 +362,9 @@ const reconstructStack = (parsedStack) => {
356362

357363
const { whitespace, originalFile, function: fn, line, column } = parsedLine
358364

359-
return `${whitespace}at ${fn} (${originalFile || '<unknown>'}:${line}:${column})`
365+
const lineAndColumn = (Number.isInteger(line) || Number.isInteger(column)) ? `:${line}:${column}` : ''
366+
367+
return `${whitespace}at ${fn} (${originalFile || '<unknown>'}${lineAndColumn})`
360368
}).join('\n').trimEnd()
361369
}
362370

@@ -392,7 +400,35 @@ const normalizedStack = (err) => {
392400
// Chromium-based errors do, so we normalize them so that the stack
393401
// always includes the name/message
394402
const errString = err.toString()
395-
const errStack = err.stack || ''
403+
let errStack = err.stack || ''
404+
405+
if (Cypress.isBrowser('webkit')) {
406+
// WebKit will not determine the proper stack trace for an error, with stack entries
407+
// missing function names, call locations, or both. This is due to a number of documented
408+
// issues with WebKit:
409+
// https://bugs.webkit.org/show_bug.cgi?id=86493
410+
// https://bugs.webkit.org/show_bug.cgi?id=243668
411+
// https://bugs.webkit.org/show_bug.cgi?id=174380
412+
//
413+
// We update these stack entries with placeholder names/locations to more closely align
414+
// the output with other browsers, minimizing the visual impact to the stack traces we render
415+
// within the command log and console and ensuring that the stacks can be identified within
416+
// and parsed out of test snapshots that include them.
417+
errStack = errStack.replaceAll(webkitStackLineRegex, (match, ...parts: string[]) => {
418+
// We patch WebKit's Error within the AUT as CyWebKitError, causing it to
419+
// be presented within the stack. If we detect it within the stack, we remove it.
420+
if (parts[0] === '__CyWebKitError') {
421+
return ''
422+
}
423+
424+
return [
425+
parts[0] || '<unknown>',
426+
'@',
427+
parts[1] || '<unknown>',
428+
parts[2],
429+
].join('')
430+
})
431+
}
396432

397433
// the stack has already been normalized and normalizing the indentation
398434
// again could mess up the whitespace

packages/errors/src/stackUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import _ from 'lodash'
22
import type { ErrorLike } from './errorTypes'
33

4-
const stackLineRegex = /^\s*(at )?.*@?\(?.*\:\d+\:\d+\)?$/
4+
export const stackLineRegex = /^\s*(at )?.*@?(?:\(?.*(?::\d+:\d+|<unknown>|\[native code\])+\)?)$/
55

66
type MessageLines = [string[], string[]] & {messageEnded?: boolean}
77

packages/reporter/src/errors/error-stack.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ const ErrorStack = observer(({ err }: Props) => {
7676
)
7777

7878
if (dontLink) {
79-
return makeLine(key, [whitespace, `at ${fn} (${originalFile}:${line}:${column})`])
79+
const lineAndColumn = (Number.isInteger(line) || Number.isInteger(column)) ? `:${line}:${column}` : ''
80+
81+
return makeLine(key, [whitespace, `at ${fn} (${originalFile}${lineAndColumn})`])
8082
}
8183

8284
const link = (

0 commit comments

Comments
 (0)