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 2b91eff369.

* 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
This commit is contained in:
Tyler Biethman
2022-09-12 15:40:12 -05:00
committed by GitHub
parent 282e6c07b3
commit 405e7f7ccb
20 changed files with 1022 additions and 49 deletions
+44 -8
View File
@@ -8,12 +8,13 @@ import $utils from './utils'
import $sourceMapUtils from './source_map_utils'
// Intentionally deep-importing from @packages/errors so as to not bundle the entire @packages/errors in the client unnecessarily
import { getStackLines, replacedStack, stackWithoutMessage, splitStack, unsplitStack } from '@packages/errors/src/stackUtils'
import { getStackLines, replacedStack, stackWithoutMessage, splitStack, unsplitStack, stackLineRegex } from '@packages/errors/src/stackUtils'
const whitespaceRegex = /^(\s*)*/
const stackLineRegex = /^\s*(at )?.*@?\(?.*\:\d+\:\d+\)?$/
const customProtocolRegex = /^[^:\/]+:\/{1,3}/
const percentNotEncodedRegex = /%(?![0-9A-F][0-9A-F])/g
const webkitStackLineRegex = /(.*)@(.*)(\n?)/g
const STACK_REPLACEMENT_MARKER = '__stackReplacementMarker'
const hasCrossFrameStacks = (specWindow) => {
@@ -244,9 +245,7 @@ const cleanFunctionName = (functionName) => {
}
const parseLine = (line) => {
const isStackLine = stackLineRegex.test(line)
if (!isStackLine) return
if (!stackLineRegex.test(line)) return
const parsed = errorStackParser.parse({ stack: line } as any)[0]
@@ -318,7 +317,14 @@ const getSourceDetailsForLine = (projectRoot, line): LineDetail => {
let absoluteFile
if (relativeFile && projectRoot) {
// WebKit stacks may include an `<unknown>` or `[native code]` location that is not navigable.
// We ensure that the absolute path is not set in this case.
const canBuildAbsolutePath = relativeFile && projectRoot && (
!Cypress.isBrowser('webkit') || (relativeFile !== '<unknown>' && relativeFile !== '[native code]')
)
if (canBuildAbsolutePath) {
absoluteFile = path.resolve(projectRoot, relativeFile)
// rollup-plugin-node-builtins/src/es6/path.js only support POSIX, we have
@@ -356,7 +362,9 @@ const reconstructStack = (parsedStack) => {
const { whitespace, originalFile, function: fn, line, column } = parsedLine
return `${whitespace}at ${fn} (${originalFile || '<unknown>'}:${line}:${column})`
const lineAndColumn = (Number.isInteger(line) || Number.isInteger(column)) ? `:${line}:${column}` : ''
return `${whitespace}at ${fn} (${originalFile || '<unknown>'}${lineAndColumn})`
}).join('\n').trimEnd()
}
@@ -392,7 +400,35 @@ const normalizedStack = (err) => {
// Chromium-based errors do, so we normalize them so that the stack
// always includes the name/message
const errString = err.toString()
const errStack = err.stack || ''
let errStack = err.stack || ''
if (Cypress.isBrowser('webkit')) {
// WebKit will not determine the proper stack trace for an error, with stack entries
// missing function names, call locations, or both. This is due to a number of documented
// issues with WebKit:
// https://bugs.webkit.org/show_bug.cgi?id=86493
// https://bugs.webkit.org/show_bug.cgi?id=243668
// https://bugs.webkit.org/show_bug.cgi?id=174380
//
// We update these stack entries with placeholder names/locations to more closely align
// the output with other browsers, minimizing the visual impact to the stack traces we render
// within the command log and console and ensuring that the stacks can be identified within
// and parsed out of test snapshots that include them.
errStack = errStack.replaceAll(webkitStackLineRegex, (match, ...parts: string[]) => {
// We patch WebKit's Error within the AUT as CyWebKitError, causing it to
// be presented within the stack. If we detect it within the stack, we remove it.
if (parts[0] === '__CyWebKitError') {
return ''
}
return [
parts[0] || '<unknown>',
'@',
parts[1] || '<unknown>',
parts[2],
].join('')
})
}
// the stack has already been normalized and normalizing the indentation
// again could mess up the whitespace
+1 -1
View File
@@ -1,7 +1,7 @@
import _ from 'lodash'
import type { ErrorLike } from './errorTypes'
const stackLineRegex = /^\s*(at )?.*@?\(?.*\:\d+\:\d+\)?$/
export const stackLineRegex = /^\s*(at )?.*@?(?:\(?.*(?::\d+:\d+|<unknown>|\[native code\])+\)?)$/
type MessageLines = [string[], string[]] & {messageEnded?: boolean}
+3 -1
View File
@@ -76,7 +76,9 @@ const ErrorStack = observer(({ err }: Props) => {
)
if (dontLink) {
return makeLine(key, [whitespace, `at ${fn} (${originalFile}:${line}:${column})`])
const lineAndColumn = (Number.isInteger(line) || Number.isInteger(column)) ? `:${line}:${column}` : ''
return makeLine(key, [whitespace, `at ${fn} (${originalFile}${lineAndColumn})`])
}
const link = (
@@ -3271,3 +3271,934 @@ exports['e2e spec_isolation failing with retries enabled [firefox] 1'] = {
"config": {},
"status": "finished"
}
exports['e2e spec_isolation failing with retries enabled [webkit] 1'] = {
"status": "finished",
"startedTestsAt": "2018-02-01T20:14:19.323Z",
"endedTestsAt": "2018-02-01T20:14:19.323Z",
"totalDuration": 5555,
"totalSuites": 6,
"totalTests": 8,
"totalPassed": 2,
"totalPending": 1,
"totalFailed": 4,
"totalSkipped": 1,
"runs": [
{
"stats": {
"suites": 5,
"tests": 6,
"passes": 1,
"pending": 1,
"skipped": 1,
"failures": 3,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"endedAt": "2018-02-01T20:14:19.323Z"
},
"reporter": "spec",
"reporterStats": {
"suites": 5,
"tests": 5,
"passes": 1,
"pending": 1,
"failures": 3,
"start": "2018-02-01T20:14:19.323Z",
"end": "2018-02-01T20:14:19.323Z",
"duration": 1234
},
"hooks": [
{
"hookName": "before each",
"title": [
"\"before each\" hook"
],
"body": "() => {\n throw new Error('fail1');\n }"
},
{
"hookName": "after each",
"title": [
"\"after each\" hook"
],
"body": "() => {\n throw new Error('fail2');\n }"
},
{
"hookName": "after all",
"title": [
"\"after all\" hook"
],
"body": "() => {\n throw new Error('fail3');\n }"
}
],
"tests": [
{
"title": [
"simple failing hook spec",
"beforeEach hooks",
"never gets here"
],
"state": "failed",
"body": "() => {}",
"displayError": "Error: fail1\n\nBecause this error occurred during a `before each` hook we are skipping the remaining tests in the current suite: `beforeEach hooks`\n [stack trace lines]",
"attempts": [
{
"state": "failed",
"error": {
"name": "Error",
"message": "fail1",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_failing_hook.cy.js/simple failing hook spec -- beforeEach hooks -- never gets here (failed).png",
"height": 720,
"width": 1280
}
]
},
{
"state": "failed",
"error": {
"name": "Error",
"message": "fail1\n\nBecause this error occurred during a `before each` hook we are skipping the remaining tests in the current suite: `beforeEach hooks`",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_failing_hook.cy.js/simple failing hook spec -- beforeEach hooks -- never gets here -- before each hook (failed) (attempt 2).png",
"height": 720,
"width": 1280
}
]
}
]
},
{
"title": [
"simple failing hook spec",
"pending",
"is pending"
],
"state": "pending",
"body": "",
"displayError": null,
"attempts": [
{
"state": "pending",
"error": null,
"videoTimestamp": null,
"duration": null,
"startedAt": null,
"screenshots": []
}
]
},
{
"title": [
"simple failing hook spec",
"afterEach hooks",
"runs this"
],
"state": "failed",
"body": "() => {}",
"displayError": "Error: fail2\n\nBecause this error occurred during a `after each` hook we are skipping the remaining tests in the current suite: `afterEach hooks`\n [stack trace lines]",
"attempts": [
{
"state": "failed",
"error": {
"name": "Error",
"message": "fail2",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_failing_hook.cy.js/simple failing hook spec -- afterEach hooks -- runs this -- after each hook (failed).png",
"height": 720,
"width": 1280
}
]
},
{
"state": "failed",
"error": {
"name": "Error",
"message": "fail2\n\nBecause this error occurred during a `after each` hook we are skipping the remaining tests in the current suite: `afterEach hooks`",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_failing_hook.cy.js/simple failing hook spec -- afterEach hooks -- runs this -- after each hook (failed) (attempt 2).png",
"height": 720,
"width": 1280
}
]
}
]
},
{
"title": [
"simple failing hook spec",
"afterEach hooks",
"does not run this"
],
"state": "skipped",
"body": "() => {}",
"displayError": null,
"attempts": [
{
"state": "skipped",
"error": null,
"videoTimestamp": null,
"duration": null,
"startedAt": null,
"screenshots": []
}
]
},
{
"title": [
"simple failing hook spec",
"after hooks",
"runs this"
],
"state": "passed",
"body": "() => {}",
"displayError": null,
"attempts": [
{
"state": "passed",
"error": null,
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": []
}
]
},
{
"title": [
"simple failing hook spec",
"after hooks",
"fails on this"
],
"state": "failed",
"body": "() => {}",
"displayError": "Error: fail3\n\nBecause this error occurred during a `after all` hook we are skipping the remaining tests in the current suite: `after hooks`\n\nAlthough you have test retries enabled, we do not retry tests when `before all` or `after all` hooks fail\n [stack trace lines]",
"attempts": [
{
"state": "failed",
"error": {
"name": "Error",
"message": "fail3\n\nBecause this error occurred during a `after all` hook we are skipping the remaining tests in the current suite: `after hooks`\n\nAlthough you have test retries enabled, we do not retry tests when `before all` or `after all` hooks fail",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_failing_hook.cy.js/simple failing hook spec -- after hooks -- fails on this -- after all hook (failed).png",
"height": 720,
"width": 1280
}
]
}
]
}
],
"error": null,
"video": null,
"spec": {
"fileExtension": ".js",
"baseName": "simple_failing_hook.cy.js",
"fileName": "simple_failing_hook",
"specFileExtension": ".cy.js",
"relativeToCommonRoot": "simple_failing_hook.cy.js",
"specType": "integration",
"name": "cypress/e2e/simple_failing_hook.cy.js",
"relative": "cypress/e2e/simple_failing_hook.cy.js",
"absolute": "/foo/bar/.projects/e2e/cypress/e2e/simple_failing_hook.cy.js"
},
"shouldUploadVideo": true
},
{
"stats": {
"suites": 1,
"tests": 2,
"passes": 1,
"pending": 0,
"skipped": 0,
"failures": 1,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"endedAt": "2018-02-01T20:14:19.323Z"
},
"reporter": "spec",
"reporterStats": {
"suites": 1,
"tests": 2,
"passes": 1,
"pending": 0,
"failures": 1,
"start": "2018-02-01T20:14:19.323Z",
"end": "2018-02-01T20:14:19.323Z",
"duration": 1234
},
"hooks": [],
"tests": [
{
"title": [
"simple retrying spec",
"t1"
],
"state": "failed",
"body": "() => {\n const test = cy.state('test');\n throw new Error(`${test.title} attempt #${cy.state('test').currentRetry()}`);\n }",
"displayError": "Error: t1 attempt #1\n [stack trace lines]",
"attempts": [
{
"state": "failed",
"error": {
"name": "Error",
"message": "t1 attempt #0",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_retrying.cy.js/simple retrying spec -- t1 (failed).png",
"height": 720,
"width": 1280
}
]
},
{
"state": "failed",
"error": {
"name": "Error",
"message": "t1 attempt #1",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_retrying.cy.js/simple retrying spec -- t1 (failed) (attempt 2).png",
"height": 720,
"width": 1280
}
]
}
]
},
{
"title": [
"simple retrying spec",
"t2"
],
"state": "passed",
"body": "() => {// pass\n }",
"displayError": null,
"attempts": [
{
"state": "passed",
"error": null,
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": []
}
]
}
],
"error": null,
"video": null,
"spec": {
"fileExtension": ".js",
"baseName": "simple_retrying.cy.js",
"fileName": "simple_retrying",
"specFileExtension": ".cy.js",
"relativeToCommonRoot": "simple_retrying.cy.js",
"specType": "integration",
"name": "cypress/e2e/simple_retrying.cy.js",
"relative": "cypress/e2e/simple_retrying.cy.js",
"absolute": "/foo/bar/.projects/e2e/cypress/e2e/simple_retrying.cy.js"
},
"shouldUploadVideo": true
}
],
"browserPath": "path/to/browser",
"browserName": "FooBrowser",
"browserVersion": "88",
"osName": "FooOS",
"osVersion": "1234",
"cypressVersion": "9.9.9",
"config": {}
}
exports['e2e spec_isolation fails [webkit] 1'] = {
"status": "finished",
"startedTestsAt": "2018-02-01T20:14:19.323Z",
"endedTestsAt": "2018-02-01T20:14:19.323Z",
"totalDuration": 5555,
"totalSuites": 8,
"totalTests": 12,
"totalPassed": 5,
"totalPending": 1,
"totalFailed": 5,
"totalSkipped": 1,
"runs": [
{
"stats": {
"suites": 1,
"tests": 1,
"passes": 1,
"pending": 0,
"skipped": 0,
"failures": 0,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"endedAt": "2018-02-01T20:14:19.323Z"
},
"reporter": "spec",
"reporterStats": {
"suites": 1,
"tests": 1,
"passes": 1,
"pending": 0,
"failures": 0,
"start": "2018-02-01T20:14:19.323Z",
"end": "2018-02-01T20:14:19.323Z",
"duration": 1234
},
"hooks": [
{
"hookName": "before each",
"title": [
"\"before each\" hook"
],
"body": "() => {\n cy.wait(1000);\n }"
}
],
"tests": [
{
"title": [
"simple passing spec",
"passes"
],
"state": "passed",
"body": "() => {\n cy.wrap(true).should('be.true');\n }",
"displayError": null,
"attempts": [
{
"state": "passed",
"error": null,
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": []
}
]
}
],
"error": null,
"video": null,
"spec": {
"fileExtension": ".js",
"baseName": "simple_passing.cy.js",
"fileName": "simple_passing",
"specFileExtension": ".cy.js",
"relativeToCommonRoot": "simple_passing.cy.js",
"specType": "integration",
"name": "cypress/e2e/simple_passing.cy.js",
"relative": "cypress/e2e/simple_passing.cy.js",
"absolute": "/foo/bar/.projects/e2e/cypress/e2e/simple_passing.cy.js"
},
"shouldUploadVideo": true
},
{
"stats": {
"suites": 1,
"tests": 3,
"passes": 3,
"pending": 0,
"skipped": 0,
"failures": 0,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"endedAt": "2018-02-01T20:14:19.323Z"
},
"reporter": "spec",
"reporterStats": {
"suites": 1,
"tests": 3,
"passes": 3,
"pending": 0,
"failures": 0,
"start": "2018-02-01T20:14:19.323Z",
"end": "2018-02-01T20:14:19.323Z",
"duration": 1234
},
"hooks": [
{
"hookName": "before all",
"title": [
"\"before all\" hook"
],
"body": "() => {\n cy.wait(100);\n }"
},
{
"hookName": "before each",
"title": [
"\"before each\" hook"
],
"body": "() => {\n cy.wait(200);\n }"
},
{
"hookName": "after each",
"title": [
"\"after each\" hook"
],
"body": "() => {\n cy.wait(200);\n }"
},
{
"hookName": "after all",
"title": [
"\"after all\" hook"
],
"body": "() => {\n cy.wait(100);\n }"
}
],
"tests": [
{
"title": [
"simple hooks spec",
"t1"
],
"state": "passed",
"body": "() => {\n cy.wrap('t1').should('eq', 't1');\n }",
"displayError": null,
"attempts": [
{
"state": "passed",
"error": null,
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": []
}
]
},
{
"title": [
"simple hooks spec",
"t2"
],
"state": "passed",
"body": "() => {\n cy.wrap('t2').should('eq', 't2');\n }",
"displayError": null,
"attempts": [
{
"state": "passed",
"error": null,
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": []
}
]
},
{
"title": [
"simple hooks spec",
"t3"
],
"state": "passed",
"body": "() => {\n cy.wrap('t3').should('eq', 't3');\n }",
"displayError": null,
"attempts": [
{
"state": "passed",
"error": null,
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": []
}
]
}
],
"error": null,
"video": null,
"spec": {
"fileExtension": ".js",
"baseName": "simple_hooks.cy.js",
"fileName": "simple_hooks",
"specFileExtension": ".cy.js",
"relativeToCommonRoot": "simple_hooks.cy.js",
"specType": "integration",
"name": "cypress/e2e/simple_hooks.cy.js",
"relative": "cypress/e2e/simple_hooks.cy.js",
"absolute": "/foo/bar/.projects/e2e/cypress/e2e/simple_hooks.cy.js"
},
"shouldUploadVideo": true
},
{
"stats": {
"suites": 1,
"tests": 2,
"passes": 0,
"pending": 0,
"skipped": 0,
"failures": 2,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"endedAt": "2018-02-01T20:14:19.323Z"
},
"reporter": "spec",
"reporterStats": {
"suites": 1,
"tests": 2,
"passes": 0,
"pending": 0,
"failures": 2,
"start": "2018-02-01T20:14:19.323Z",
"end": "2018-02-01T20:14:19.323Z",
"duration": 1234
},
"hooks": [],
"tests": [
{
"title": [
"simple failing spec",
"fails1"
],
"state": "failed",
"body": "() => {\n cy.wrap(true, {\n timeout: 100\n }).should('be.false');\n }",
"displayError": "AssertionError: Timed out retrying after 100ms: expected true to be false\n [stack trace lines]",
"attempts": [
{
"state": "failed",
"error": {
"name": "AssertionError",
"message": "Timed out retrying after 100ms: expected true to be false",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_failing.cy.js/simple failing spec -- fails1 (failed).png",
"height": 720,
"width": 1280
}
]
}
]
},
{
"title": [
"simple failing spec",
"fails2"
],
"state": "failed",
"body": "() => {\n throw new Error('fails2');\n }",
"displayError": "Error: fails2\n [stack trace lines]",
"attempts": [
{
"state": "failed",
"error": {
"name": "Error",
"message": "fails2",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_failing.cy.js/simple failing spec -- fails2 (failed).png",
"height": 720,
"width": 1280
}
]
}
]
}
],
"error": null,
"video": null,
"spec": {
"fileExtension": ".js",
"baseName": "simple_failing.cy.js",
"fileName": "simple_failing",
"specFileExtension": ".cy.js",
"relativeToCommonRoot": "simple_failing.cy.js",
"specType": "integration",
"name": "cypress/e2e/simple_failing.cy.js",
"relative": "cypress/e2e/simple_failing.cy.js",
"absolute": "/foo/bar/.projects/e2e/cypress/e2e/simple_failing.cy.js"
},
"shouldUploadVideo": true
},
{
"stats": {
"suites": 5,
"tests": 6,
"passes": 1,
"pending": 1,
"skipped": 1,
"failures": 3,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"endedAt": "2018-02-01T20:14:19.323Z"
},
"reporter": "spec",
"reporterStats": {
"suites": 5,
"tests": 5,
"passes": 1,
"pending": 1,
"failures": 3,
"start": "2018-02-01T20:14:19.323Z",
"end": "2018-02-01T20:14:19.323Z",
"duration": 1234
},
"hooks": [
{
"hookName": "before each",
"title": [
"\"before each\" hook"
],
"body": "() => {\n throw new Error('fail1');\n }"
},
{
"hookName": "after each",
"title": [
"\"after each\" hook"
],
"body": "() => {\n throw new Error('fail2');\n }"
},
{
"hookName": "after all",
"title": [
"\"after all\" hook"
],
"body": "() => {\n throw new Error('fail3');\n }"
}
],
"tests": [
{
"title": [
"simple failing hook spec",
"beforeEach hooks",
"never gets here"
],
"state": "failed",
"body": "() => {}",
"displayError": "Error: fail1\n\nBecause this error occurred during a `before each` hook we are skipping the remaining tests in the current suite: `beforeEach hooks`\n [stack trace lines]",
"attempts": [
{
"state": "failed",
"error": {
"name": "Error",
"message": "fail1\n\nBecause this error occurred during a `before each` hook we are skipping the remaining tests in the current suite: `beforeEach hooks`",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_failing_hook.cy.js/simple failing hook spec -- beforeEach hooks -- never gets here -- before each hook (failed).png",
"height": 720,
"width": 1280
}
]
}
]
},
{
"title": [
"simple failing hook spec",
"pending",
"is pending"
],
"state": "pending",
"body": "",
"displayError": null,
"attempts": [
{
"state": "pending",
"error": null,
"videoTimestamp": null,
"duration": null,
"startedAt": null,
"screenshots": []
}
]
},
{
"title": [
"simple failing hook spec",
"afterEach hooks",
"runs this"
],
"state": "failed",
"body": "() => {}",
"displayError": "Error: fail2\n\nBecause this error occurred during a `after each` hook we are skipping the remaining tests in the current suite: `afterEach hooks`\n [stack trace lines]",
"attempts": [
{
"state": "failed",
"error": {
"name": "Error",
"message": "fail2\n\nBecause this error occurred during a `after each` hook we are skipping the remaining tests in the current suite: `afterEach hooks`",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_failing_hook.cy.js/simple failing hook spec -- afterEach hooks -- runs this -- after each hook (failed).png",
"height": 720,
"width": 1280
}
]
}
]
},
{
"title": [
"simple failing hook spec",
"afterEach hooks",
"does not run this"
],
"state": "skipped",
"body": "() => {}",
"displayError": null,
"attempts": [
{
"state": "skipped",
"error": null,
"videoTimestamp": null,
"duration": null,
"startedAt": null,
"screenshots": []
}
]
},
{
"title": [
"simple failing hook spec",
"after hooks",
"runs this"
],
"state": "passed",
"body": "() => {}",
"displayError": null,
"attempts": [
{
"state": "passed",
"error": null,
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": []
}
]
},
{
"title": [
"simple failing hook spec",
"after hooks",
"fails on this"
],
"state": "failed",
"body": "() => {}",
"displayError": "Error: fail3\n\nBecause this error occurred during a `after all` hook we are skipping the remaining tests in the current suite: `after hooks`\n [stack trace lines]",
"attempts": [
{
"state": "failed",
"error": {
"name": "Error",
"message": "fail3\n\nBecause this error occurred during a `after all` hook we are skipping the remaining tests in the current suite: `after hooks`",
"stack": "[stack trace lines]"
},
"videoTimestamp": null,
"duration": 1234,
"startedAt": "2018-02-01T20:14:19.323Z",
"screenshots": [
{
"name": null,
"takenAt": "2018-02-01T20:14:19.323Z",
"path": "/foo/bar/.projects/e2e/cypress/screenshots/simple_failing_hook.cy.js/simple failing hook spec -- after hooks -- fails on this -- after all hook (failed).png",
"height": 720,
"width": 1280
}
]
}
]
}
],
"error": null,
"video": null,
"spec": {
"fileExtension": ".js",
"baseName": "simple_failing_hook.cy.js",
"fileName": "simple_failing_hook",
"specFileExtension": ".cy.js",
"relativeToCommonRoot": "simple_failing_hook.cy.js",
"specType": "integration",
"name": "cypress/e2e/simple_failing_hook.cy.js",
"relative": "cypress/e2e/simple_failing_hook.cy.js",
"absolute": "/foo/bar/.projects/e2e/cypress/e2e/simple_failing_hook.cy.js"
},
"shouldUploadVideo": true
}
],
"browserPath": "path/to/browser",
"browserName": "FooBrowser",
"browserVersion": "88",
"osName": "FooOS",
"osVersion": "1234",
"cypressVersion": "9.9.9",
"config": {}
}
+31 -9
View File
@@ -9,7 +9,6 @@ export const pathUpToProjectName = Fixtures.projectPath('')
export const browserNameVersionRe = /(Browser\:\s+)(Custom |)(Electron|Chrome|Canary|Chromium|Firefox|WebKit)(\s\d+)(\s\(\w+\))?(\s+)/
const stackTraceLinesRe = /(\n?[^\S\n\r]*).*?(@|\bat\b)(?:.*node:.*|.*\.(js|coffee|ts|html|jsx|tsx))\??(-\d+)?:\d+:\d+[\n\S\s]*?(\n\s*?\n|$)/g
const availableBrowsersRe = /(Available browsers found on your system are:)([\s\S]+)/g
const crossOriginErrorRe = /(Blocked a frame .* from accessing a cross-origin frame.*|Permission denied.*cross-origin object.*)/gm
const whiteSpaceBetweenNewlines = /\n\s+\n/
@@ -77,14 +76,31 @@ const replaceUploadingResults = function (orig: string, ...rest: string[]) {
// this captures an entire stack trace and replaces it with [stack trace lines]
// so that the stdout can contain stack traces of different lengths
// '@' will be present in firefox stack trace lines
// 'at' will be present in chrome stack trace lines
export const replaceStackTraceLines = (str: string) => {
return str.replace(stackTraceLinesRe, (match: string, ...parts: string[]) => {
const isFirefoxStack = parts[1] === '@'
let post = parts[4]
export const replaceStackTraceLines = (str: string, browserName: 'electron' | 'firefox' | 'chrome' | 'webkit') => {
// matches the newline preceding the stack and any leading whitespace
const leadingNewLinesAndWhitespace = `(?:\\n?[^\\S\\n\\r]*)`
// matches against the potential file location patterns, including:
// foo.js:1:2 - file locations including line/column numbers
// <unknown> - rendered when location cannot be determined
// [native code] - rendered in some cases by WebKit browser
const location = `(?:.*:\\d+:\\d+|<unknown>|\\[native code\\])`
// matches stack lines with Chrome-style rendering:
// ' at foobar (foo.js:1:2)'
// ' at foo.js:1:2'
const verboseStyleLine = `at\\s.*(?::\\d+:\\d+|\\s\\(${location}\\))`
// matches stack lines with Firefox/WebKit style rendering:
// ' foobar@foo.js:1:2'
const condensedStyleLine = `.*@${location}`
// matches against remainder of stack trace until blank lines found.
// includes group to normalize whitespace between newlines in Firefox
const remainderOfStack = `[\\n\\S\\s]*?(\\n\\s*?\\n|$)`
if (isFirefoxStack) {
const stackTraceRegex = new RegExp(`${leadingNewLinesAndWhitespace}(?:${verboseStyleLine}|${condensedStyleLine})${remainderOfStack}`, 'g')
return str.replace(stackTraceRegex, (match: string, ...parts: string[]) => {
let post = parts[0]
if (browserName === 'firefox') {
post = post.replace(whiteSpaceBetweenNewlines, '\n')
}
@@ -142,6 +158,12 @@ export const normalizeStdout = function (str: string, options: any = {}) {
// Replaces connection warning since Chrome or Firefox sometimes take longer to connect
.replace(/Still waiting to connect to .+, retrying in 1 second \(attempt .+\/.+\)\n/g, '')
if (options.browser === 'webkit') {
// WebKit throws for lookups on undefined refs with "Can't find variable: <var>"
// This message is replaced with Chrome/Firefox's exception text for consistent diffs
str = str.replace(/(ReferenceError:|>) Can\'t find variable: (\S+)/g, '$1 $2 is not defined')
}
// avoid race condition when webpack prints this at a non-deterministic timing
const wdsFailedMsg = ' 「wdm」: Failed to compile.'
@@ -154,5 +176,5 @@ export const normalizeStdout = function (str: string, options: any = {}) {
str = str.replace(/(\(\d+x\d+\))/g, replaceScreenshotDims)
}
return replaceStackTraceLines(str)
return replaceStackTraceLines(str, options.browser)
}
@@ -20,7 +20,11 @@ describe('foo', () => {
it('passes with fail handler after failing with setTimeout', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('foo is not defined')
if (Cypress.isBrowser('webkit')) {
expect(err.message).to.include('Can\'t find variable: foo')
} else {
expect(err.message).to.include('foo is not defined')
}
setTimeout(() => {
return done()
@@ -43,8 +47,13 @@ describe('foo', () => {
it('passes with fail handler after failing with async app code error', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('qax is not defined')
expect(err.stack).to.include('qax is not defined')
if (Cypress.isBrowser('webkit')) {
expect(err.message).to.include('Can\'t find variable: qax')
expect(err.stack).to.include('Can\'t find variable: qax')
} else {
expect(err.message).to.include('qax is not defined')
expect(err.stack).to.include('qax is not defined')
}
setTimeout(() => {
return done()
-1
View File
@@ -4,7 +4,6 @@ describe('e2e async timeouts', () => {
systemTests.setup()
systemTests.it('failing1', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'async_timeouts.cy.js',
snapshot: true,
expectedExitCode: 2,
@@ -9,28 +9,24 @@ describe('e2e caught and uncaught hooks errors', () => {
})
systemTests.it('failing1', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'hook_caught_error_failing.cy.js',
snapshot: true,
expectedExitCode: 3,
})
systemTests.it('failing2', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to different ReferenceError snapshot)
spec: 'hook_uncaught_error_failing.cy.js',
snapshot: true,
expectedExitCode: 1,
})
systemTests.it('failing3', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to different ReferenceError snapshot)
spec: 'hook_uncaught_root_error_failing.cy.js',
snapshot: true,
expectedExitCode: 1,
})
systemTests.it('failing4', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to different ReferenceError snapshot)
spec: 'hook_uncaught_error_events_failing.cy.js',
snapshot: true,
expectedExitCode: 1,
@@ -4,14 +4,12 @@ describe('e2e commands outside of test', () => {
systemTests.setup()
systemTests.it('fails on cy commands', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'commands_outside_of_test.cy.js',
snapshot: true,
expectedExitCode: 1,
})
systemTests.it('fails on failing assertions', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'assertions_failing_outside_of_test.cy.js',
snapshot: true,
expectedExitCode: 1,
@@ -95,7 +95,6 @@ describe('e2e forms', () => {
})
systemTests.it('failing', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'form_submission_failing.cy.js',
snapshot: true,
expectedExitCode: 1,
-1
View File
@@ -5,7 +5,6 @@ describe('e2e issue 173', () => {
// https://github.com/cypress-io/cypress/issues/173
systemTests.it('failing', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'issue_173.cy.js',
snapshot: true,
expectedExitCode: 1,
-1
View File
@@ -5,7 +5,6 @@ describe('e2e issue 674', () => {
// https://github.com/cypress-io/cypress/issues/674
systemTests.it('fails', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'issue_674.cy.js',
snapshot: true,
expectedExitCode: 1,
@@ -46,7 +46,6 @@ describe('e2e js error handling', () => {
})
systemTests.it('fails', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'js_error_handling_failing.cy.js',
snapshot: true,
expectedExitCode: 5,
-1
View File
@@ -4,7 +4,6 @@ describe('e2e promises', () => {
systemTests.setup()
systemTests.it('failing1', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'promises.cy.js',
snapshot: true,
expectedExitCode: 2,
@@ -20,7 +20,6 @@ describe('e2e runnable execution', () => {
// but throws correct error
// https://github.com/cypress-io/cypress/issues/1987
systemTests.it('cannot navigate in before hook and test', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
project: 'hooks-after-rerun',
spec: 'beforehook-and-test-navigation.cy.js',
snapshot: true,
@@ -34,7 +33,6 @@ describe('e2e runnable execution', () => {
})
systemTests.it('runs correctly after top navigation with already ran suite', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'runnables_already_run_suite.cy.js',
snapshot: true,
expectedExitCode: 1,
-2
View File
@@ -21,7 +21,6 @@ describe('e2e spec_isolation', () => {
systemTests.setup()
it('fails', {
browser: '!webkit', // TODO(webkit): fix+unskip
spec: specs,
outputPath,
snapshot: false,
@@ -52,7 +51,6 @@ describe('e2e spec_isolation', () => {
})
it('failing with retries enabled', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'simple_failing_hook.cy.js,simple_retrying.cy.js',
outputPath,
snapshot: true,
-1
View File
@@ -71,7 +71,6 @@ describe('e2e stdout', () => {
})
systemTests.it('displays assertion errors', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'stdout_assertion_errors.cy.js',
snapshot: true,
expectedExitCode: 4,
@@ -21,7 +21,6 @@ describe('testConfigOverrides', () => {
})
systemTests.it('fails when passing invalid config value browser', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'testConfigOverrides/invalid-browser.js',
snapshot: true,
expectedExitCode: 1,
@@ -4,35 +4,30 @@ describe('e2e uncaught errors', () => {
systemTests.setup()
systemTests.it('failing1', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'uncaught_synchronous_before_tests_parsed.js',
snapshot: true,
expectedExitCode: 1,
})
systemTests.it('failing2', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'uncaught_synchronous_during_hook.cy.js',
snapshot: true,
expectedExitCode: 1,
})
systemTests.it('failing3', {
browser: '!webkit', // TODO(webkit): fix+unskip
spec: 'uncaught_during_test.cy.js',
snapshot: true,
expectedExitCode: 3,
})
systemTests.it('failing4', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'uncaught_during_hook.cy.js',
snapshot: true,
expectedExitCode: 1,
})
systemTests.it('failing5', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'caught_async_sync_test.cy.js',
snapshot: true,
expectedExitCode: 4,
-4
View File
@@ -171,7 +171,6 @@ describe('e2e visit', () => {
})
systemTests.it('fails when server responds with 500', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'visit_http_500_response_failing.cy.js',
snapshot: true,
expectedExitCode: 1,
@@ -185,7 +184,6 @@ describe('e2e visit', () => {
})
systemTests.it('fails when content type isnt html', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'visit_non_html_content_type_failing.cy.js',
snapshot: true,
expectedExitCode: 1,
@@ -211,7 +209,6 @@ describe('e2e visit', () => {
})
systemTests.it('fails when response never ends', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'visit_response_never_ends_failing.cy.js',
snapshot: true,
expectedExitCode: 3,
@@ -232,7 +229,6 @@ describe('e2e visit', () => {
})
systemTests.it('fails when visit times out', {
browser: '!webkit', // TODO(webkit): fix+unskip (failing due to broken stack trace)
spec: 'visit_http_timeout_failing.cy.js',
snapshot: true,
expectedExitCode: 2,