Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 36.4k
test_runner: display failed test stack trace with dot reporter#52655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,97 +1,35 @@ | ||
| 'use strict'; | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto | ||
| const { | ||
| ArrayPrototypeJoin, | ||
| ArrayPrototypePop, | ||
| ArrayPrototypePush, | ||
| ArrayPrototypeShift, | ||
| ArrayPrototypeUnshift, | ||
| RegExpPrototypeSymbolSplit, | ||
| SafeMap, | ||
| StringPrototypeRepeat, | ||
| hardenRegExp, | ||
| } = primordials; | ||
| const assert = require('assert'); | ||
| const Transform = require('internal/streams/transform'); | ||
| const { inspectWithNoCustomRetry } = require('internal/errors'); | ||
| const colors = require('internal/util/colors'); | ||
| const { kSubtestsFailed } = require('internal/test_runner/test'); | ||
| const { getCoverageReport } = require('internal/test_runner/utils'); | ||
| const { relative } = require('path'); | ||
| const { | ||
| formatTestReport, | ||
| indent, | ||
| reporterColorMap, | ||
| reporterUnicodeSymbolMap, | ||
| } = require('internal/test_runner/reporter/utils'); | ||
| const symbols = { | ||
| '__proto__': null, | ||
| 'test:fail': '\u2716 ', | ||
| 'test:pass': '\u2714 ', | ||
| 'test:diagnostic': '\u2139 ', | ||
| 'test:coverage': '\u2139 ', | ||
| 'arrow:right': '\u25B6 ', | ||
| 'hyphen:minus': '\uFE63 ', | ||
| }; | ||
| class SpecReporter extends Transform { | ||
| #stack = []; | ||
| #reported = []; | ||
| #indentMemo = new SafeMap(); | ||
| #failedTests = []; | ||
| #cwd = process.cwd(); | ||
| #inspectOptions; | ||
| #colors; | ||
| constructor() { | ||
| super({ __proto__: null, writableObjectMode: true }); | ||
| colors.refresh(); | ||
| this.#inspectOptions = { __proto__: null, colors: colors.shouldColorize(process.stdout), breakLength: Infinity }; | ||
| this.#colors = { | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moving colors to utils breaks when there is no TTY, see https://ci.nodejs.org/job/node-test-commit-custom-suites-freestyle/35939/console:
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changing ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
are you suggesting to replace the map with a function in utils.js or modify the constructor in spec.js? Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
with Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
either will work I think Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this might be an easier & cleaner fix: diff --git a/lib/internal/test_runner/reporter/utils.js b/lib/internal/test_runner/reporter/utils.js
index caa35959ee..42c4ffa3cd 100644
--- a/lib/internal/test_runner/reporter/utils.js+++ b/lib/internal/test_runner/reporter/utils.js@@ -28,9 +28,15 @@ const reporterUnicodeSymbolMap = {
const reporterColorMap = {
'__proto__': null,
- 'test:fail': colors.red,- 'test:pass': colors.green,- 'test:diagnostic': colors.blue,+ get 'test:fail'() {+ return colors.red;+ },+ get 'test:pass'() {+ return colors.green;+ },+ get 'test:diagnostic'() {+ return colors.blue;+ },
};ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Still passes all tests :( ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have made the suggested changes, please let me know if I need to add anything else. | ||
| '__proto__': null, | ||
| 'test:fail': colors.red, | ||
| 'test:pass': colors.green, | ||
| 'test:diagnostic': colors.blue, | ||
| }; | ||
| } | ||
| #indent(nesting) { | ||
| let value = this.#indentMemo.get(nesting); | ||
| if (value === undefined) { | ||
| value = StringPrototypeRepeat(' ', nesting); | ||
| this.#indentMemo.set(nesting, value); | ||
| } | ||
| return value; | ||
| } | ||
| #formatError(error, indent) { | ||
| if (!error) return ''; | ||
| const err = error.code === 'ERR_TEST_FAILURE' ? error.cause : error; | ||
| const message = ArrayPrototypeJoin( | ||
| RegExpPrototypeSymbolSplit( | ||
| hardenRegExp(/\r?\n/), | ||
| inspectWithNoCustomRetry(err, this.#inspectOptions), | ||
| ), `\n${indent} `); | ||
| return `\n${indent} ${message}\n`; | ||
| } | ||
| #formatTestReport(type, data, prefix = '', indent = '', hasChildren = false) { | ||
| let color = this.#colors[type] ?? colors.white; | ||
| let symbol = symbols[type] ?? ' '; | ||
| const { skip, todo } = data; | ||
| const duration_ms = data.details?.duration_ms ? ` ${colors.gray}(${data.details.duration_ms}ms)${colors.white}` : ''; | ||
| let title = `${data.name}${duration_ms}`; | ||
| if (skip !== undefined) { | ||
| title += ` # ${typeof skip === 'string' && skip.length ? skip : 'SKIP'}`; | ||
| } else if (todo !== undefined) { | ||
| title += ` # ${typeof todo === 'string' && todo.length ? todo : 'TODO'}`; | ||
| } | ||
| const error = this.#formatError(data.details?.error, indent); | ||
| if (hasChildren) { | ||
| // If this test has had children - it was already reported, so slightly modify the output | ||
| const err = !error || data.details?.error?.failureType === 'subtestsFailed' ? '' : `\n${error}`; | ||
| return `${prefix}${indent}${color}${symbols['arrow:right']}${colors.white}${title}${err}`; | ||
| } | ||
| if (skip !== undefined) { | ||
| color = colors.gray; | ||
| symbol = symbols['hyphen:minus']; | ||
| } | ||
| return `${prefix}${indent}${color}${symbol}${title}${colors.white}${error}`; | ||
| } | ||
| #handleTestReportEvent(type, data) { | ||
| const subtest = ArrayPrototypeShift(this.#stack); // This is the matching `test:start` event | ||
| if (subtest) { | ||
| @@ -106,15 +44,15 @@ class SpecReporter extends Transform { | ||
| assert(parent.type === 'test:start'); | ||
| const msg = parent.data; | ||
| ArrayPrototypeUnshift(this.#reported, msg); | ||
| prefix += `${this.#indent(msg.nesting)}${symbols['arrow:right']}${msg.name}\n`; | ||
| prefix += `${indent(msg.nesting)}${reporterUnicodeSymbolMap['arrow:right']}${msg.name}\n`; | ||
| } | ||
| let hasChildren = false; | ||
| if (this.#reported[0] && this.#reported[0].nesting === data.nesting && this.#reported[0].name === data.name) { | ||
| ArrayPrototypeShift(this.#reported); | ||
| hasChildren = true; | ||
| } | ||
| const indent = this.#indent(data.nesting); | ||
| return `${this.#formatTestReport(type, data, prefix, indent, hasChildren)}\n`; | ||
| const indentation = indent(data.nesting); | ||
| return `${formatTestReport(type, data, prefix, indentation, hasChildren)}\n`; | ||
| } | ||
| #handleEvent({ type, data }) { | ||
| switch (type) { | ||
| @@ -132,9 +70,10 @@ class SpecReporter extends Transform { | ||
| case 'test:stdout': | ||
| return data.message; | ||
| case 'test:diagnostic': | ||
| return `${this.#colors[type]}${this.#indent(data.nesting)}${symbols[type]}${data.message}${colors.white}\n`; | ||
| return `${reporterColorMap[type]}${indent(data.nesting)}${reporterUnicodeSymbolMap[type]}${data.message}${colors.white}\n`; | ||
| case 'test:coverage': | ||
| return getCoverageReport(this.#indent(data.nesting), data.summary, symbols['test:coverage'], colors.blue, true); | ||
| return getCoverageReport(indent(data.nesting), data.summary, | ||
| reporterUnicodeSymbolMap['test:coverage'], colors.blue, true); | ||
| } | ||
| } | ||
| _transform({ type, data }, encoding, callback) { | ||
| @@ -145,10 +84,10 @@ class SpecReporter extends Transform { | ||
| callback(null, ''); | ||
| return; | ||
| } | ||
| const results = [`\n${this.#colors['test:fail']}${symbols['test:fail']}failing tests:${colors.white}\n`]; | ||
| const results = [`\n${reporterColorMap['test:fail']}${reporterUnicodeSymbolMap['test:fail']}failing tests:${colors.white}\n`]; | ||
| for (let i = 0; i < this.#failedTests.length; i++) { | ||
| const test = this.#failedTests[i]; | ||
| const formattedErr = this.#formatTestReport('test:fail', test); | ||
| const formattedErr = formatTestReport('test:fail', test); | ||
| if (test.file) { | ||
| const relPath = relative(this.#cwd, test.file); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| 'use strict'; | ||
| const { | ||
| ArrayPrototypeJoin, | ||
| RegExpPrototypeSymbolSplit, | ||
| SafeMap, | ||
| StringPrototypeRepeat, | ||
| hardenRegExp, | ||
| } = primordials; | ||
| const colors = require('internal/util/colors'); | ||
| const { inspectWithNoCustomRetry } = require('internal/errors'); | ||
| const indentMemo = new SafeMap(); | ||
| const inspectOptions = { | ||
| __proto__: null, | ||
| colors: colors.shouldColorize(process.stdout), | ||
| breakLength: Infinity, | ||
| }; | ||
| const reporterUnicodeSymbolMap = { | ||
| '__proto__': null, | ||
MoLow marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| 'test:fail': '\u2716 ', | ||
| 'test:pass': '\u2714 ', | ||
| 'test:diagnostic': '\u2139 ', | ||
| 'test:coverage': '\u2139 ', | ||
| 'arrow:right': '\u25B6 ', | ||
| 'hyphen:minus': '\uFE63 ', | ||
| }; | ||
| const reporterColorMap = { | ||
| '__proto__': null, | ||
| get 'test:fail'() { | ||
| return colors.red; | ||
| }, | ||
| get 'test:pass'() { | ||
| return colors.green; | ||
| }, | ||
| get 'test:diagnostic'() { | ||
| return colors.blue; | ||
| }, | ||
| }; | ||
| function indent(nesting) { | ||
| let value = indentMemo.get(nesting); | ||
| if (value === undefined) { | ||
| value = StringPrototypeRepeat(' ', nesting); | ||
| indentMemo.set(nesting, value); | ||
| } | ||
| return value; | ||
| } | ||
| function formatError(error, indent) { | ||
| if (!error) return ''; | ||
| const err = error.code === 'ERR_TEST_FAILURE' ? error.cause : error; | ||
| const message = ArrayPrototypeJoin( | ||
| RegExpPrototypeSymbolSplit( | ||
| hardenRegExp(/\r?\n/), | ||
| inspectWithNoCustomRetry(err, inspectOptions), | ||
| ), `\n${indent} `); | ||
| return `\n${indent} ${message}\n`; | ||
| } | ||
| function formatTestReport(type, data, prefix = '', indent = '', hasChildren = false) { | ||
| let color = reporterColorMap[type] ?? colors.white; | ||
| let symbol = reporterUnicodeSymbolMap[type] ?? ' '; | ||
| const { skip, todo } = data; | ||
| const duration_ms = data.details?.duration_ms ? ` ${colors.gray}(${data.details.duration_ms}ms)${colors.white}` : ''; | ||
| let title = `${data.name}${duration_ms}`; | ||
| if (skip !== undefined) { | ||
| title += ` # ${typeof skip === 'string' && skip.length ? skip : 'SKIP'}`; | ||
| } else if (todo !== undefined) { | ||
| title += ` # ${typeof todo === 'string' && todo.length ? todo : 'TODO'}`; | ||
| } | ||
| const error = formatError(data.details?.error, indent); | ||
| if (hasChildren) { | ||
| // If this test has had children - it was already reported, so slightly modify the output | ||
| const err = !error || data.details?.error?.failureType === 'subtestsFailed' ? '' : `\n${error}`; | ||
| return `${prefix}${indent}${color}${reporterUnicodeSymbolMap['arrow:right']}${colors.white}${title}${err}`; | ||
| } | ||
| if (skip !== undefined) { | ||
| color = colors.gray; | ||
| symbol = reporterUnicodeSymbolMap['hyphen:minus']; | ||
| } | ||
| return `${prefix}${indent}${color}${symbol}${title}${colors.white}${error}`; | ||
| } | ||
| module.exports = { | ||
| __proto__: null, | ||
| reporterUnicodeSymbolMap, | ||
| reporterColorMap, | ||
| formatTestReport, | ||
| indent, | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change seems irrelevant