/* eslint-env mocha */ 'use strict'; var Reporter = require('../index'); var mochaVersion = process.env.MOCHA_VERSION || ''; var Mocha = require('mocha' + mochaVersion); var Runner = Mocha.Runner; var Suite = Mocha.Suite; var Test = Mocha.Test; var fs = require('fs'); var path = require('path'); var rimraf = require('rimraf'); var chai = require('chai'); var expect = chai.expect; var FakeTimer = require('@sinonjs/fake-timers'); var xmllint = require('xmllint'); var chaiXML = require('chai-xml'); var mockXml = require('./mock-results'); var mockJunitSuites = require('./mock-junit-suites'); var testConsole = require('test-console'); var debug = require('debug')('mocha-junit-reporter:tests'); chai.use(chaiXML); describe('mocha-junit-reporter', function() { var filePath; var MOCHA_FILE; var stdout; function mockStdout () { stdout = testConsole.stdout.inspect(); return stdout; } function createTest(name, options, fn) { if (typeof options === 'function') { fn = options; options = null; } options = options || {}; // null fn means no callback which mocha treats as pending test. // undefined fn means caller wants a default fn. if (fn === undefined) { fn = function () {}; } var test = new Test(name, fn); var duration = options.duration; if (duration != null) { // mock duration so we have consistent output Object.defineProperty(test, 'duration', { set: function() { // do nothing }, get: function() { return duration; } }); } return test; } function runTests(reporter, options, callback) { if (!callback) { callback = options; options = null; } options = options || {}; options.invalidChar = options.invalidChar || ''; options.title = options.title || 'Foo Bar'; var runner = reporter.runner; var rootSuite = runner.suite; var suite1 = Suite.create(rootSuite, options.title); suite1.addTest(createTest('can weez the juice', { duration: 101 })); suite1.addTest(createTest('can narfle the garthog', {duration: 2002}, function(done) { var err = new Error(options.invalidChar + 'expected garthog to be dead' + options.invalidChar); err.stack = 'this is where the stack would be'; done(err); })); suite1.addTest(createTest('can behave like a flandip', {duration: 30003}, function(done) { var err = new Error('expected baz to be masher, a hustler, an uninvited grasper of cone'); err.name = 'BazError'; err.stack = 'stack'; done(err); })); var suite2 = Suite.create(rootSuite, 'Another suite!'); suite2.addTest(createTest('works', {duration: 400004})); if (options.includePending) { var pendingSuite = Suite.create(rootSuite, 'Pending suite!'); pendingSuite.addTest(createTest('pending', null, null)); } var _onSuiteEnd = reporter._onSuiteEnd.bind(reporter); reporter._onSuiteEnd = function(suite) { if (suite === rootSuite) { // root suite took no time to execute reporter._Date.clock.tick(0); } else if (suite === suite1) { // suite1 took an arbitrary amount of time that includes time to run each test + setup and teardown reporter._Date.clock.tick(100001); } else if (suite === suite2) { reporter._Date.clock.tick(400005); } return _onSuiteEnd(suite); }; runRunner(runner, callback); } function assertXmlEquals(actual, expected) { expect(actual).xml.to.be.valid(); expect(actual).xml.to.equal(expected); } function verifyMochaFile(runner, path, options) { var now = (new Date()).toISOString(); debug('verify', now); var output = fs.readFileSync(path, 'utf-8'); assertXmlEquals(output, mockXml(runner.stats, options)); debug('done', now); } function removeTestPath(callback) { rimraf(__dirname + '/output', function(err) { if (err) { return callback(err); } // tests that exercise defaults will write to $CWD/test-results.xml rimraf(__dirname + '/../test-results.xml', callback); }); } function createRunner() { // mocha always has a root suite var rootSuite = new Suite('', 'root', true); // We don't want Mocha to emit timeout errors. // If we want to simulate errors, we'll emit them ourselves. rootSuite.timeout(0); return new Runner(rootSuite); } function createReporter(options) { options = options || {}; filePath = path.join(path.dirname(__dirname), options.mochaFile || ''); var mocha = new Mocha({ reporter: Reporter, allowUncaught: true }); return new mocha._reporter(createRunner(), { reporterOptions: options, Date: FakeTimer.createClock(0).Date }); } function runRunner(runner, callback) { runner.run(function(failureCount) { if (runner.dispose) { // Ensure uncaught exception handlers are cleared before we execute test assertions. // Otherwise, this runner would intercept uncaught exceptions that were already handled by the mocha instance // running our tests. runner.dispose(); } callback(failureCount); }); } function getFileNameWithHash(path) { var filenames = fs.readdirSync(path); var expected = /(^results\.)([a-f0-9]{32})(\.xml)$/i; for (var i = 0; i < filenames.length; i++) { if (expected.test(filenames[i])) { return filenames[i]; } } } before(function(done) { // cache this MOCHA_FILE = process.env.MOCHA_FILE; removeTestPath(done); }); after(function() { // reset this process.env.MOCHA_FILE = MOCHA_FILE; }); beforeEach(function() { filePath = undefined; delete process.env.MOCHA_FILE; delete process.env.PROPERTIES; }); afterEach(function(done) { debug('after'); if (stdout) { stdout.restore(); } removeTestPath(done); }); it('can produce a JUnit XML report', function(done) { var reporter = createReporter({mochaFile: 'test/output/mocha.xml'}); runTests(reporter, function() { verifyMochaFile(reporter.runner, filePath); done(); }); }); it('can handle getXml being called twice', function() { var reporter = createReporter({mochaFile: 'test/output/mocha.xml'}); var testsuites = mockJunitSuites.withStringTimes(); reporter.getXml(testsuites); }); it('respects `process.env.MOCHA_FILE`', function(done) { process.env.MOCHA_FILE = 'test/output/results.xml'; var reporter = createReporter(); runTests(reporter, function() { verifyMochaFile(reporter.runner, process.env.MOCHA_FILE); done(); }); }); it('respects `process.env.PROPERTIES`', function(done) { process.env.PROPERTIES = 'CUSTOM_PROPERTY:ABC~123'; var reporter = createReporter({mochaFile: 'test/output/properties.xml'}); runTests(reporter, function() { verifyMochaFile(reporter.runner, filePath, { properties: [ { name: 'CUSTOM_PROPERTY', value: 'ABC~123' } ] }); done(); }); }); it('respects `--reporter-options mochaFile=`', function(done) { var reporter = createReporter({mochaFile: 'test/output/results.xml'}); runTests(reporter, function() { verifyMochaFile(reporter.runner, filePath); done(); }); }); it('respects `[hash]` pattern in test results report filename', function(done) { var dir = 'test/output/'; var path = dir + 'results.[hash].xml'; var reporter = createReporter({mochaFile: path}); runTests(reporter, function() { verifyMochaFile(reporter.runner, dir + getFileNameWithHash(dir)); done(); }); }); it("respects `[testsuitesTitle]` pattern in test results report filename", function (done) { var dir = "test/output/"; var path = dir + "results.[testsuitesTitle].xml"; var reporter = createReporter({ mochaFile: path }); runTests(reporter, function () { verifyMochaFile( reporter.runner, dir + "results." + reporter._options.testsuitesTitle + ".xml" ); done(); }); }); it("respects `[rootSuiteTitle]` pattern in test results report filename", function (done) { var dir = "test/output/"; var path = dir + "results.[rootSuiteTitle].xml"; var reporter = createReporter({ mochaFile: path }); runTests(reporter, function () { verifyMochaFile( reporter.runner, dir + "results." + reporter._testsuites[0].testsuite[0]._attr.name + ".xml" ); done(); }); }); it("respects `[suiteFilename]` pattern in test results report filename", function (done) { var dir = "test/output/"; var path = dir + "results.[suiteFilename].xml"; var reporter = createReporter({ mochaFile: path }); runTests(reporter, function () { verifyMochaFile( reporter.runner, dir + "results." + (reporter._testsuites[0]?.testsuite[0]?._attr?.file ?? 'suiteFilename') + ".xml" ); done(); }); }); it("respects `[suiteName]` pattern in test results report filename", function (done) { var dir = "test/output/"; var path = dir + "results.[suiteName].xml"; var reporter = createReporter({ mochaFile: path }); runTests(reporter, function () { verifyMochaFile( reporter.runner, dir + "results." + (reporter._testsuites[1]?.testsuite[0]?._attr?.name ?? 'suiteName') + ".xml" ); done(); }); }); it('will create intermediate directories', function(done) { var reporter = createReporter({mochaFile: 'test/output/foo/mocha.xml'}); runTests(reporter, function() { verifyMochaFile(reporter.runner, filePath); done(); }); }); it('creates valid XML report for invalid message', function(done) { var reporter = createReporter({mochaFile: 'test/output/mocha.xml'}); runTests(reporter, {invalidChar: '\u001b'}, function() { assertXmlEquals(reporter._xml, mockXml(reporter.runner.stats)); done(); }); }); it('creates valid XML report even if title contains ANSI character sequences', function(done) { var reporter = createReporter({mochaFile: 'test/output/mocha.xml'}); runTests(reporter, {title: 'Foo Bar'}, function() { verifyMochaFile(reporter.runner, filePath); done(); }); }); it('outputs pending tests if "includePending" is specified', function(done) { var reporter = createReporter({mochaFile: 'test/output/mocha.xml', includePending: true}); runTests(reporter, {includePending: true}, function() { verifyMochaFile(reporter.runner, filePath); done(); }); }); it('can output to the console', function(done) { var reporter = createReporter({mochaFile: 'test/output/console.xml', toConsole: true}); var stdout = mockStdout(); runTests(reporter, function() { verifyMochaFile(reporter.runner, filePath); var xml = stdout.output[0]; assertXmlEquals(xml, mockXml(reporter.runner.stats)); done(); }); }); it('properly outputs tests when error in beforeAll', function(done) { var reporter = createReporter(); var rootSuite = reporter.runner.suite; var suite1 = Suite.create(rootSuite, 'failing beforeAll'); suite1.beforeAll('failing hook', function() { throw new Error('error in before'); }); suite1.addTest(createTest('test 1')); var suite2 = Suite.create(rootSuite, 'good suite'); suite2.addTest(createTest('test 2')); runRunner(reporter.runner, function() { if (reporter.runner.dispose) { reporter.runner.dispose(); } expect(reporter._testsuites).to.have.lengthOf(3); expect(reporter._testsuites[1].testsuite[0]._attr.name).to.equal('failing beforeAll'); expect(reporter._testsuites[1].testsuite[1].testcase).to.have.lengthOf(2); var failureMessage = 'failing beforeAll "before all" hook: failing hook'; if (!['2', '3', '4', '5'].includes(mochaVersion)) { // newer versions of Mocha include the name of the test in the message failureMessage += ' for "test 1"'; } expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal(failureMessage); expect(reporter._testsuites[1].testsuite[1].testcase[1].failure._attr.message).to.equal('error in before'); expect(reporter._testsuites[2].testsuite[0]._attr.name).to.equal('good suite'); expect(reporter._testsuites[2].testsuite[1].testcase).to.have.lengthOf(1); expect(reporter._testsuites[2].testsuite[1].testcase[0]._attr.name).to.equal('good suite test 2'); done(); }); }); it('properly diffs errors from Chai', function(done) { var reporter = createReporter(); var rootSuite = reporter.runner.suite; var suite1 = Suite.create(rootSuite, 'failing with Chai'); suite1.addTest(createTest('test 1', function () { expect({}).to.deep.equal({missingProperty: true}); })); runRunner(reporter.runner, function() { if (reporter.runner.dispose) { reporter.runner.dispose(); } expect(reporter._testsuites).to.have.lengthOf(2); expect(reporter._testsuites[1].testsuite[0]._attr.name).to.equal('failing with Chai'); expect(reporter._testsuites[1].testsuite[1].testcase).to.have.lengthOf(2); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal('failing with Chai test 1'); expect(reporter._testsuites[1].testsuite[1].testcase[1].failure._attr.message).to.equal('expected {} to deeply equal { missingProperty: true }'); expect(reporter._testsuites[1].testsuite[1].testcase[1].failure._cdata).to.match(/AssertionError: expected {} to deeply equal {\s*missingProperty:\s*true\s*}\n(?:\s* at .*?\n)*\n\s*\+ expected - actual\n+\s*-{}\n\s*\+{\n\s*\+\s*"missingProperty":\s*true\n\s*\+}[\s\S]*/); done(); }); }); describe('when "useFullSuiteTitle" option is specified', function() { it('generates full suite title', function(done) { var reporter = createReporter({useFullSuiteTitle: true }); runTests(reporter, function() { expect(suiteName(reporter._testsuites[0])).to.equal(''); expect(suiteName(reporter._testsuites[1])).to.equal('Root Suite Foo Bar'); expect(suiteName(reporter._testsuites[2])).to.equal('Root Suite Another suite!'); done(); }); }); it('generates full suite title separated by "suiteTitleSeparatedBy" option', function(done) { var reporter = createReporter({useFullSuiteTitle: true, suiteTitleSeparatedBy: '.'}); runTests(reporter, function() { expect(suiteName(reporter._testsuites[0])).to.equal(''); expect(suiteName(reporter._testsuites[1])).to.equal('Root Suite.Foo Bar'); expect(suiteName(reporter._testsuites[2])).to.equal('Root Suite.Another suite!'); done(); }); }); function suiteName(suite) { return suite.testsuite[0]._attr.name; } }); describe('when "outputs" option is specified', function() { it('adds output/error lines to xml report', function(done) { var reporter = createReporter({outputs: true}); var test = createTest('has outputs'); test.consoleOutputs = [ 'hello', 'world' ]; test.consoleErrors = [ 'typical diagnostic info', 'all is OK' ]; var suite = Suite.create(reporter.runner.suite, 'with console output and error'); suite.addTest(test); runRunner(reporter.runner, function() { expect(reporter._testsuites[1].testsuite[0]._attr.name).to.equal(suite.title); expect(reporter._testsuites[1].testsuite[1].testcase).to.have.length(3); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal(test.fullTitle()); expect(reporter._testsuites[1].testsuite[1].testcase[1]).to.have.property('system-out', 'hello\nworld'); expect(reporter._testsuites[1].testsuite[1].testcase[2]).to.have.property('system-err', 'typical diagnostic info\nall is OK'); expect(reporter._xml).to.include('hello\nworld'); expect(reporter._xml).to.include('typical diagnostic info\nall is OK'); done(); }); }); it('does not add system-out if no outputs/errors were passed', function(done) { var reporter = createReporter({outputs: true}); var test = createTest('has outputs'); var suite = Suite.create(reporter.runner.suite, 'with console output and error'); suite.addTest(test); runRunner(reporter.runner, function() { expect(reporter._testsuites[1].testsuite[0]._attr.name).to.equal(suite.title); expect(reporter._testsuites[1].testsuite[1].testcase).to.have.length(1); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal(test.fullTitle()); expect(reporter._xml).not.to.include(''); expect(reporter._xml).not.to.include(''); done(); }); }); it('does not add system-out if outputs/errors were empty', function(done) { var reporter = createReporter({outputs: true}); var test = createTest('has outputs'); test.consoleOutputs = []; test.consoleErrors = []; var suite = Suite.create(reporter.runner.suite, 'with console output and error'); suite.addTest(test); runRunner(reporter.runner, function() { expect(reporter._testsuites[1].testsuite[0]._attr.name).to.equal(suite.title); expect(reporter._testsuites[1].testsuite[1].testcase).to.have.length(1); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal(test.fullTitle()); expect(reporter._xml).not.to.include(''); expect(reporter._xml).not.to.include(''); done(); }); }); }); describe('when "attachments" option is specified', function() { it('adds attachments to xml report', function(done) { var filePath = '/path/to/file'; var reporter = createReporter({attachments: true}); var test = createTest('has attachment'); test.attachments = [filePath]; var suite = Suite.create(reporter.runner.suite, 'with attachments'); suite.addTest(test); runRunner(reporter.runner, function() { expect(reporter._testsuites[1].testsuite[0]._attr.name).to.equal(suite.title); expect(reporter._testsuites[1].testsuite[1].testcase).to.have.length(2); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal(test.fullTitle()); expect(reporter._testsuites[1].testsuite[1].testcase[1]).to.have.property('system-out', '[[ATTACHMENT|' + filePath + ']]'); expect(reporter._xml).to.include('[[ATTACHMENT|' + filePath + ']]'); done(); }); }); it('does not add system-out if no attachments were passed', function(done) { var reporter = createReporter({attachments: true}); var test = createTest('has attachment'); var suite = Suite.create(reporter.runner.suite, 'with attachments'); suite.addTest(test); runRunner(reporter.runner, function() { expect(reporter._testsuites[1].testsuite[0]._attr.name).to.equal(suite.title); expect(reporter._testsuites[1].testsuite[1].testcase).to.have.lengthOf(1); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal(test.fullTitle()); expect(reporter._xml).to.not.include(''); done(); }); }); it('does not add system-out if attachments array is empty', function(done) { var reporter = createReporter({attachments: true}); var test = createTest('has attachment'); test.attachments = []; var suite = Suite.create(reporter.runner.suite, 'with attachments'); suite.addTest(test); runRunner(reporter.runner, function() { expect(reporter._testsuites[1].testsuite[0]._attr.name).to.equal(suite.title); expect(reporter._testsuites[1].testsuite[1].testcase).to.have.lengthOf(1); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal(test.fullTitle()); expect(reporter._xml).to.not.include(''); done(); }); }); it('includes both console outputs and attachments in XML', function(done) { var reporter = createReporter({attachments: true, outputs: true}); var test = createTest('has attachment'); var filePath = '/path/to/file'; test.attachments = [filePath]; test.consoleOutputs = [ 'first console line', 'second console line' ]; var suite = Suite.create(reporter.runner.suite, 'with attachments and outputs'); suite.addTest(test); runRunner(reporter.runner, function() { expect(reporter._testsuites[1].testsuite[0]._attr.name).to.equal(suite.title); expect(reporter._testsuites[1].testsuite[1].testcase).to.have.length(2); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal(test.fullTitle()); expect(reporter._testsuites[1].testsuite[1].testcase[1]).to.have.property('system-out', 'first console line\nsecond console line\n[[ATTACHMENT|' + filePath + ']]'); expect(reporter._xml).to.include('first console line\nsecond console line\n[[ATTACHMENT|' + filePath + ']]'); done(); }); }); }); describe('Output', function() { it('skips suites with empty title', function(done) { var reporter = createReporter(); var suite = Suite.create(reporter.runner.suite, ''); suite.root = false; // mocha treats suites with empty title as root, so not sure this is possible suite.addTest(createTest('test')); runRunner(reporter.runner, function() { expect(reporter._testsuites).to.have.lengthOf(1); expect(reporter._testsuites[0].testsuite[0]._attr.name).to.equal('Root Suite'); done(); }); }); it('skips suites without testcases and suites', function(done) { var reporter = createReporter(); Suite.create(reporter.runner.suite, 'empty suite'); // mocha won't emit the `suite` event if a suite has no tests in it, so we won't even output the root suite. // See https://github.com/mochajs/mocha/blob/c0137eb698add08f29035467ea1dc230904f82ba/lib/runner.js#L723. runRunner(reporter.runner, function() { expect(reporter._testsuites).to.have.lengthOf(0); done(); }); }); it('skips suites without testcases even if they have nested suites', function(done) { var reporter = createReporter(); var suite1 = Suite.create(reporter.runner.suite, 'suite'); Suite.create(suite1, 'nested suite'); runRunner(reporter.runner, function() { // even though we have nested suites, there are no tests so mocha won't emit the `suite` event expect(reporter._testsuites).to.have.lengthOf(0); done(); }); }); it('does not skip suites with nested tests', function(done) { var reporter = createReporter(); var suite = Suite.create(reporter.runner.suite, 'nested suite'); suite.addTest(createTest('test')); runRunner(reporter.runner, function() { expect(reporter._testsuites).to.have.lengthOf(2); expect(reporter._testsuites[0].testsuite[0]._attr.name).to.equal('Root Suite'); expect(reporter._testsuites[1].testsuite[1].testcase).to.have.lengthOf(1); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal('nested suite test'); done(); }); }); it('does not skip root suite', function(done) { var reporter = createReporter(); reporter.runner.suite.addTest(createTest('test')); runRunner(reporter.runner, function() { expect(reporter._testsuites).to.have.lengthOf(1); expect(reporter._testsuites[0].testsuite[0]._attr.name).to.equal('Root Suite'); expect(reporter._testsuites[0].testsuite[1].testcase).to.have.lengthOf(1); var expectedName = 'test'; if (['2', '3'].includes(mochaVersion)) { expectedName = ' ' + expectedName; } expect(reporter._testsuites[0].testsuite[1].testcase[0]._attr.name).to.equal(expectedName); done(); }); }); it('respects the `rootSuiteTitle`', function(done) { var name = 'The Root Suite!'; var reporter = createReporter({rootSuiteTitle: name}); reporter.runner.suite.addTest(createTest('test')); runRunner(reporter.runner, function() { expect(reporter._testsuites).to.have.lengthOf(1); expect(reporter._testsuites[0].testsuite[0]._attr.name).to.equal(name); done(); }); }); it('uses "Mocha Tests" by default', function(done) { var reporter = createReporter(); reporter.runner.suite.addTest(createTest('test')); runRunner(reporter.runner, function() { expect(reporter._xml).to.include('testsuites name="Mocha Tests"'); done(); }); }); it('respects the `testsuitesTitle`', function(done) { var title = 'SuitesTitle'; var reporter = createReporter({testsuitesTitle: title}); reporter.runner.suite.addTest(createTest('test')); runRunner(reporter.runner, function() { expect(reporter._xml).to.include('testsuites name="SuitesTitle"'); done(); }); }); }); describe('Feature "Configurable classname/name switch"', function() { var mockedTestCase = { title: "should behave like so", timestamp: 123, tests: "1", failures: "0", time: "0.004", fullTitle: function() { return 'Super Suite ' + this.title; } }; it('should generate valid testCase for testCaseSwitchClassnameAndName default', function() { var reporter = createReporter(); var testCase = reporter.getTestcaseData(mockedTestCase); expect(testCase.testcase[0]._attr.name).to.equal(mockedTestCase.fullTitle()); expect(testCase.testcase[0]._attr.classname).to.equal(mockedTestCase.title); }); it('should generate valid testCase for testCaseSwitchClassnameAndName=false', function() { var reporter = createReporter({testCaseSwitchClassnameAndName: false}); var testCase = reporter.getTestcaseData(mockedTestCase); expect(testCase.testcase[0]._attr.name).to.equal(mockedTestCase.fullTitle()); expect(testCase.testcase[0]._attr.classname).to.equal(mockedTestCase.title); }); it('should generate valid testCase for testCaseSwitchClassnameAndName=true', function() { var reporter = createReporter({testCaseSwitchClassnameAndName: true}); var testCase = reporter.getTestcaseData(mockedTestCase); expect(testCase.testcase[0]._attr.name).to.equal(mockedTestCase.title); expect(testCase.testcase[0]._attr.classname).to.equal(mockedTestCase.fullTitle()); }); }); describe('XML format', function () { it('generates Jenkins compatible XML when in jenkinsMode', function(done) { this.timeout(10000); // xmllint is very slow var reporter = createReporter({jenkinsMode: true}); var rootSuite = reporter.runner.suite; var suite1 = Suite.create(rootSuite, 'Inner Suite'); suite1.addTest(createTest('test')); var suite2 = Suite.create(rootSuite, 'Another Suite'); suite2.addTest(createTest('test', function(done) { done(new Error('failed test')); })); runRunner(reporter.runner, function() { var schema = fs.readFileSync(path.join(__dirname, 'resources', 'jenkins-junit.xsd')); var result = xmllint.validateXML({ xml: reporter._xml, schema: schema }); expect(result.errors).to.equal(null, JSON.stringify(result.errors)); done(); }); }); it('generates Ant compatible XML when in antMode', function(done) { this.timeout(10000); // xmllint is very slow var reporter = createReporter({antMode: true}); var rootSuite = reporter.runner.suite; var suite1 = Suite.create(rootSuite, 'Inner Suite'); suite1.addTest(createTest('test')); var suite2 = Suite.create(rootSuite, 'Another Suite'); suite2.addTest(createTest('test', function(done) { done(new Error('failed test')); })); runRunner(reporter.runner, function() { var schema = fs.readFileSync(path.join(__dirname, 'resources', 'JUnit.xsd')); var result = xmllint.validateXML({ xml: reporter._xml, schema: schema }); expect(result.errors).to.equal(null, JSON.stringify(result.errors)); done(); }); }); describe('Jenkins format', function () { it('generates Jenkins compatible classnames and suite name', function(done) { var reporter = createReporter({jenkinsMode: true}); var rootSuite = reporter.runner.suite; var suite1 = Suite.create(rootSuite, 'Inner Suite'); suite1.addTest(createTest('test')); var suite2 = Suite.create(suite1, 'Another Suite'); suite2.addTest(createTest('fail test', function(done) { done(new Error('failed test')); })); runRunner(reporter.runner, function() { expect(reporter._testsuites[0].testsuite[0]._attr.name).to.equal(''); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal('test'); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.classname).to.equal('Inner Suite'); expect(reporter._testsuites[2].testsuite[0]._attr.name).to.equal('Root Suite.Inner Suite.Another Suite'); expect(reporter._testsuites[2].testsuite[1].testcase[0]._attr.name).to.equal('fail test'); expect(reporter._testsuites[2].testsuite[1].testcase[0]._attr.classname).to.equal('Inner Suite.Another Suite'); done(); }); }); it('prefix is added to a classname when jenkinsClassnamePrefix is specified', function(done) { var reporter = createReporter({jenkinsMode: true, jenkinsClassnamePrefix: "Added Prefix"}); var rootSuite = reporter.runner.suite; var suite1 = Suite.create(rootSuite, 'Inner Suite'); suite1.addTest(createTest('test')); var suite2 = Suite.create(suite1, 'Another Suite'); suite2.addTest(createTest('fail test', function(done) { done(new Error('failed test')); })); runRunner(reporter.runner, function() { expect(reporter._testsuites[0].testsuite[0]._attr.name).to.equal(''); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.name).to.equal('test'); expect(reporter._testsuites[1].testsuite[1].testcase[0]._attr.classname).to.equal('Added Prefix.Inner Suite'); expect(reporter._testsuites[2].testsuite[0]._attr.name).to.equal('Root Suite.Inner Suite.Another Suite'); expect(reporter._testsuites[2].testsuite[1].testcase[0]._attr.name).to.equal('fail test'); expect(reporter._testsuites[2].testsuite[1].testcase[0]._attr.classname).to.equal('Added Prefix.Inner Suite.Another Suite'); done(); }); }); }); }); });