test
Version:
Node.js 18's node:test, as an npm package
111 lines (99 loc) • 3.72 kB
JavaScript
// https://github.com/nodejs/node/blob/a1b27b25bb01aadd3fd2714e4b136db11b7eb85a/lib/test/reporter/spec.js
const {
ArrayPrototypeJoin,
ArrayPrototypePop,
ArrayPrototypeShift,
ArrayPrototypeUnshift,
hardenRegExp,
RegExpPrototypeSymbolSplit,
SafeMap,
StringPrototypeRepeat
} = require('#internal/per_context/primordials')
const assert = require('assert')
const Transform = require('stream').Transform
const { inspectWithNoCustomRetry } = require('#internal/errors')
const { green, blue, red, white, gray } = require('#internal/util/colors')
const inspectOptions = { __proto__: null, colors: true, breakLength: Infinity }
const colors = {
__proto__: null,
'test:fail': red,
'test:pass': green,
'test:diagnostic': blue
}
const symbols = {
__proto__: null,
'test:fail': '\u2716 ',
'test:pass': '\u2714 ',
'test:diagnostic': '\u2139 ',
'arrow:right': '\u25B6 '
}
class SpecReporter extends Transform {
#stack = []
#reported = []
#indentMemo = new SafeMap()
constructor () {
super({ writableObjectMode: true })
}
#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, inspectOptions)
), `\n${indent} `)
return `\n${indent} ${message}\n`
}
#handleEvent ({ type, data }) {
const color = colors[type] ?? white
const symbol = symbols[type] ?? ' '
switch (type) {
case 'test:fail':
case 'test:pass': {
const subtest = ArrayPrototypeShift(this.#stack) // This is the matching `test:start` event
if (subtest) {
assert(subtest.type === 'test:start')
assert(subtest.data.nesting === data.nesting)
assert(subtest.data.name === data.name)
}
let prefix = ''
while (this.#stack.length) {
// Report all the parent `test:start` events
const parent = ArrayPrototypePop(this.#stack)
assert(parent.type === 'test:start')
const msg = parent.data
ArrayPrototypeUnshift(this.#reported, msg)
prefix += `${this.#indent(msg.nesting)}${symbols['arrow:right']}${msg.name}\n`
}
const indent = this.#indent(data.nesting)
const duration_ms = data.details?.duration_ms ? ` ${gray}(${data.details.duration_ms}ms)${white}` : '' // eslint-disable-line camelcase
const title = `${data.name}${duration_ms}` // eslint-disable-line camelcase
if (this.#reported[0] && this.#reported[0].nesting === data.nesting && this.#reported[0].name === data.name) {
// If this test has had children - it was already reporter, so slightly modify the output
ArrayPrototypeShift(this.#reported)
return `${prefix}${indent}${color}${symbols['arrow:right']}${white}${title}\n\n`
}
const error = this.#formatError(data.details?.error, indent)
return `${prefix}${indent}${color}${symbol}${title}${error}${white}\n`
}
case 'test:start':
ArrayPrototypeUnshift(this.#stack, { __proto__: null, data, type })
break
case 'test:diagnostic':
return `${color}${this.#indent(data.nesting)}${symbol}${data.message}${white}\n`
}
}
_transform ({ type, data }, encoding, callback) {
callback(null, this.#handleEvent({ type, data }))
}
}
module.exports = SpecReporter