From b4bd640a17263ed2b62c5a0bf3b320b414cab4b6 Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 5 Dec 2017 18:21:38 -0700 Subject: [PATCH] [6.x] [CI] Produce junit test reports (#15281) (#15437) * [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 --- package.json | 2 + scripts/jest.js | 2 +- src/dev/index.js | 4 + .../jest/babel_transform.js} | 2 +- src/{ => dev}/jest/cli.js | 0 src/{ => dev}/jest/config.json | 16 ++- src/dev/jest/junit_reporter.js | 99 +++++++++++++ src/{ => dev}/jest/mocks/file_mock.js | 0 src/{ => dev}/jest/mocks/style_mock.js | 0 src/{ => dev}/jest/setup/babel_polyfill.js | 2 +- .../setup/request_animation_frame_polyfill.js | 0 src/dev/mocha/auto_junit_reporter.js | 18 +++ src/dev/mocha/index.js | 2 + src/dev/mocha/junit_report_generation.js | 136 ++++++++++++++++++ src/dev/precommit_hook/casing_check_config.js | 1 - src/dev/tooling_log/tooling_log.js | 4 +- .../lib/config/schema.js | 9 +- .../reporter}/colors.js | 8 +- .../lib/mocha/reporter/index.js | 1 + .../console_reporter => mocha/reporter}/ms.js | 0 .../reporter/reporter.js} | 15 +- .../reporter}/symbols.js | 0 .../reporter}/write_epilogue.js | 0 .../lib/mocha/setup_mocha.js | 5 +- .../lib/reporters/console_reporter/index.js | 1 - .../lib/reporters/index.js | 1 - tasks/config/karma.js | 17 ++- tasks/config/simplemocha.js | 11 +- test/api_integration/config.js | 3 + test/functional/config.js | 3 + test/mocha.opts | 2 +- test/mocha_setup.js | 1 - 32 files changed, 332 insertions(+), 33 deletions(-) rename src/{jest/babelTransform.js => dev/jest/babel_transform.js} (70%) rename src/{ => dev}/jest/cli.js (100%) rename src/{ => dev}/jest/config.json (74%) create mode 100644 src/dev/jest/junit_reporter.js rename src/{ => dev}/jest/mocks/file_mock.js (100%) rename src/{ => dev}/jest/mocks/style_mock.js (100%) rename src/{ => dev}/jest/setup/babel_polyfill.js (84%) rename src/{ => dev}/jest/setup/request_animation_frame_polyfill.js (100%) create mode 100644 src/dev/mocha/auto_junit_reporter.js create mode 100644 src/dev/mocha/index.js create mode 100644 src/dev/mocha/junit_report_generation.js rename src/functional_test_runner/lib/{reporters/console_reporter => mocha/reporter}/colors.js (57%) create mode 100644 src/functional_test_runner/lib/mocha/reporter/index.js rename src/functional_test_runner/lib/{reporters/console_reporter => mocha/reporter}/ms.js (100%) rename src/functional_test_runner/lib/{reporters/console_reporter/console_reporter.js => mocha/reporter/reporter.js} (86%) rename src/functional_test_runner/lib/{reporters/console_reporter => mocha/reporter}/symbols.js (100%) rename src/functional_test_runner/lib/{reporters/console_reporter => mocha/reporter}/write_epilogue.js (100%) delete mode 100644 src/functional_test_runner/lib/reporters/console_reporter/index.js delete mode 100644 src/functional_test_runner/lib/reporters/index.js delete mode 100644 test/mocha_setup.js diff --git a/package.json b/package.json index a6001420f9b7..a163d01fb407 100644 --- a/package.json +++ b/package.json @@ -268,6 +268,7 @@ "karma-coverage": "1.1.1", "karma-firefox-launcher": "1.0.1", "karma-ie-launcher": "1.0.0", + "karma-junit-reporter": "1.2.0", "karma-mocha": "1.3.0", "karma-safari-launcher": "1.0.0", "keymirror": "0.1.1", @@ -294,6 +295,7 @@ "supertest-as-promised": "2.0.2", "tree-kill": "1.1.0", "webpack-dev-server": "2.9.1", + "xmlbuilder": "9.0.4", "yeoman-generator": "1.1.1", "yo": "2.0.0" }, diff --git a/scripts/jest.js b/scripts/jest.js index 751591ad528d..5f130a843910 100755 --- a/scripts/jest.js +++ b/scripts/jest.js @@ -11,4 +11,4 @@ // See all cli options in https://facebook.github.io/jest/docs/cli.html require('../src/babel-register'); -require('../src/jest/cli'); +require('../src/dev/jest/cli'); diff --git a/src/dev/index.js b/src/dev/index.js index 28f67ed09d3a..d42cd5388c39 100644 --- a/src/dev/index.js +++ b/src/dev/index.js @@ -1 +1,5 @@ export { createToolingLog } from './tooling_log'; +export { + createAutoJunitReporter, + setupJunitReportGeneration, +} from './mocha'; diff --git a/src/jest/babelTransform.js b/src/dev/jest/babel_transform.js similarity index 70% rename from src/jest/babelTransform.js rename to src/dev/jest/babel_transform.js index c2b9485295bf..9a649df360da 100644 --- a/src/jest/babelTransform.js +++ b/src/dev/jest/babel_transform.js @@ -2,6 +2,6 @@ const babelJest = require('babel-jest'); module.exports = babelJest.createTransformer({ presets: [ - require.resolve('../babel-preset/node') + require.resolve('../../babel-preset/node') ] }); diff --git a/src/jest/cli.js b/src/dev/jest/cli.js similarity index 100% rename from src/jest/cli.js rename to src/dev/jest/cli.js diff --git a/src/jest/config.json b/src/dev/jest/config.json similarity index 74% rename from src/jest/config.json rename to src/dev/jest/config.json index 417ebd2951d6..5e5c9f52cb10 100644 --- a/src/jest/config.json +++ b/src/dev/jest/config.json @@ -1,5 +1,5 @@ { - "rootDir": "../../", + "rootDir": "../../..", "roots": [ "/src/ui/public", "/src/core_plugins", @@ -18,12 +18,12 @@ "^ui_framework/services": "/ui_framework/services", "^ui_framework/src/test": "/ui_framework/src/test", "^ui/(.*)": "/src/ui/public/$1", - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/jest/mocks/file_mock.js", - "\\.(css|less|scss)$": "/src/jest/mocks/style_mock.js" + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/dev/jest/mocks/file_mock.js", + "\\.(css|less|scss)$": "/src/dev/jest/mocks/style_mock.js" }, "setupFiles": [ - "/src/jest/setup/babel_polyfill.js", - "/src/jest/setup/request_animation_frame_polyfill.js" + "/src/dev/jest/setup/babel_polyfill.js", + "/src/dev/jest/setup/request_animation_frame_polyfill.js" ], "coverageDirectory": "/target/jest-coverage", "coverageReporters": [ @@ -42,12 +42,16 @@ "/ui_framework/generator-kui/" ], "transform": { - "^.+\\.js$": "/src/jest/babelTransform.js" + "^.+\\.js$": "/src/dev/jest/babel_transform.js" }, "transformIgnorePatterns": [ "[/\\\\]node_modules[/\\\\].+\\.js$" ], "snapshotSerializers": [ "/node_modules/enzyme-to-json/serializer" + ], + "reporters": [ + "default", + "/src/dev/jest/junit_reporter.js" ] } diff --git a/src/dev/jest/junit_reporter.js b/src/dev/jest/junit_reporter.js new file mode 100644 index 000000000000..efd809cd3f5b --- /dev/null +++ b/src/dev/jest/junit_reporter.js @@ -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'); + } +} diff --git a/src/jest/mocks/file_mock.js b/src/dev/jest/mocks/file_mock.js similarity index 100% rename from src/jest/mocks/file_mock.js rename to src/dev/jest/mocks/file_mock.js diff --git a/src/jest/mocks/style_mock.js b/src/dev/jest/mocks/style_mock.js similarity index 100% rename from src/jest/mocks/style_mock.js rename to src/dev/jest/mocks/style_mock.js diff --git a/src/jest/setup/babel_polyfill.js b/src/dev/jest/setup/babel_polyfill.js similarity index 84% rename from src/jest/setup/babel_polyfill.js rename to src/dev/jest/setup/babel_polyfill.js index 81ce37f08320..39194b93ceea 100644 --- a/src/jest/setup/babel_polyfill.js +++ b/src/dev/jest/setup/babel_polyfill.js @@ -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'; diff --git a/src/jest/setup/request_animation_frame_polyfill.js b/src/dev/jest/setup/request_animation_frame_polyfill.js similarity index 100% rename from src/jest/setup/request_animation_frame_polyfill.js rename to src/dev/jest/setup/request_animation_frame_polyfill.js diff --git a/src/dev/mocha/auto_junit_reporter.js b/src/dev/mocha/auto_junit_reporter.js new file mode 100644 index 000000000000..1a8f7a7d9c1d --- /dev/null +++ b/src/dev/mocha/auto_junit_reporter.js @@ -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); + } + } + }; +} diff --git a/src/dev/mocha/index.js b/src/dev/mocha/index.js new file mode 100644 index 000000000000..fa9e33d3d4be --- /dev/null +++ b/src/dev/mocha/index.js @@ -0,0 +1,2 @@ +export { createAutoJunitReporter } from './auto_junit_reporter'; +export { setupJunitReportGeneration } from './junit_report_generation'; diff --git a/src/dev/mocha/junit_report_generation.js b/src/dev/mocha/junit_report_generation.js new file mode 100644 index 000000000000..3ab0ba1c76c3 --- /dev/null +++ b/src/dev/mocha/junit_report_generation.js @@ -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'); + }); +} diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 363716b85ccc..e7f8e34ae5c7 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -100,7 +100,6 @@ export const TEMPORARILY_IGNORED_PATHS = [ 'src/core_plugins/timelion/server/series_functions/__tests__/fixtures/tlConfig.js', 'src/fixtures/config_upgrade_from_4.0.0_to_4.0.1-snapshot.json', 'src/fixtures/vislib/mock_data/terms/_seriesMultiple.js', - 'src/jest/babelTransform.js', 'src/ui/i18n/__tests__/fixtures/translations/test_plugin_1/es-ES.json', 'src/ui/public/angular-bootstrap/accordion/accordion-group.html', 'src/ui/public/angular-bootstrap/bindHtml/bindHtml.js', diff --git a/src/dev/tooling_log/tooling_log.js b/src/dev/tooling_log/tooling_log.js index 536b54ee6a3a..74838f5d7327 100644 --- a/src/dev/tooling_log/tooling_log.js +++ b/src/dev/tooling_log/tooling_log.js @@ -1,7 +1,7 @@ import { format } from 'util'; import { PassThrough } from 'stream'; -import { magenta, yellow, red, blue, green, brightBlack } from 'ansicolors'; +import { magenta, yellow, red, blue, green, dim } from 'chalk'; import { parseLogLevel } from './log_levels'; @@ -25,7 +25,7 @@ export function createToolingLog(initialLogLevelName = 'silent') { debug(...args) { if (!logLevel.flags.debug) return; - this.write(' %s ', brightBlack('debg'), format(...args)); + this.write(' %s ', dim('debg'), format(...args)); } info(...args) { diff --git a/src/functional_test_runner/lib/config/schema.js b/src/functional_test_runner/lib/config/schema.js index 796fbee4522b..34c9f1c8ebe9 100644 --- a/src/functional_test_runner/lib/config/schema.js +++ b/src/functional_test_runner/lib/config/schema.js @@ -2,8 +2,6 @@ import { resolve, dirname } from 'path'; import Joi from 'joi'; -import { ConsoleReporterProvider } from '../reporters'; - // valid pattern for ID // enforced camel-case identifiers for consistency const ID_PATTERN = /^[a-zA-Z0-9_]+$/; @@ -62,7 +60,12 @@ export const schema = Joi.object().keys({ slow: Joi.number().default(30000), timeout: Joi.number().default(INSPECTING ? Infinity : 120000), ui: Joi.string().default('bdd'), - reporterProvider: Joi.func().default(ConsoleReporterProvider), + }).default(), + + junit: Joi.object().keys({ + enabled: Joi.boolean().default(!!process.env.CI), + reportName: Joi.string(), + rootDirectory: Joi.string(), }).default(), users: Joi.object().pattern( diff --git a/src/functional_test_runner/lib/reporters/console_reporter/colors.js b/src/functional_test_runner/lib/mocha/reporter/colors.js similarity index 57% rename from src/functional_test_runner/lib/reporters/console_reporter/colors.js rename to src/functional_test_runner/lib/mocha/reporter/colors.js index 624dd9888787..2d880851a6da 100644 --- a/src/functional_test_runner/lib/reporters/console_reporter/colors.js +++ b/src/functional_test_runner/lib/mocha/reporter/colors.js @@ -1,7 +1,7 @@ -import { brightBlack, green, yellow, red, brightWhite, brightCyan } from 'ansicolors'; +import { bold, dim, green, yellow, red, cyan } from 'chalk'; -export const suite = brightWhite; -export const pending = brightCyan; +export const suite = bold; +export const pending = cyan; export const pass = green; export const fail = red; @@ -14,6 +14,6 @@ export function speed(name, txt) { case 'slow': return red(txt); default: - return brightBlack(txt); + return dim(txt); } } diff --git a/src/functional_test_runner/lib/mocha/reporter/index.js b/src/functional_test_runner/lib/mocha/reporter/index.js new file mode 100644 index 000000000000..6270c06361b3 --- /dev/null +++ b/src/functional_test_runner/lib/mocha/reporter/index.js @@ -0,0 +1 @@ +export { MochaReporterProvider } from './reporter'; diff --git a/src/functional_test_runner/lib/reporters/console_reporter/ms.js b/src/functional_test_runner/lib/mocha/reporter/ms.js similarity index 100% rename from src/functional_test_runner/lib/reporters/console_reporter/ms.js rename to src/functional_test_runner/lib/mocha/reporter/ms.js diff --git a/src/functional_test_runner/lib/reporters/console_reporter/console_reporter.js b/src/functional_test_runner/lib/mocha/reporter/reporter.js similarity index 86% rename from src/functional_test_runner/lib/reporters/console_reporter/console_reporter.js rename to src/functional_test_runner/lib/mocha/reporter/reporter.js index 408e6164bc48..0619f9e1c8bb 100644 --- a/src/functional_test_runner/lib/reporters/console_reporter/console_reporter.js +++ b/src/functional_test_runner/lib/mocha/reporter/reporter.js @@ -2,17 +2,19 @@ import { format } from 'util'; import Mocha from 'mocha'; +import { setupJunitReportGeneration } from '../../../../dev'; import * as colors from './colors'; import * as symbols from './symbols'; import { ms } from './ms'; import { writeEpilogue } from './write_epilogue'; -export function ConsoleReporterProvider({ getService }) { +export function MochaReporterProvider({ getService }) { const log = getService('log'); + const config = getService('config'); return class MochaReporter extends Mocha.reporters.Base { - constructor(runner) { - super(runner); + constructor(runner, options) { + super(runner, options); runner.on('start', this.onStart); runner.on('hook', this.onHookStart); runner.on('hook end', this.onHookEnd); @@ -24,6 +26,13 @@ export function ConsoleReporterProvider({ getService }) { runner.on('test end', this.onTestEnd); runner.on('suite end', this.onSuiteEnd); runner.on('end', this.onEnd); + + if (config.get('junit.enabled') && config.get('junit.reportName')) { + setupJunitReportGeneration(runner, { + reportName: config.get('junit.reportName'), + rootDirectory: config.get('junit.rootDirectory') + }); + } } onStart = () => { diff --git a/src/functional_test_runner/lib/reporters/console_reporter/symbols.js b/src/functional_test_runner/lib/mocha/reporter/symbols.js similarity index 100% rename from src/functional_test_runner/lib/reporters/console_reporter/symbols.js rename to src/functional_test_runner/lib/mocha/reporter/symbols.js diff --git a/src/functional_test_runner/lib/reporters/console_reporter/write_epilogue.js b/src/functional_test_runner/lib/mocha/reporter/write_epilogue.js similarity index 100% rename from src/functional_test_runner/lib/reporters/console_reporter/write_epilogue.js rename to src/functional_test_runner/lib/mocha/reporter/write_epilogue.js diff --git a/src/functional_test_runner/lib/mocha/setup_mocha.js b/src/functional_test_runner/lib/mocha/setup_mocha.js index a0d02926e013..bc59f6810c7b 100644 --- a/src/functional_test_runner/lib/mocha/setup_mocha.js +++ b/src/functional_test_runner/lib/mocha/setup_mocha.js @@ -1,6 +1,7 @@ import Mocha from 'mocha'; import { loadTestFiles } from './load_test_files'; +import { MochaReporterProvider } from './reporter'; /** * Instansiate mocha and load testfiles into it @@ -16,8 +17,8 @@ export async function setupMocha(lifecycle, log, config, providers) { const mocha = new Mocha({ ...config.get('mochaOpts'), reporter: await providers.loadExternalService( - 'configured mocha reporter', - config.get('mochaOpts.reporterProvider') + 'mocha reporter', + MochaReporterProvider ) }); diff --git a/src/functional_test_runner/lib/reporters/console_reporter/index.js b/src/functional_test_runner/lib/reporters/console_reporter/index.js deleted file mode 100644 index 0129f2eea728..000000000000 --- a/src/functional_test_runner/lib/reporters/console_reporter/index.js +++ /dev/null @@ -1 +0,0 @@ -export { ConsoleReporterProvider } from './console_reporter'; diff --git a/src/functional_test_runner/lib/reporters/index.js b/src/functional_test_runner/lib/reporters/index.js deleted file mode 100644 index 0129f2eea728..000000000000 --- a/src/functional_test_runner/lib/reporters/index.js +++ /dev/null @@ -1 +0,0 @@ -export { ConsoleReporterProvider } from './console_reporter'; diff --git a/tasks/config/karma.js b/tasks/config/karma.js index a949a39ebaf1..b362b5471c6e 100644 --- a/tasks/config/karma.js +++ b/tasks/config/karma.js @@ -1,6 +1,8 @@ import { times } from 'lodash'; +import { resolve, dirname } from 'path'; const TOTAL_CI_SHARDS = 4; +const ROOT = dirname(require.resolve('../../package.json')); module.exports = function (grunt) { const config = { @@ -18,7 +20,20 @@ module.exports = function (grunt) { browsers: ['<%= karmaBrowser %>'], // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: process.env.CI ? ['dots'] : ['progress'], + reporters: process.env.CI ? ['dots', 'junit'] : ['progress'], + + junitReporter: { + outputFile: resolve(ROOT, 'target/junit/karma.xml'), + useBrowserName: false, + nameFormatter: (browser, result) => [ + ...result.suite, + result.description + ].join(' '), + classNameFormatter: (browser, result) => { + const rootSuite = result.suite[0] || result.description; + return `Browser Unit Tests.${rootSuite.replace(/\./g, '·')}`; + } + }, // list of files / patterns to load in the browser files: [ diff --git a/tasks/config/simplemocha.js b/tasks/config/simplemocha.js index 3b0f6c1c17ca..2f2f960e9389 100644 --- a/tasks/config/simplemocha.js +++ b/tasks/config/simplemocha.js @@ -1,14 +1,17 @@ -module.exports = { +import { createAutoJunitReporter } from '../../src/dev'; + +export default { options: { timeout: 10000, slow: 5000, ignoreLeaks: false, - reporter: 'spec', - globals: ['nil'] + reporter: createAutoJunitReporter({ + reportName: 'Server Mocha Tests' + }), + globals: ['nil'], }, all: { src: [ - 'test/mocha_setup.js', 'test/**/__tests__/**/*.js', 'src/**/__tests__/**/*.js', 'tasks/**/__tests__/**/*.js', diff --git a/test/api_integration/config.js b/test/api_integration/config.js index 5e80b19dc463..52af2f8d27a5 100644 --- a/test/api_integration/config.js +++ b/test/api_integration/config.js @@ -18,5 +18,8 @@ export default async function ({ readConfigFile }) { chance: ChanceProvider, }, servers: commonConfig.get('servers'), + junit: { + reportName: 'API Integration Tests' + } }; } diff --git a/test/functional/config.js b/test/functional/config.js index 39de985719e5..32159400cc0e 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -104,5 +104,8 @@ export default async function ({ readConfigFile }) { hash: '/dev_tools/console', }, }, + junit: { + reportName: 'UI Functional Tests' + } }; } diff --git a/test/mocha.opts b/test/mocha.opts index 72451b6e7f81..a4d588165948 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1 +1 @@ ---require test/mocha_setup.js +--require src/babel-register diff --git a/test/mocha_setup.js b/test/mocha_setup.js deleted file mode 100644 index 6b2389e9bcec..000000000000 --- a/test/mocha_setup.js +++ /dev/null @@ -1 +0,0 @@ -require('../src/babel-register');