feat: Add codeFrame to record data attempt errors (#8595)

This commit is contained in:
Chris Breiding
2020-10-01 09:48:49 -04:00
committed by GitHub
parent 92cf3696e9
commit d28676c43e
10 changed files with 2753 additions and 569 deletions
+4 -3
View File
@@ -8,6 +8,7 @@
declare namespace CypressCommandLine {
type HookName = 'before' | 'beforeEach' | 'afterEach' | 'after'
interface TestError {
name: string
message: string
@@ -225,7 +226,7 @@ declare namespace CypressCommandLine {
startedAt: dateTimeISO
endedAt: dateTimeISO
duration: ms
},
}
/**
* Reporter name like "spec"
*/
@@ -255,7 +256,7 @@ declare namespace CypressCommandLine {
* resolved filename of the spec
*/
absolute: string
},
}
shouldUploadVideo: boolean
}
@@ -358,7 +359,7 @@ declare module 'cypress' {
})
```
*/
run(options?: Partial<CypressCommandLine.CypressRunOptions>): Promise<CypressCommandLine.CypressRunResult | CypressCommandLine.CypressFailedRunResult>,
run(options?: Partial<CypressCommandLine.CypressRunOptions>): Promise<CypressCommandLine.CypressRunResult | CypressCommandLine.CypressFailedRunResult>
/**
* Opens Cypress GUI. Resolves with void when the
* GUI is closed.
File diff suppressed because it is too large Load Diff
+20 -2
View File
@@ -2191,7 +2191,16 @@ exports['e2e record passing passes 2'] = [
"error": {
"name": "Error",
"message": "foo\n\nBecause this error occurred during a `before each` hook we are skipping the remaining tests in the current suite: `record fails`",
"stack": "[stack trace lines]"
"stack": "[stack trace lines]",
"codeFrame": {
"line": 3,
"column": 11,
"originalFile": "cypress/integration/record_fail_spec.coffee",
"relativeFile": "cypress/integration/record_fail_spec.coffee",
"absoluteFile": "/foo/bar/.projects/e2e/cypress/integration/record_fail_spec.coffee",
"frame": " 1 | describe \"record fails\", ->\n 2 | beforeEach ->\n> 3 | throw new Error(\"foo\")\n | ^\n 4 | \n 5 | it \"fails 1\", ->\n 6 | ",
"language": "coffee"
}
},
"timings": {
"lifecycle": 100,
@@ -2382,7 +2391,16 @@ exports['e2e record passing passes 2'] = [
"error": {
"name": "Error",
"message": "The following error originated from your test code, not from Cypress.\n\n > instantly fails\n\nWhen Cypress detects uncaught errors originating from your test code it will automatically fail the current test.\n\nCypress could not associate this error to any specific test.\n\nWe dynamically generated a new test to display this failure.",
"stack": "[stack trace lines]"
"stack": "[stack trace lines]",
"codeFrame": {
"line": 1,
"column": 7,
"originalFile": "cypress/integration/record_uncaught_spec.coffee",
"relativeFile": "cypress/integration/record_uncaught_spec.coffee",
"absoluteFile": "/foo/bar/.projects/e2e/cypress/integration/record_uncaught_spec.coffee",
"frame": "> 1 | throw new Error('instantly fails')\n | ^\n 2 | ",
"language": "coffee"
}
},
"timings": {
"lifecycle": 100,
+10 -1
View File
@@ -33,7 +33,16 @@ exports['lib/reporter #stats has reporterName stats, reporterStats, etc 1'] = {
"state": "failed",
"error": {
"message": "foo",
"stack": "at foo:1:1\nat bar:1:1\nat baz:1:1"
"stack": "at foo:1:1\nat bar:1:1\nat baz:1:1",
"codeFrame": {
"line": 7,
"column": 8,
"originalFile": "cypress/integration/spec.js",
"relativeFile": "cypress/integration/spec.js",
"absoluteFile": "/path/to/cypress/integration/spec.js",
"frame": " 5 | \n 6 | it('fails', () => {\n> 7 | cy.get('nope', { timeout: 1 })\n | ^\n 8 | })\n 9 | })\n 10 | ",
"language": "js"
}
},
"timings": null,
"failedFromHookId": null,
+1
View File
@@ -362,6 +362,7 @@ class Reporter {
name: attempt.err.name,
message: attempt.err.message,
stack: stackUtils.stackWithoutMessage(attempt.err.stack),
codeFrame: attempt.err.codeFrame,
}
return {
@@ -27,6 +27,9 @@ describe('e2e spec_isolation', () => {
outputPath,
snapshot: false,
expectedExitCode: 5,
config: {
video: false,
},
async onRun (execFn) {
const { stdout } = await execFn()
@@ -36,14 +39,16 @@ describe('e2e spec_isolation', () => {
// now what we want to do is read in the outputPath
// and snapshot it so its what we expect after normalizing it
const json = await fs.readJsonAsync(outputPath)
let json = await fs.readJsonAsync(outputPath)
json.runs = e2e.normalizeRuns(json.runs)
// also mutates into normalized obj ready for snapshot
expectCorrectModuleApiResult(json, {
e2ePath, runs: 4,
e2ePath, runs: 4, video: false,
})
snapshot('e2e spec isolation fails', json, { allowSharedSnapshot: true })
snapshot(json, { allowSharedSnapshot: true })
},
})
@@ -54,15 +59,20 @@ describe('e2e spec_isolation', () => {
expectedExitCode: 4,
config: {
retries: 1,
video: false,
},
async onRun (execFn) {
await execFn()
const json = await fs.readJsonAsync(outputPath)
let json = await fs.readJsonAsync(outputPath)
json.runs = e2e.normalizeRuns(json.runs)
// also mutates into normalized obj ready for snapshot
expectCorrectModuleApiResult(json, { e2ePath, runs: 2 })
expectCorrectModuleApiResult(json, {
e2ePath, runs: 2, video: false,
})
snapshot('failing with retries enabled', json)
snapshot(json)
},
})
})
+3 -1
View File
@@ -402,10 +402,12 @@ describe('e2e record', () => {
expect(forthInstanceStdout.body.stdout).not.to.include('record_fail_spec.coffee')
expect(forthInstanceStdout.body.stdout).not.to.include('record_pass_spec.coffee')
const runs = requests.filter((v) => v.body.tests).map((v) => v.body)
let runs = requests.filter((v) => v.body.tests).map((v) => v.body)
expectRunsToHaveCorrectTimings(runs)
runs = e2e.normalizeRuns(runs)
snapshot(runs)
const results = await fs.readJsonAsync(outputPath)
@@ -781,6 +781,22 @@ const e2e = {
.replace(/using description file: .* \(relative/g, 'using description file: [..] (relative')
.replace(/Module build failed \(from .*\)/g, 'Module build failed (from [..])')
},
normalizeRuns (runs) {
runs.forEach((run) => {
run.tests.forEach((test) => {
test.attempts.forEach((attempt) => {
const codeFrame = attempt.error && attempt.error.codeFrame
if (codeFrame) {
codeFrame.absoluteFile = codeFrame.absoluteFile.split(pathUpToProjectName).join('/foo/bar/.projects')
}
})
})
})
return runs
},
}
export {
@@ -185,7 +185,12 @@ export const expectRunsToHaveCorrectTimings = (runs = []) => {
export const expectCorrectModuleApiResult = (json, opts: {
e2ePath: string
runs: number
video: boolean
}) => {
if (opts.video == null) {
opts.video = true
}
// should be n runs
expect(json.runs).to.have.length(opts.runs)
@@ -281,8 +286,10 @@ export const expectCorrectModuleApiResult = (json, opts: {
expect(d.toJSON()).to.eq(attempt.startedAt)
attempt.startedAt = STATIC_DATE
expect(attempt.videoTimestamp).to.be.a('number')
attempt.videoTimestamp = 9999
if (opts.video) {
expect(attempt.videoTimestamp).to.be.a('number')
attempt.videoTimestamp = 9999
}
}
attempt.screenshots.forEach((screenshot) => {
@@ -303,7 +310,9 @@ export const expectCorrectModuleApiResult = (json, opts: {
}
})
// normalize video path
run.video = e2e.normalizeStdout(run.video)
if (opts.video) {
// normalize video path
run.video = e2e.normalizeStdout(run.video)
}
})
}
@@ -33,6 +33,15 @@ describe('lib/reporter', () => {
err: {
message: 'foo',
stack: 'at foo:1:1\nat bar:1:1\nat baz:1:1',
codeFrame: {
line: 7,
column: 8,
originalFile: 'cypress/integration/spec.js',
relativeFile: 'cypress/integration/spec.js',
absoluteFile: '/path/to/cypress/integration/spec.js',
frame: ' 5 | \n 6 | it(\'fails\', () => {\n> 7 | cy.get(\'nope\', { timeout: 1 })\n | ^\n 8 | })\n 9 | })\n 10 | ',
language: 'js',
},
},
},
{