Skip to content

Commit

Permalink
Populate Test Suite Attributes
Browse files Browse the repository at this point in the history
The specification for the testsuites tag in Junit includes
multiple attributes, as per http://llg.cubic.org/docs/junit/
This commit populates disabled, errors, failures, tests and time.
It does so by recursively collecting those metrics from nested
testsuite tags in jasmineDone and then passing them to
wrapOutputAndWriteFile.
  • Loading branch information
FuadBalashov committed Jun 1, 2017
1 parent 1abc7e6 commit 0264d28
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 15 deletions.
51 changes: 46 additions & 5 deletions spec/JUnitXmlReporterSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,34 @@ describe("JUnitXmlReporter", function(){
});
});

function assertTestsuitesTagAttributes(testSuitesTag, {disabled, errors, failures, tests} = {}) {
expect(testSuitesTag.getAttribute('disabled')).toBe(disabled);
expect(testSuitesTag.getAttribute('errors')).toBe(errors);
expect(testSuitesTag.getAttribute('failures')).toBe(failures);
expect(testSuitesTag.getAttribute('tests')).toBe(tests);
};


it("the testsuites tags should include a time attribute", function() {
var testSuitesTags = writeCalls[0].xmldoc.getElementsByTagName('testsuites');
expect(testSuitesTags.length).toBe(1);
var testSuitesTag = testSuitesTags[0];
expect(testSuitesTag.getAttribute('time')).not.toBe('');
});

describe("no xml output generation", function() {
beforeEach(function() {
setupReporterWithOptions({consolidateAll:true});
triggerRunnerEvents();
});

it("testsuites tags should default disabled, errors, failures to 0 when undefined", function() {
assertTestsuitesTagAttributes(
writeCalls[0].xmldoc.getElementsByTagName('testsuites')[0],
{disabled: '0', errors: '0', failures: '0', tests: '1'});
});
});

describe("generated xml output", function(){
var subSuite, subSubSuite, siblingSuite;
function itShouldIncludeXmlPreambleInAllFiles() {
Expand All @@ -184,9 +212,11 @@ describe("JUnitXmlReporter", function(){
fakeSpec(subSuite, "should be one level down");
fakeSpec(subSubSuite, "should be two levels down");
var skipped = fakeSpec(subSubSuite, "should be skipped two levels down");
var disabled = fakeSpec(subSubSuite, "should be disabled two levels down");
var failed = fakeSpec(subSubSuite, "should be failed two levels down");
fakeSpec(siblingSuite, "should be a sibling of Parent");
skipped.result.status = "pending";
disabled.result.status = "disabled";
failed.result.status = "failed";
failed.result.failedExpectations.push({
passed: false,
Expand All @@ -212,6 +242,11 @@ describe("JUnitXmlReporter", function(){
it("should write a single file using filePrefix as the filename", function() {
expect(writeCalls[0].args[0]).toBe('results.xml');
});
it("testsuites tags should include disabled, errors, failures, and tests (count) when defined", function() {
assertTestsuitesTagAttributes(
writeCalls[0].xmldoc.getElementsByTagName('testsuites')[0],
{disabled: '1', errors: '0', failures: '1', tests: '7'});
});
itShouldHaveOneTestsuitesElementPerFile();
itShouldIncludeXmlPreambleInAllFiles();
});
Expand All @@ -231,6 +266,12 @@ describe("JUnitXmlReporter", function(){
expect(writeCalls[0].args[0]).toBe('results-ParentSuite.xml');
expect(writeCalls[1].args[0]).toBe('results-SiblingSuiteWithInvalidChars.xml');
});
it("testsuites tags should include disabled, errors, failures, and tests (count) when defined", function() {
assertTestsuitesTagAttributes(writeCalls[0].xmldoc.getElementsByTagName('testsuites')[0],
{disabled: '1', errors: '0', failures: '1', tests: '6'});
assertTestsuitesTagAttributes(writeCalls[1].xmldoc.getElementsByTagName('testsuites')[0],
{disabled: '0', errors: '0', failures: '0', tests: '1'});
});
itShouldHaveOneTestsuitesElementPerFile();
itShouldIncludeXmlPreambleInAllFiles();
});
Expand Down Expand Up @@ -300,7 +341,7 @@ describe("JUnitXmlReporter", function(){
});
it("should include total / failed / skipped counts for each suite (ignoring descendent results)", function() {
expect(suites[1].getAttribute('tests')).toBe('1');
expect(suites[2].getAttribute('tests')).toBe('3');
expect(suites[2].getAttribute('tests')).toBe('4');
expect(suites[2].getAttribute('skipped')).toBe('1');
expect(suites[2].getAttribute('failures')).toBe('1');
});
Expand Down Expand Up @@ -346,7 +387,7 @@ describe("JUnitXmlReporter", function(){
});
it("should include specs in order", function() {
expect(specs[0].getAttribute('name')).toContain('should be a dummy');
expect(specs[4].getAttribute('name')).toBe('should be failed two levels down');
expect(specs[5].getAttribute('name')).toBe('should be failed two levels down');
});
it("should escape bad xml characters in spec description", function() {
expect(writeCalls[0].output).toContain("& < > " '");
Expand All @@ -355,15 +396,15 @@ describe("JUnitXmlReporter", function(){
expect(Number(specs[0].getAttribute('time'))).not.toEqual(NaN);
});
it("should include failed matcher name as the failure type", function() {
var failure = specs[4].getElementsByTagName('failure')[0];
var failure = specs[5].getElementsByTagName('failure')[0];
expect(failure.getAttribute('type')).toBe('toBe');
});
it("should include failure messages", function() {
var failure = specs[4].getElementsByTagName('failure')[0];
var failure = specs[5].getElementsByTagName('failure')[0];
expect(failure.getAttribute('message')).toBe('Expected true to be false.');
});
it("should include stack traces for failed specs (using CDATA to preserve special characters)", function() {
var failure = specs[4].getElementsByTagName('failure')[0];
var failure = specs[5].getElementsByTagName('failure')[0];
expect(failure.textContent).toContain('cool & can have "special" characters <3');
});
it("should include <skipped/> for skipped specs", function() {
Expand Down
50 changes: 40 additions & 10 deletions src/junit_reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,18 @@
self.suiteDone(fakeFocusedSuite);
}
var output = '';
var testSuitesResults = { disabled: 0, errors: 0, failures: 0, tests: 0, time: 0 };
for (var i = 0; i < suites.length; i++) {
output += self.getOrWriteNestedOutput(suites[i]);
// retrieve nested suite data to include in the testsuites tag
var suiteResults = self.getNestedSuiteData(suites[i]);
for (var key in suiteResults) {
testSuitesResults[key] += suiteResults[key];
};
}
// if we have anything to write here, write out the consolidated file
if (output) {
wrapOutputAndWriteFile(self.filePrefix, output);
wrapOutputAndWriteFile(self.filePrefix, output, testSuitesResults);
}
//log("Specs skipped but not reported (entire suite skipped or targeted to specific specs)", totalSpecsDefined - totalSpecsExecuted + totalSpecsDisabled);

Expand All @@ -306,6 +312,27 @@
}
};

self.formatSuiteData = function(suite) {
return {
disabled: suite._disabled || 0,
errors: 0,
failures: suite._failures || 0,
tests: suite._specs.length || 0,
time: (suite._endTime.getTime() - suite._startTime.getTime()) || 0
};
};

self.getNestedSuiteData = function (suite) {
var suiteResults = self.formatSuiteData(suite);
for (var i = 0; i < suite._suites.length; i++) {
var childSuiteResults = self.getNestedSuiteData(suite._suites[i]);
for (var key in suiteResults) {
suiteResults[key] += childSuiteResults[key];
};
}
return suiteResults;
};

self.getOrWriteNestedOutput = function(suite) {
var output = suiteAsXml(suite);
for (var i = 0; i < suite._suites.length; i++) {
Expand All @@ -315,7 +342,7 @@
return output;
} else {
// if we aren't supposed to consolidate output, just write it now
wrapOutputAndWriteFile(generateFilename(suite), output);
wrapOutputAndWriteFile(generateFilename(suite), output, self.getNestedSuiteData(suite));
return '';
}
};
Expand Down Expand Up @@ -467,17 +494,20 @@
self.logEntries.splice(0, self.logEntries.length);
}
}

// To remove complexity and be more DRY about the silly preamble and <testsuites> element
var prefix = '<?xml version="1.0" encoding="UTF-8" ?>';
if (self.stylesheetPath) {
prefix += '\n<?xml-stylesheet type="text/xsl" href="' + self.stylesheetPath + '" ?>';
function getPrefix({disabled, errors, failures, tests, time} = {}) {
// To remove complexity and be more DRY about the silly preamble and <testsuites> element
var prefix = '<?xml version="1.0" encoding="UTF-8" ?>';
if (self.stylesheetPath) {
prefix += '\n<?xml-stylesheet type="text/xsl" href="' + self.stylesheetPath + '" ?>';
}
prefix += '\n<testsuites disabled="' + disabled + '" errors="' + errors + '" failures="' + failures +
'" tests="' + tests + '" time="' + time + '">';
return prefix;
}
prefix += '\n<testsuites>';
var suffix = '\n</testsuites>';
function wrapOutputAndWriteFile(filename, text) {
function wrapOutputAndWriteFile(filename, text, testSuitesResults) {
if (filename.substr(-4) !== '.xml') { filename += '.xml'; }
self.writeFile(filename, (prefix + text + suffix));
self.writeFile(filename, (getPrefix(testSuitesResults) + text + suffix));
}
};
})(this);

0 comments on commit 0264d28

Please sign in to comment.