-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CI] Produce junit test reports (#15281)
* [mocha] use custom reporter for legible results in jenkins * [jest] use custom result processor for legible results in jenkins * [karma] enable junit output on CI * [mocha/junitReporter] accept rootDirectory as configuration * [jest/reporter] use reporters option added in jest 20 * [toolingLog] remove black/white specific colors * [dev/mocha/junit] no reason for junit to be a "reporter" * typos * [dev/mocha/junit] use else if * [karma/junit] use string#replace for explicitness * [junit] use test file path as "classname" * [ftr/mocha] no longer a "console" specific reporter
- Loading branch information
Showing
32 changed files
with
332 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
export { createToolingLog } from './tooling_log'; | ||
export { | ||
createAutoJunitReporter, | ||
setupJunitReportGeneration, | ||
} from './mocha'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { resolve, dirname, relative } from 'path'; | ||
import { writeFileSync } from 'fs'; | ||
|
||
import mkdirp from 'mkdirp'; | ||
import xmlBuilder from 'xmlbuilder'; | ||
|
||
const ROOT_DIR = dirname(require.resolve('../../../package.json')); | ||
|
||
/** | ||
* Jest reporter that produces JUnit report when running on CI | ||
* @class JestJunitReporter | ||
*/ | ||
export default class JestJunitReporter { | ||
constructor(globalConfig, options = {}) { | ||
const { | ||
reportName = 'Jest Tests', | ||
rootDirectory = ROOT_DIR | ||
} = options; | ||
|
||
this._reportName = reportName; | ||
this._rootDirectory = rootDirectory; | ||
} | ||
|
||
/** | ||
* Called by jest when all tests complete | ||
* @param {Object} contexts | ||
* @param {JestResults} results see https://facebook.github.io/jest/docs/en/configuration.html#testresultsprocessor-string | ||
* @return {undefined} | ||
*/ | ||
onRunComplete(contexts, results) { | ||
if (!process.env.CI) { | ||
return; | ||
} | ||
|
||
const reportName = this._reportName; | ||
const rootDirectory = this._rootDirectory; | ||
const root = xmlBuilder.create( | ||
'testsuites', | ||
{ encoding: 'utf-8' }, | ||
{}, | ||
{ skipNullAttributes: true } | ||
); | ||
|
||
const msToIso = ms => ms ? new Date(ms).toISOString().slice(0, -5) : undefined; | ||
const msToSec = ms => ms ? (ms / 1000).toFixed(3) : undefined; | ||
|
||
root.att({ | ||
name: 'jest', | ||
timestamp: msToIso(results.startTime), | ||
time: msToSec(Date.now() - results.startTime), | ||
tests: results.numTotalTests, | ||
failures: results.numFailedTests, | ||
skipped: results.numPendingTests, | ||
}); | ||
|
||
// top level test results are the files/suites | ||
results.testResults.forEach(suite => { | ||
const suiteEl = root.ele('testsuite', { | ||
name: relative(rootDirectory, suite.testFilePath), | ||
timestamp: msToIso(suite.perfStats.start), | ||
time: msToSec(suite.perfStats.end - suite.perfStats.start), | ||
tests: suite.testResults.length, | ||
failures: suite.numFailedTests, | ||
skipped: suite.numPendingTests, | ||
file: suite.testFilePath | ||
}); | ||
|
||
// nested in there are the tests in that file | ||
const relativePath = dirname(relative(rootDirectory, suite.testFilePath)); | ||
const classname = `${reportName}.${relativePath.replace(/\./g, '·')}`; | ||
suite.testResults.forEach(test => { | ||
const testEl = suiteEl.ele('testcase', { | ||
classname, | ||
name: [...test.ancestorTitles, test.title].join(' '), | ||
time: msToSec(test.duration) | ||
}); | ||
|
||
test.failureMessages.forEach((message) => { | ||
testEl.ele('failure').dat(message); | ||
}); | ||
|
||
if (test.status === 'pending') { | ||
testEl.ele('skipped'); | ||
} | ||
}); | ||
}); | ||
|
||
const reportPath = resolve(rootDirectory, `target/junit/${reportName}.xml`); | ||
const reportXML = root.end({ | ||
pretty: true, | ||
indent: ' ', | ||
newline: '\n', | ||
spacebeforeslash: '' | ||
}); | ||
|
||
mkdirp.sync(dirname(reportPath)); | ||
writeFileSync(reportPath, reportXML, 'utf8'); | ||
} | ||
} |
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion
2
src/jest/setup/babel_polyfill.js → src/dev/jest/setup/babel_polyfill.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
// Note: In theory importing the polyfill should not be needed, as Babel should | ||
// include the necessary polyfills when using `babel-preset-env`, but for some | ||
// reason it did not work. See https://github.com/elastic/kibana/issues/14506 | ||
import '../../babel-register/polyfill'; | ||
import '../../../babel-register/polyfill'; |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import mocha from 'mocha'; | ||
import { setupJunitReportGeneration } from './junit_report_generation'; | ||
|
||
const MochaSpecReporter = mocha.reporters.spec; | ||
|
||
export function createAutoJunitReporter(junitReportOptions) { | ||
return class createAutoJunitReporter { | ||
constructor(runner, options) { | ||
// setup a spec reporter for console output | ||
new MochaSpecReporter(runner, options); | ||
|
||
// in CI we also setup the Junit reporter | ||
if (process.env.CI) { | ||
setupJunitReportGeneration(runner, junitReportOptions); | ||
} | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { createAutoJunitReporter } from './auto_junit_reporter'; | ||
export { setupJunitReportGeneration } from './junit_report_generation'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { resolve, dirname, relative } from 'path'; | ||
import { writeFileSync } from 'fs'; | ||
import { inspect } from 'util'; | ||
|
||
import mkdirp from 'mkdirp'; | ||
import xmlBuilder from 'xmlbuilder'; | ||
|
||
export function setupJunitReportGeneration(runner, options = {}) { | ||
const { | ||
reportName = 'Unnamed Mocha Tests', | ||
rootDirectory = dirname(require.resolve('../../../package.json')), | ||
} = options; | ||
|
||
const rootSuite = runner.suite; | ||
const isTestFailed = test => test.state === 'failed'; | ||
const isTestPending = test => !!test.pending; | ||
const returnTrue = () => true; | ||
|
||
const getDuration = (node) => ( | ||
node.startTime && node.endTime | ||
? ((node.endTime - node.startTime) / 1000).toFixed(3) | ||
: null | ||
); | ||
|
||
const getTimestamp = (node) => ( | ||
node.startTime | ||
? new Date(node.startTime).toISOString().slice(0, -5) | ||
: null | ||
); | ||
|
||
const countTests = (suite, filter = returnTrue) => ( | ||
suite.suites.reduce((sum, suite) => ( | ||
sum + countTests(suite, filter) | ||
), suite.tests.filter(filter).length) | ||
); | ||
|
||
const getFullTitle = node => { | ||
const parentTitle = node.parent && getFullTitle(node.parent); | ||
return parentTitle ? `${parentTitle} ${node.title}` : node.title; | ||
}; | ||
|
||
const getPath = node => { | ||
if (node.file) { | ||
return relative(rootDirectory, node.file); | ||
} | ||
|
||
if (node.parent) { | ||
return getPath(node.parent); | ||
} | ||
|
||
return 'unknown'; | ||
}; | ||
|
||
runner.on('start', () => { | ||
rootSuite.startTime = Date.now(); | ||
}); | ||
|
||
runner.on('suite', (suite) => { | ||
suite.startTime = Date.now(); | ||
}); | ||
|
||
runner.on('test', (test) => { | ||
test.startTime = Date.now(); | ||
}); | ||
|
||
runner.on('test end', (test) => { | ||
test.endTime = Date.now(); | ||
}); | ||
|
||
runner.on('suite end', (suite) => { | ||
suite.endTime = Date.now(); | ||
}); | ||
|
||
runner.on('end', () => { | ||
rootSuite.endTime = Date.now(); | ||
const builder = xmlBuilder.create( | ||
'testsuites', | ||
{ encoding: 'utf-8' }, | ||
{}, | ||
{ skipNullAttributes: true } | ||
); | ||
|
||
function addSuite(parent, suite) { | ||
const attributes = { | ||
name: suite.title, | ||
timestamp: getTimestamp(suite), | ||
time: getDuration(suite), | ||
tests: countTests(suite), | ||
failures: countTests(suite, isTestFailed), | ||
skipped: countTests(suite, isTestPending), | ||
file: suite.file | ||
}; | ||
|
||
const el = suite === rootSuite | ||
? parent.att(attributes) | ||
: parent.ele('testsuite', attributes); | ||
|
||
suite.suites.forEach(childSuite => { | ||
addSuite(el, childSuite); | ||
}); | ||
|
||
suite.tests.forEach(test => { | ||
addTest(el, test); | ||
}); | ||
} | ||
|
||
function addTest(parent, test) { | ||
const el = parent.ele('testcase', { | ||
name: getFullTitle(test), | ||
classname: `${reportName}.${getPath(test).replace(/\./g, '·')}`, | ||
time: getDuration(test), | ||
}); | ||
|
||
if (isTestFailed(test)) { | ||
el | ||
.ele('failure') | ||
.dat(inspect(test.err)); | ||
} else if (isTestPending(test)) { | ||
el.ele('skipped'); | ||
} | ||
} | ||
|
||
addSuite(builder, rootSuite); | ||
|
||
const reportPath = resolve(rootDirectory, `target/junit/${reportName}.xml`); | ||
const reportXML = builder.end({ | ||
pretty: true, | ||
indent: ' ', | ||
newline: '\n', | ||
spacebeforeslash: '' | ||
}); | ||
|
||
mkdirp.sync(dirname(reportPath)); | ||
writeFileSync(reportPath, reportXML, 'utf8'); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.