fix: Show stacks and code frames in specs named with spaces (#9201)

Co-authored-by: Chris Breiding <chrisbreiding@gmail.com>
Co-authored-by: Chris Breiding <chrisbreiding@users.noreply.github.com>
This commit is contained in:
Kukhyeon Heo
2020-12-18 01:16:05 +09:00
committed by GitHub
parent 0f6d7bda79
commit 2caac1bfec
6 changed files with 65 additions and 30 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ const fetchScript = (scriptWindow, script) => {
}
const extractSourceMap = ([script, contents]) => {
script.fullyQualifiedUrl = `${window.top.location.origin}${script.relativeUrl}`
script.fullyQualifiedUrl = `${window.top.location.origin}${script.relativeUrl}`.replace(/ /g, '%20')
const sourceMap = $sourceMapUtils.extractSourceMap(script, contents)
@@ -1,7 +1,7 @@
{
"name": "CommandError",
"message": "`foo` \\`bar\\` **baz** *fizz* ** buzz **",
"stack": "Some Error\n at foo.bar (my/app.js:2:7)\n at baz.qux (cypress/integration/foo_spec.js:5:2)\n From previous event:\n at bar.baz (my/app.js:8:11)\n ",
"stack": "Some Error\n at foo.bar (my/app.js:2:7)\n at baz.qux (cypress/integration/foo_spec.js:5:2)\n at space (cypress/integration/a b.js:34:99)\n From previous event:\n at bar.baz (my/app.js:8:11)\n ",
"parsedStack": [
{
"message": "Some Error",
@@ -35,6 +35,15 @@
"column": 2,
"whitespace": " "
},
{
"originalFile": "cypress/integration/a%20b.js",
"relativeFile": "cypress/integration/a%20b.js",
"absoluteFile": "/me/dev/cypress/integration/a%20b.js",
"function": "space",
"line": 34,
"column": 99,
"whitespace": " "
},
{
"message": "At previous event:",
"whitespace": " "
@@ -95,20 +95,23 @@ describe('test errors', () => {
cy.get('.runnable-err-stack-trace').within(() => {
cy.get('.err-stack-line')
.should('have.length', 9)
.should('have.length', 10)
.first().should('have.text', 'at foo.bar (my/app.js:2:7)')
cy.get('.err-stack-line')
.eq(1).should('have.text', ' at baz.qux (cypress/integration/foo_spec.js:5:2)')
cy.get('.err-stack-line')
.eq(2).should('have.text', 'At previous event:')
.eq(2).should('have.text', ' at space (cypress/integration/a b.js:34:99)')
cy.get('.err-stack-line')
.eq(3).should('have.text', ' at bar.baz (http://localhost:1234/me/dev/my/app.js:8:11)')
.eq(3).should('have.text', 'At previous event:')
cy.get('.err-stack-line')
.eq(4).should('have.text', ' at callFn (cypress://../driver/src/cypress/runner.js:9:12)')
.eq(4).should('have.text', ' at bar.baz (http://localhost:1234/me/dev/my/app.js:8:11)')
cy.get('.err-stack-line')
.eq(5).should('have.text', ' at callFn (cypress://../driver/src/cypress/runner.js:9:12)')
})
})
@@ -124,12 +127,15 @@ describe('test errors', () => {
cy.contains('View stack trace').click()
cy.get('.runnable-err-stack-trace .runnable-err-file-path')
.should('have.length', 2)
.should('have.length', 3)
.first()
.should('have.text', 'my/app.js:2:7')
cy.get('.runnable-err-stack-trace .runnable-err-file-path').eq(1)
.should('have.text', 'cypress/integration/foo_spec.js:5:2')
cy.get('.runnable-err-stack-trace .runnable-err-file-path').eq(2)
.should('have.text', 'cypress/integration/a b.js:34:99')
})
it('does not turn cypress:// files into links', () => {
@@ -0,0 +1,12 @@
import { FileDetails } from '@packages/ui-components'
export function decodeFilePaths (fileDetails: FileDetails) {
const { absoluteFile, originalFile, relativeFile } = fileDetails
return {
...fileDetails,
absoluteFile: absoluteFile ? absoluteFile.replace(/%20/g, ' ') : absoluteFile,
originalFile: originalFile ? originalFile.replace(/%20/g, ' ') : originalFile,
relativeFile: relativeFile ? relativeFile.replace(/%20/g, ' ') : relativeFile,
}
}
@@ -4,6 +4,7 @@ import Prism from 'prismjs'
import { CodeFrame } from './err-model'
import FileNameOpener from '../lib/file-name-opener'
import { decodeFilePaths } from './decode-file-paths'
interface Props {
codeFrame: CodeFrame
@@ -24,7 +25,7 @@ class ErrorCodeFrame extends Component<Props> {
return (
<div className='test-err-code-frame'>
<FileNameOpener className="runnable-err-file-path" fileDetails={this.props.codeFrame} />
<FileNameOpener className="runnable-err-file-path" fileDetails={decodeFilePaths(this.props.codeFrame)} />
<pre ref='codeFrame' data-line={highlightLine}>
<code className={`language-${language || 'text'}`}>{frame}</code>
</pre>
+29 -22
View File
@@ -3,6 +3,7 @@ import { observer } from 'mobx-react'
import React, { ReactElement } from 'react'
import FileNameOpener from '../lib/file-name-opener'
import { decodeFilePaths } from './decode-file-paths'
import Err, { ParsedStackFileLine, ParsedStackMessageLine } from './err-model'
const cypressLineRegex = /(cypress:\/\/|cypress_runner\.js)/
@@ -13,21 +14,26 @@ interface Props {
type StringOrElement = string | ReactElement
const isMessageLine = (stackLine: ParsedStackFileLine | ParsedStackMessageLine) => {
return (stackLine as ParsedStackMessageLine).message != null
}
const ErrorStack = observer(({ err }: Props) => {
if (!err.parsedStack) return <>err.stack</>
// only display stack lines beyond the original message, since it's already
// displayed above this
// displayed above this in the UI
let foundFirstStackLine = false
const stackLines = _.filter(err.parsedStack, (line) => {
if (foundFirstStackLine) return true
if ((line as ParsedStackMessageLine).message != null) return false
if (isMessageLine(line)) return false
foundFirstStackLine = true
return true
})
// instead of having every line indented, get rid of the smallest amount of
// whitespace common to each line so the stack is aligned left but lines
// with extra whitespace still have it
@@ -42,38 +48,39 @@ const ErrorStack = observer(({ err }: Props) => {
let stopLinking = false
const lines = _.map(stackLines, (stackLine, index) => {
const { originalFile, function: fn, line, column, absoluteFile } = stackLine as ParsedStackFileLine
const key = `${originalFile}${index}`
const whitespace = stackLine.whitespace.slice(commonWhitespaceLength)
if ((stackLine as ParsedStackMessageLine).message != null) {
if (isMessageLine(stackLine)) {
const message = (stackLine as ParsedStackMessageLine).message
// we append some errors with 'node internals', which we don't want to link
// so stop linking anything after 'From Node.js Internals'
if ((stackLine as ParsedStackMessageLine).message.includes('From Node')) {
if (message.includes('From Node')) {
stopLinking = true
}
return makeLine(key, [whitespace, (stackLine as ParsedStackMessageLine).message])
return makeLine(`${message}${index}`, [whitespace, message])
}
if (stopLinking) {
// we have already shown a good link, so no need to make
// clickable links deep in the woods of the stack trace
const { originalFile, function: fn, line, column, absoluteFile } = stackLine as ParsedStackFileLine
const key = `${originalFile}${index}`
const dontLink = (
// don't link to Node files, opening them in IDE won't work
stopLinking
// sometimes we can determine the file on disk, but if there are no
// source maps or the file was transpiled in the browser, there
// is no absoluteFile to link to
|| !absoluteFile
// don't link to cypress internals, opening them in IDE won't work
|| cypressLineRegex.test(originalFile || '')
)
if (dontLink) {
return makeLine(key, [whitespace, `at ${fn} (${originalFile}:${line}:${column})`])
}
// decide if can show "open file in IDE" link or not
// sometimes we can determine the file on disk, but if
// there are no source maps, or the file was transpiled in the browser
// then all we can do is show the link as is without making it clickable
if (!absoluteFile) {
return makeLine(key, [whitespace, `at ${fn} (${originalFile}:${line}:${column})`])
}
if (cypressLineRegex.test(originalFile || '')) {
return makeLine(key, [whitespace, `at ${fn} (${originalFile}:${line}:${column})`])
}
stackLine = decodeFilePaths(stackLine as ParsedStackFileLine) as ParsedStackFileLine
const link = (
<FileNameOpener key={key} className="runnable-err-file-path" fileDetails={stackLine as ParsedStackFileLine} />