buildkite-test-collector
Version:
Buildkite Test Engine collectors for JavaScript test frameworks
114 lines (101 loc) • 3.52 kB
JavaScript
const { randomUUID } = require('crypto')
const CI = require('../util/ci')
const Paths = require('../util/paths')
const Mocha = require('mocha')
const Runnable = require('mocha/lib/runnable')
const uploadTestResults = require('../util/uploadTestResults')
const failureExpanded = require('../util/failureExpanded')
const {
EVENT_TEST_BEGIN,
EVENT_TEST_END,
} = Mocha.Runner.constants
const {
STATE_PASSED,
STATE_PENDING,
STATE_FAILED,
} = Runnable.constants
class MochaBuildkiteTestEngineReporter {
constructor(runner, options) {
this._options = { token: process.env[`${options.reporterOptions.token_name}`]}
this._testResults = []
this._testEnv = (new CI()).env();
this._tags = options.reporterOptions.tags;
this._paths = new Paths({ cwd: process.cwd() }, this._testEnv.location_prefix)
runner
.on(EVENT_TEST_BEGIN, (test) => {
this.testStarted(test)
})
.on(EVENT_TEST_END, (test) => {
this.testFinished(test)
})
}
testStarted(test) {
test.testEngineId = randomUUID()
test.startAt = performance.now() / 1000
}
testFinished(test) {
const failureReason = test.err !== undefined ? test.err.toString() : undefined
const prefixedTestPath = this._paths.prefixTestPath(this.getRootParentFile(test))
this._testResults.push({
'id': test.testEngineId,
'name': test.title,
'scope': this.scope(test),
'file_name': prefixedTestPath,
'location': prefixedTestPath,
'result': this.testEngineResult(test.state),
'failure_reason': failureReason,
'failure_expanded': failureExpanded(test.err == undefined ? [] : (test.err.multiple || [test.err])),
'history': {
'section': 'top',
'start_at': test.startAt,
'end_at': performance.now() / 1000,
'duration': test.duration / 1000,
}
})
}
// This function will be called when Mocha has finished running all tests.
// It behaves similarly to runner.on(EVENT_RUN_END, ...).
// The difference is that runner.on(EVENT_RUN_END, ...) will exit the process immediately when `--exit` option is used.
// On the other hand, done() can be used to wait for an async process and programatically exit the process, even when `--exit` option is used.
// ref: https://github.com/mochajs/mocha/pull/1218
done(_failures, exit) {
uploadTestResults(this._testEnv, this._tags, this._testResults, this._options, exit)
}
testEngineResult(state) {
// Mocha test statuses:
// - passed
// - failed
// - pending
//
// Buildkite Test Engine execution results:
// - passed
// - failed
// - pending
// - skipped
// - unknown
return {
[STATE_PASSED]: 'passed',
[STATE_PENDING]: 'pending',
[STATE_FAILED]: 'failed',
}[state]
}
scope(test) {
const titlePath = test.titlePath()
// titlePath returns an array of the scope + the test title.
// as the test title is the last array item, we just remove it
// and then join the rest of the array as a space separated string
return titlePath.slice(0, titlePath.length - 1).join(' ')
}
// Recursively find the root parent, and return the parents file
// This is required as test.file can be undefined in some tests on cypress
getRootParentFile(test) {
if (test.file) {
return test.file
}
if (test.parent) {
return this.getRootParentFile(test.parent)
}
return null
}
}
module.exports = MochaBuildkiteTestEngineReporter