Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const Testsuites = require('./src/Testsuites');
const xml = require('xml');
const fs = require('fs');
const path = require('path');
const out = process.env.TEST_REPORT_PATH || process.cwd();
const filename = process.env.TEST_REPORT_FILENAME || 'test-report.xml';

module.exports = (results) => {
const testSuites = new Testsuites(results);
const data = xml(testSuites, { declaration: true, indent: ' ' });
fs.writeFileSync(`${out}/test-report.xml`, data);
fs.writeFileSync(path.join(out, filename), data);
};
4 changes: 4 additions & 0 deletions src/Testcase.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class Testcase {
}
];

if (result.status === 'pending') {
testCase.push({ skipped: {} });
}

this.testcase = testCase.concat(failures);
}
}
Expand Down
9 changes: 2 additions & 7 deletions src/Testsuite.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const Testcase = require('./Testcase');
class Testsuite {
constructor (id, result) {
let testcases = result.testResults
.filter(result => (result.status !== 'pending'))
.map(result => new Testcase(result));

let suite = {
Expand All @@ -15,17 +14,13 @@ class Testsuite {
hostname: 'localhost',
tests: (result.numPendingTests + result.numFailingTests + result.numPassingTests),
failures: result.numFailingTests,
skipped: result.numPendingTests,
time: (result.perfStats.end - result.perfStats.start) / 1000,
timestamp: new Date(result.perfStats.start).toISOString().slice(0, -5)
}
};

this.testsuite = [suite, { properties: [] }]
.concat(
testcases,
{ 'system-out': {} },
{ 'system-err': {} }
);
this.testsuite = [suite].concat(testcases);
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/__tests__/Testcase.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,16 @@ it('should produce a <failure>', () => {
const expected = `<testcase classname="foo" name="should foo bar" time="0"><failure message="Assertion error" type="AssertionError"></failure></testcase>`;
expect(report).toEqual(expected);
});

it('should produce a <skipped>', () => {
const testcase = {
title: 'should foo bar',
status: 'pending',
ancestorTitles: ['boo', 'foo'],
failureMessages: []
};
const result = new Testcase(testcase);
const report = xml(result);
const expected = `<testcase classname="foo" name="should foo bar" time="0"><skipped/></testcase>`;
expect(report).toEqual(expected);
});
92 changes: 92 additions & 0 deletions src/__tests__/fixtures/junit4.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:element name="failure">
<xs:complexType mixed="true">
<xs:attribute name="type" type="xs:string" use="optional"/>
<xs:attribute name="message" type="xs:string" use="optional"/>
</xs:complexType>
</xs:element>

<xs:element name="error">
<xs:complexType mixed="true">
<xs:attribute name="type" type="xs:string" use="optional"/>
<xs:attribute name="message" type="xs:string" use="optional"/>
</xs:complexType>
</xs:element>

<xs:element name="properties">
<xs:complexType>
<xs:sequence>
<xs:element ref="property" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:element name="property">
<xs:complexType>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="value" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>

<xs:element name="skipped" type="xs:string"/>
<xs:element name="system-err" type="xs:string"/>
<xs:element name="system-out" type="xs:string"/>

<xs:element name="testcase">
<xs:complexType>
<xs:sequence>
<xs:element ref="skipped" minOccurs="0" maxOccurs="1"/>
<xs:element ref="error" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="failure" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="system-out" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="system-err" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="assertions" type="xs:string" use="optional"/>
<xs:attribute name="time" type="xs:string" use="optional"/>
<xs:attribute name="classname" type="xs:string" use="optional"/>
<xs:attribute name="status" type="xs:string" use="optional"/>
</xs:complexType>
</xs:element>

<xs:element name="testsuite">
<xs:complexType>
<xs:sequence>
<xs:element ref="properties" minOccurs="0" maxOccurs="1"/>
<xs:element ref="testsuite" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="testcase" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="system-out" minOccurs="0" maxOccurs="1"/>
<xs:element ref="system-err" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="tests" type="xs:string" use="required"/>
<xs:attribute name="failures" type="xs:string" use="optional"/>
<xs:attribute name="errors" type="xs:string" use="optional"/>
<xs:attribute name="time" type="xs:string" use="optional"/>
<xs:attribute name="disabled" type="xs:string" use="optional"/>
<xs:attribute name="skipped" type="xs:string" use="optional"/>
<xs:attribute name="timestamp" type="xs:string" use="optional"/>
<xs:attribute name="hostname" type="xs:string" use="optional"/>
<xs:attribute name="id" type="xs:string" use="optional"/>
<xs:attribute name="package" type="xs:string" use="optional"/>
</xs:complexType>
</xs:element>

<xs:element name="testsuites">
<xs:complexType>
<xs:sequence>
<xs:element ref="testsuite" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="optional"/>
<xs:attribute name="time" type="xs:string" use="optional"/>
<xs:attribute name="tests" type="xs:string" use="optional"/>
<xs:attribute name="failures" type="xs:string" use="optional"/>
<xs:attribute name="disabled" type="xs:string" use="optional"/>
<xs:attribute name="errors" type="xs:string" use="optional"/>
</xs:complexType>
</xs:element>


</xs:schema>
9 changes: 4 additions & 5 deletions src/__tests__/fixtures/testsuite.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<testsuite id="1" name="src/foo.js" errors="0" package="src/foo.js" hostname="localhost" tests="3" failures="1" time="5" timestamp="2017-01-10T19:21:08">
<properties>
</properties>
<testsuite id="1" name="src/foo.js" errors="0" package="src/foo.js" hostname="localhost" tests="3" failures="1" skipped="1" time="5" timestamp="2017-01-10T19:21:08">
<testcase classname="foo" name="should foobar the baz" time="0">
<skipped/>
</testcase>
<testcase classname="foo" name="should foo the bar" time="0">
<failure message="Assertion error" type="AssertionError">
</failure>
</testcase>
<testcase classname="foo" name="should foofoo the baz" time="0">
</testcase>
<system-out/>
<system-err/>
</testsuite>
13 changes: 10 additions & 3 deletions src/__tests__/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const cwd = process.cwd();
const reporter = require('../../index');
const { failedCase, pendingCase, passingCase } = require('./fixtures/testcase');
const junitXsd = fs.readFileSync(`${__dirname}/fixtures/junit.xsd`, {encoding: 'utf-8'});
const junit4Xsd = fs.readFileSync(`${__dirname}/fixtures/junit4.xsd`, {encoding: 'utf-8'});
const mock = {
"success": true,
"startTime": Date.now(),
Expand All @@ -30,10 +31,16 @@ const mock = {
]
};

const validateXml = (junitXsd, xml) => {
const schema = xsd.parse(junitXsd);
return schema.validate(xml);
}

it('should produce a valid JUnit XML report', () => {
reporter(mock);
const report = fs.readFileSync(`${cwd}/test-report.xml`, { encoding: 'utf-8' });
const schema = xsd.parse(junitXsd);
const errors = schema.validate(report);
expect(errors).toBeNull();
const errors = validateXml(junitXsd, report);
const errors4 = validateXml(junit4Xsd, report);
// The XML should comply ethier JUnit XSD or JUnit4 XSD
expect(!!errors && !!errors4).toBeFalsy();
});