From 0994dba679fa1fdef536b66647d8d0b2afca1a77 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 13 Nov 2017 10:57:43 +0000 Subject: [PATCH 1/8] feat: highlight error code in terminal --- README.md | 2 +- package.json | 3 +- src/LineWriter.js | 35 ++++++++++++++++++++++ test/__snapshots__/LineWriter.spec.js.snap | 6 ++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5410f61..3058026 100644 --- a/README.md +++ b/README.md @@ -61,4 +61,4 @@ Available log levels are: `ERROR`, `WARN`, `INFO`. ## License -MIT, see [LICENSE.md](./LICENSE). +MIT, see [LICENSE](./LICENSE). diff --git a/package.json b/package.json index 5bd96d1..5430eb1 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "dependencies": { "chalk": "^2.3.0", "strip-ansi": "4.0.0", - "utf8-bar": "0.1.0" + "utf8-bar": "0.1.0", + "@babel/code-frame": "7.0.0-beta.32" } } diff --git a/src/LineWriter.js b/src/LineWriter.js index cf58071..3c94bd3 100644 --- a/src/LineWriter.js +++ b/src/LineWriter.js @@ -1,6 +1,8 @@ /* eslint-disable complexity, no-use-extend-native/no-use-extend-native */ const path = require('path'); +const fs = require('fs'); const chalk = require('chalk'); +const {codeFrameColumns} = require('@babel/code-frame'); const progressBar = require('./progressBar'); const REG_TRACE_LINE = /\s*(.+)\((.+):([0-9]+):([0-9]+)\)$/; @@ -49,6 +51,24 @@ const formatStatsBar = (percent, hasErrors) => { return chalk`{${textStyles} ${percentFormatted}} ${bar}`; }; +const formatCodeFrame = (filePath, line, column) => { + try { + const source = fs.readFileSync(filePath, 'utf8'); + const location = { + start: { + column, + line + } + }; + + return codeFrameColumns(source, location, { + highlightCode: true + }); + } catch (error) { + return ''; + } +}; + class LineWriter { constructor (logger, root) { this.counter = 0; @@ -71,6 +91,14 @@ class LineWriter { this.logger.info(formatComment(line)); } + commentBlock (str) { + const lines = str.split('\n'); + + for (const line of lines) { + this.comment(line); + } + } + start (numSuites) { this.blank(); this.blank(); @@ -192,6 +220,7 @@ class LineWriter { }; const pushTraceLine = (line) => push(chalk` {grey ${line}}`); const pushTraceLineDim = (line) => pushTraceLine(chalk`{dim ${line}}`); + const pushCodeFrameLine = (line) => push(' ' + line); let firstLineFormatted = firstLine; @@ -234,6 +263,12 @@ class LineWriter { pushTraceLineDim(formatFailureMessageTraceLine(description, relativeFilePath, row, column)); } else { pushTraceLine(formatFailureMessageTraceLine(description, relativeFilePath, row, column)); + + const codeFrame = formatCodeFrame(file, row, column); + + push(''); + codeFrame.split('\n').forEach((codeFrameLine) => pushCodeFrameLine(codeFrameLine)); + push(''); } } else { pushTraceLine(line); diff --git a/test/__snapshots__/LineWriter.spec.js.snap b/test/__snapshots__/LineWriter.spec.js.snap index f384f49..5bd2a83 100644 --- a/test/__snapshots__/LineWriter.spec.js.snap +++ b/test/__snapshots__/LineWriter.spec.js.snap @@ -15,7 +15,13 @@ exports[`LineWriter .errors() format stack trace 1`] = ` # Stack: # # at Something (../foo/bar.js:10:10) +# +# +# # at Foobar (../foo/bar2.js:20:20) +# +# +# # " `; From b44dccd7e59fdab1b2f9e699dd6fb3de3213e2e0 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 13 Nov 2017 11:02:38 +0000 Subject: [PATCH 2/8] fix: dont print codeFrame if empty --- src/LineWriter.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/LineWriter.js b/src/LineWriter.js index 3c94bd3..6794691 100644 --- a/src/LineWriter.js +++ b/src/LineWriter.js @@ -266,9 +266,11 @@ class LineWriter { const codeFrame = formatCodeFrame(file, row, column); - push(''); - codeFrame.split('\n').forEach((codeFrameLine) => pushCodeFrameLine(codeFrameLine)); - push(''); + if (codeFrame) { + push(''); + codeFrame.split('\n').forEach((codeFrameLine) => pushCodeFrameLine(codeFrameLine)); + push(''); + } } } else { pushTraceLine(line); From 1511ac307849715cc32e8c8d48e472b730472d58 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 13 Nov 2017 11:03:25 +0000 Subject: [PATCH 3/8] fix: dont print codeFrame if empty --- test/__snapshots__/LineWriter.spec.js.snap | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/__snapshots__/LineWriter.spec.js.snap b/test/__snapshots__/LineWriter.spec.js.snap index 5bd2a83..f384f49 100644 --- a/test/__snapshots__/LineWriter.spec.js.snap +++ b/test/__snapshots__/LineWriter.spec.js.snap @@ -15,13 +15,7 @@ exports[`LineWriter .errors() format stack trace 1`] = ` # Stack: # # at Something (../foo/bar.js:10:10) -# -# -# # at Foobar (../foo/bar2.js:20:20) -# -# -# # " `; From f904515173ed191c54a0ac7a5e684b8155d54f03 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 13 Nov 2017 11:53:24 +0000 Subject: [PATCH 4/8] refactor: create /format folder --- src/LineWriter.js | 46 ++------------------- src/format/formatCodeFrame.js | 24 +++++++++++ src/format/formatFailureMessageTraceLine.js | 6 +++ src/format/formatStatsBar.js | 23 +++++++++++ 4 files changed, 56 insertions(+), 43 deletions(-) create mode 100644 src/format/formatCodeFrame.js create mode 100644 src/format/formatFailureMessageTraceLine.js create mode 100644 src/format/formatStatsBar.js diff --git a/src/LineWriter.js b/src/LineWriter.js index 6794691..4303d59 100644 --- a/src/LineWriter.js +++ b/src/LineWriter.js @@ -1,9 +1,9 @@ /* eslint-disable complexity, no-use-extend-native/no-use-extend-native */ const path = require('path'); -const fs = require('fs'); const chalk = require('chalk'); -const {codeFrameColumns} = require('@babel/code-frame'); -const progressBar = require('./progressBar'); +const formatCodeFrame = require('./format/formatCodeFrame'); +const formatStatsBar = require('./format/formatStatsBar'); +const formatFailureMessageTraceLine = require('./format/formatFailureMessageTraceLine'); const REG_TRACE_LINE = /\s*(.+)\((.+):([0-9]+):([0-9]+)\)$/; const REG_INTERNALS = /^(node_modules|internal)\//; @@ -29,46 +29,6 @@ const PASS = chalk.supportsColor ? const formatComment = (line) => chalk`{hidden #} ${line}`; -const formatFailureMessageTraceLine = (description, relativeFilePath, row, column) => - chalk`${description}({cyan ${relativeFilePath}}:{black.bold ${row}}:{black.bold ${column}})`; - -const formatStatsBar = (percent, hasErrors) => { - let percentFormatted = Math.round(100 * percent) + '%'; - - percentFormatted = percentFormatted.padStart(3, ' '); - percentFormatted = percentFormatted.padEnd(4, ' '); - - const bar = progressBar(percent, hasErrors ? 'red' : 'grey.dim'); - - let textStyles = 'green'; - - if (hasErrors) { - textStyles = 'red.bold'; - } else if (percent < 1) { - textStyles = 'yellow'; - } - - return chalk`{${textStyles} ${percentFormatted}} ${bar}`; -}; - -const formatCodeFrame = (filePath, line, column) => { - try { - const source = fs.readFileSync(filePath, 'utf8'); - const location = { - start: { - column, - line - } - }; - - return codeFrameColumns(source, location, { - highlightCode: true - }); - } catch (error) { - return ''; - } -}; - class LineWriter { constructor (logger, root) { this.counter = 0; diff --git a/src/format/formatCodeFrame.js b/src/format/formatCodeFrame.js new file mode 100644 index 0000000..63d4645 --- /dev/null +++ b/src/format/formatCodeFrame.js @@ -0,0 +1,24 @@ +const fs = require('fs'); +const {codeFrameColumns} = require('@babel/code-frame'); + +const formatCodeFrame = (filePath, line, column) => { + try { + const source = fs.readFileSync(filePath, 'utf8'); + const location = { + start: { + column, + line + } + }; + + return codeFrameColumns(source, location, { + highlightCode: true, + linesAbove: 4, + linesBelow: 4 + }); + } catch (error) { + return ''; + } +}; + +module.exports = formatCodeFrame; diff --git a/src/format/formatFailureMessageTraceLine.js b/src/format/formatFailureMessageTraceLine.js new file mode 100644 index 0000000..2049384 --- /dev/null +++ b/src/format/formatFailureMessageTraceLine.js @@ -0,0 +1,6 @@ +const chalk = require('chalk'); + +const formatFailureMessageTraceLine = (description, relativeFilePath, row, column) => + chalk`${description}({cyan ${relativeFilePath}}:{black.bold ${row}}:{black.bold ${column}})`; + +module.exports = formatFailureMessageTraceLine; diff --git a/src/format/formatStatsBar.js b/src/format/formatStatsBar.js new file mode 100644 index 0000000..d2ff558 --- /dev/null +++ b/src/format/formatStatsBar.js @@ -0,0 +1,23 @@ +const chalk = require('chalk'); +const progressBar = require('../progressBar'); + +const formatStatsBar = (percent, hasErrors) => { + let percentFormatted = Math.round(100 * percent) + '%'; + + percentFormatted = percentFormatted.padStart(3, ' '); + percentFormatted = percentFormatted.padEnd(4, ' '); + + const bar = progressBar(percent, hasErrors ? 'red' : 'grey.dim'); + + let textStyles = 'green'; + + if (hasErrors) { + textStyles = 'red.bold'; + } else if (percent < 1) { + textStyles = 'yellow'; + } + + return chalk`{${textStyles} ${percentFormatted}} ${bar}`; +}; + +module.exports = formatStatsBar; From 3f81383dc71a27f909d967b3ed11a0ba8f544e7c Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 13 Nov 2017 15:23:45 +0000 Subject: [PATCH 5/8] fix: recognize more generic 'Expected' headers --- src/LineWriter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LineWriter.js b/src/LineWriter.js index 4303d59..48ee94a 100644 --- a/src/LineWriter.js +++ b/src/LineWriter.js @@ -10,7 +10,7 @@ const REG_INTERNALS = /^(node_modules|internal)\//; const REG_AT = /^\s*at/; const REG_ERROR = /^\s*Error:\s*/; const REG_RECEIVED = /^\s*Received:/; -const REG_EXPECTED = /^\s*Expected value to equal:/; +const REG_EXPECTED = /^\s*Expected value to[^:]+:/; const REG_DIFFERENCE = /^\s*Difference:/; const MDASH = '\u2014'; From d921d73ac5ed1ed96ffcaf61f9f328e95b31fa50 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 13 Nov 2017 18:04:05 +0000 Subject: [PATCH 6/8] feat: display error in test suite itself --- demo/broken.test.js | 1 + src/LineWriter.js | 4 ++-- src/TapReporter.js | 23 +++++++++++++---------- 3 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 demo/broken.test.js diff --git a/demo/broken.test.js b/demo/broken.test.js new file mode 100644 index 0000000..7323fa8 --- /dev/null +++ b/demo/broken.test.js @@ -0,0 +1 @@ +throw new Error('Error in test suite itself, reporter should report it properly.'); diff --git a/src/LineWriter.js b/src/LineWriter.js index 48ee94a..04a4d00 100644 --- a/src/LineWriter.js +++ b/src/LineWriter.js @@ -180,7 +180,7 @@ class LineWriter { }; const pushTraceLine = (line) => push(chalk` {grey ${line}}`); const pushTraceLineDim = (line) => pushTraceLine(chalk`{dim ${line}}`); - const pushCodeFrameLine = (line) => push(' ' + line); + const pushCodeFrameLine = (line) => push(' ' + line); let firstLineFormatted = firstLine; @@ -256,7 +256,7 @@ class LineWriter { push(' ' + line); break; case 'difference': - push(' ' + line); + push(' ' + line.trim()); break; default: push(line); diff --git a/src/TapReporter.js b/src/TapReporter.js index cc431f8..1c52a06 100755 --- a/src/TapReporter.js +++ b/src/TapReporter.js @@ -59,20 +59,23 @@ class TapReporter { } } - onTestResult (contexts, suite) { - const {testResults, testFilePath, numFailingTests} = suite; + onTestResult (test, testResult) { + const {testExecError, testResults, testFilePath, numFailingTests} = testResult; + const {dir, base} = path.parse(testFilePath); + const suiteFailed = Boolean(testExecError); - if (testFilePath) { - const {dir, base} = path.parse(testFilePath); - - if (!this.globalConfig.watch) { - this.writer.blank(); - } - this.writer.suite(numFailingTests > 0, dir, base); + if (!this.globalConfig.watch) { this.writer.blank(); } + this.writer.suite(numFailingTests > 0 || suiteFailed, dir, base); + this.writer.blank(); - testResults.forEach(this.onAssertionResult); + // If error in test suite itself. + if (suiteFailed) { + this.writer.errors([testExecError.stack]); + } else { + testResults.forEach(this.onAssertionResult); + } } onRunStart (results, options) { From c8d33a7f7bb446a0af1baf3dc4974c56c7a98b27 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 13 Nov 2017 18:14:39 +0000 Subject: [PATCH 7/8] feat: improve at-path stack trace formatting --- src/LineWriter.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/LineWriter.js b/src/LineWriter.js index 04a4d00..c7306ee 100644 --- a/src/LineWriter.js +++ b/src/LineWriter.js @@ -7,6 +7,7 @@ const formatFailureMessageTraceLine = require('./format/formatFailureMessageTrac const REG_TRACE_LINE = /\s*(.+)\((.+):([0-9]+):([0-9]+)\)$/; const REG_INTERNALS = /^(node_modules|internal)\//; +const REG_AT_PATH = /^\s*at (\/[^:]+):([0-9]+):([0-9]+)\s*$/; const REG_AT = /^\s*at/; const REG_ERROR = /^\s*Error:\s*/; const REG_RECEIVED = /^\s*Received:/; @@ -233,7 +234,16 @@ class LineWriter { } } } else { - pushTraceLine(line); + const atPathMatches = line.match(REG_AT_PATH); + const pushMethod = internalsStarted ? pushTraceLineDim : pushTraceLine; + + if (atPathMatches) { + const [, atPathPath, atPathRow, atPathColumn] = atPathMatches; + + pushMethod(chalk`at {cyan ${this.getPathRelativeToRoot(atPathPath)}}:{bold ${atPathRow}}:{bold ${atPathColumn}}`); + } else { + pushMethod(line); + } } } else { // eslint-disable-next-line no-lonely-if From 6cbddcd730b8d6ee8a7519bb8e3274e24c477fb1 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 13 Nov 2017 18:29:22 +0000 Subject: [PATCH 8/8] fix: fixtures --- src/format/formatCodeFrame.js | 12 ++++++++---- test/fixtures/failingTestSuite.json | 3 ++- test/fixtures/severalTestsSuite.json | 3 ++- test/fixtures/skippedTestSuite.json | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/format/formatCodeFrame.js b/src/format/formatCodeFrame.js index 63d4645..9d81ee4 100644 --- a/src/format/formatCodeFrame.js +++ b/src/format/formatCodeFrame.js @@ -1,7 +1,7 @@ const fs = require('fs'); const {codeFrameColumns} = require('@babel/code-frame'); -const formatCodeFrame = (filePath, line, column) => { +const formatCodeFrame = (filePath, line, column, margin = 4) => { try { const source = fs.readFileSync(filePath, 'utf8'); const location = { @@ -11,11 +11,15 @@ const formatCodeFrame = (filePath, line, column) => { } }; - return codeFrameColumns(source, location, { + const formatted = codeFrameColumns(source, location, { highlightCode: true, - linesAbove: 4, - linesBelow: 4 + linesAbove: margin, + linesBelow: margin }); + + // This below is because for some reason `@babel/code-frame` is not honoring + // `linesBelow` setting. + return formatted.split('\n').slice(0, 2 * margin + 1).join('\n'); } catch (error) { return ''; } diff --git a/test/fixtures/failingTestSuite.json b/test/fixtures/failingTestSuite.json index 03013c8..cf15be3 100644 --- a/test/fixtures/failingTestSuite.json +++ b/test/fixtures/failingTestSuite.json @@ -14,5 +14,6 @@ "name":"/jest-tap-reporter/test/index.spec.js", "startTime":1500478841634, "status":"failed", - "summary":"" + "summary":"", + "testFilePath": "/jest-tap-reporter/test/TapReporter.spec.js" } \ No newline at end of file diff --git a/test/fixtures/severalTestsSuite.json b/test/fixtures/severalTestsSuite.json index 24f6baf..bcdae5e 100644 --- a/test/fixtures/severalTestsSuite.json +++ b/test/fixtures/severalTestsSuite.json @@ -55,5 +55,6 @@ "name":"/jest-tap-reporter/test/index.spec.js", "startTime":1500478996144, "status":"passed", - "summary":"" + "summary":"", + "testFilePath": "/jest-tap-reporter/test/TapReporter.spec.js" } \ No newline at end of file diff --git a/test/fixtures/skippedTestSuite.json b/test/fixtures/skippedTestSuite.json index 60ea16d..76d1eed 100644 --- a/test/fixtures/skippedTestSuite.json +++ b/test/fixtures/skippedTestSuite.json @@ -13,5 +13,6 @@ "name":"/jest-tap-reporter/test/index.spec.js", "startTime":1500479077405, "status":"passed", - "summary":"" + "summary":"", + "testFilePath": "/jest-tap-reporter/test/TapReporter.spec.js" } \ No newline at end of file