junit-xml
Version:
JUnit XML report builder with TypeScript support
160 lines (148 loc) • 12.2 kB
text/typescript
import { parseXml } from 'libxmljs';
import { getJunitXml } from './index';
import { TestSuiteReport } from './TestResults';
// The schema we're validating against:
// https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd
// The contents of `schema` are Copyright © 2011, Windy Road Technology Pty. Limited
// and is distributed under the terms of the Apache License Version 2.0 http://www.apache.org/licenses/.
const antJunitSchema = '<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"><xs:annotation><xs:documentation xml:lang="en">JUnit test result schema for the Apache Ant JUnit and JUnitReport tasks Copyright © 2011, Windy Road Technology Pty. Limited The Apache Ant JUnit XML Schema is distributed under the terms of the Apache License Version 2.0 http://www.apache.org/licenses/ Permission to waive conditions of this license may be requested from Windy Road Support (http://windyroad.org/support).</xs:documentation></xs:annotation><xs:element name="testsuite" type="testsuite"/><xs:simpleType name="ISO8601_DATETIME_PATTERN"><xs:restriction base="xs:dateTime"><xs:pattern value="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}"/></xs:restriction></xs:simpleType><xs:element name="testsuites"><xs:annotation><xs:documentation xml:lang="en">Contains an aggregation of testsuite results</xs:documentation></xs:annotation><xs:complexType><xs:sequence><xs:element name="testsuite" minOccurs="0" maxOccurs="unbounded"><xs:complexType><xs:complexContent><xs:extension base="testsuite"><xs:attribute name="package" type="xs:token" use="required"><xs:annotation><xs:documentation xml:lang="en">Derived from testsuite/@name in the non-aggregated documents</xs:documentation></xs:annotation></xs:attribute><xs:attribute name="id" type="xs:int" use="required"><xs:annotation><xs:documentation xml:lang="en">Starts at \'0\' for the first testsuite and is incremented by 1 for each following testsuite</xs:documentation></xs:annotation></xs:attribute></xs:extension></xs:complexContent></xs:complexType></xs:element></xs:sequence></xs:complexType></xs:element><xs:complexType name="testsuite"><xs:annotation><xs:documentation xml:lang="en">Contains the results of exexuting a testsuite</xs:documentation></xs:annotation><xs:sequence><xs:element name="properties"><xs:annotation><xs:documentation xml:lang="en">Properties (e.g., environment settings) set during test execution</xs:documentation></xs:annotation><xs:complexType><xs:sequence><xs:element name="property" minOccurs="0" maxOccurs="unbounded"><xs:complexType><xs:attribute name="name" use="required"><xs:simpleType><xs:restriction base="xs:token"><xs:minLength value="1"/></xs:restriction></xs:simpleType></xs:attribute><xs:attribute name="value" type="xs:string" use="required"/></xs:complexType></xs:element></xs:sequence></xs:complexType></xs:element><xs:element name="testcase" minOccurs="0" maxOccurs="unbounded"><xs:complexType><xs:choice minOccurs="0"><xs:element name="error"><xs:annotation><xs:documentation xml:lang="en">Indicates that the test errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. Contains as a text node relevant data for the error, e.g., a stack trace</xs:documentation></xs:annotation><xs:complexType><xs:simpleContent><xs:extension base="pre-string"><xs:attribute name="message" type="xs:string"><xs:annotation><xs:documentation xml:lang="en">The error message. e.g., if a java exception is thrown, the return value of getMessage()</xs:documentation></xs:annotation></xs:attribute><xs:attribute name="type" type="xs:string" use="required"><xs:annotation><xs:documentation xml:lang="en">The type of error that occured. e.g., if a java execption is thrown the full class name of the exception.</xs:documentation></xs:annotation></xs:attribute></xs:extension></xs:simpleContent></xs:complexType></xs:element><xs:element name="failure"><xs:annotation><xs:documentation xml:lang="en">Indicates that the test failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals. Contains as a text node relevant data for the failure, e.g., a stack trace</xs:documentation></xs:annotation><xs:complexType><xs:simpleContent><xs:extension base="pre-string"><xs:attribute name="message" type="xs:string"><xs:annotation><xs:documentation xml:lang="en">The message specified in the assert</xs:documentation></xs:annotation></xs:attribute><xs:attribute name="type" type="xs:string" use="required"><xs:annotation><xs:documentation xml:lang="en">The type of the assert.</xs:documentation></xs:annotation></xs:attribute></xs:extension></xs:simpleContent></xs:complexType></xs:element></xs:choice><xs:attribute name="name" type="xs:token" use="required"><xs:annotation><xs:documentation xml:lang="en">Name of the test method</xs:documentation></xs:annotation></xs:attribute><xs:attribute name="classname" type="xs:token" use="required"><xs:annotation><xs:documentation xml:lang="en">Full class name for the class the test method is in.</xs:documentation></xs:annotation></xs:attribute><xs:attribute name="time" type="xs:decimal" use="required"><xs:annotation><xs:documentation xml:lang="en">Time taken (in seconds) to execute the test</xs:documentation></xs:annotation></xs:attribute></xs:complexType></xs:element><xs:element name="system-out"><xs:annotation><xs:documentation xml:lang="en">Data that was written to standard out while the test was executed</xs:documentation></xs:annotation><xs:simpleType><xs:restriction base="pre-string"><xs:whiteSpace value="preserve"/></xs:restriction></xs:simpleType></xs:element><xs:element name="system-err"><xs:annotation><xs:documentation xml:lang="en">Data that was written to standard error while the test was executed</xs:documentation></xs:annotation><xs:simpleType><xs:restriction base="pre-string"><xs:whiteSpace value="preserve"/></xs:restriction></xs:simpleType></xs:element></xs:sequence><xs:attribute name="name" use="required"><xs:annotation><xs:documentation xml:lang="en">Full class name of the test for non-aggregated testsuite documents. Class name without the package for aggregated testsuites documents</xs:documentation></xs:annotation><xs:simpleType><xs:restriction base="xs:token"><xs:minLength value="1"/></xs:restriction></xs:simpleType></xs:attribute><xs:attribute name="timestamp" type="ISO8601_DATETIME_PATTERN" use="required"><xs:annotation><xs:documentation xml:lang="en">when the test was executed. Timezone may not be specified.</xs:documentation></xs:annotation></xs:attribute><xs:attribute name="hostname" use="required"><xs:annotation><xs:documentation xml:lang="en">Host on which the tests were executed. \'localhost\' should be used if the hostname cannot be determined.</xs:documentation></xs:annotation><xs:simpleType><xs:restriction base="xs:token"><xs:minLength value="1"/></xs:restriction></xs:simpleType></xs:attribute><xs:attribute name="tests" type="xs:int" use="required"><xs:annotation><xs:documentation xml:lang="en">The total number of tests in the suite</xs:documentation></xs:annotation></xs:attribute><xs:attribute name="failures" type="xs:int" use="required"><xs:annotation><xs:documentation xml:lang="en">The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals</xs:documentation></xs:annotation></xs:attribute><xs:attribute name="errors" type="xs:int" use="required"><xs:annotation><xs:documentation xml:lang="en">The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test.</xs:documentation></xs:annotation></xs:attribute><xs:attribute name="time" type="xs:decimal" use="required"><xs:annotation><xs:documentation xml:lang="en">Time taken (in seconds) to execute the tests in the suite</xs:documentation></xs:annotation></xs:attribute></xs:complexType><xs:simpleType name="pre-string"><xs:restriction base="xs:string"><xs:whiteSpace value="preserve"/></xs:restriction></xs:simpleType></xs:schema>';
describe('An empty report', () => {
const emptyReport: TestSuiteReport = {
suites: [],
};
it('should be correctly serialised', () => {
expect(getJunitXml(emptyReport)).toMatchSnapshot();
});
});
describe('A minimal report', () => {
const minimalReport: TestSuiteReport = {
suites: [
{
testCases: [
{ name: 'Successful test' },
],
},
],
};
it('should be correctly serialises', () => {
expect(getJunitXml(minimalReport)).toMatchSnapshot();
});
describe('in the Ant JUnit format', () => {
it('should be correctly serialised', () => {
expect(getJunitXml(minimalReport, { schema: 'ant-junit' })).toMatchSnapshot();
});
it('should be valid w.r.t. the XML Schema', () => {
const xsd = parseXml(antJunitSchema);
const reportXml = parseXml(getJunitXml(minimalReport, { schema: 'ant-junit' }));
expect(reportXml.validate(xsd)).toBe(true);
expect(reportXml.validationErrors).toEqual([]);
});
});
});
describe('A maximally detailed report', () => {
const maximalReport: TestSuiteReport = {
name: 'Some test suite report name',
time: 4.2,
suites: [
{
name: 'Some suite',
timestamp: new Date(Date.UTC(1989, 10, 3)),
hostname: 'some-hostname',
time: 1.1337,
testCases: [
{
name: 'Successful test',
assertions: 2,
classname: 'successful-test-class',
time: 0.72,
},
{
name: 'Skipped test',
assertions: 2,
skipped: true,
},
{
name: 'Unskipped test',
skipped: false,
},
{
name: 'Failing test',
failures: [
{ message: 'First failure', type: 'some-type' },
{ message: 'Second failure' },
],
},
{
name: 'Another failing test',
failures: [
{ message: 'Just one failure' },
],
},
{
name: 'Erroring test',
errors: [
{ message: 'First error', type: 'some-type' },
{ message: 'Second error' },
],
},
{
name: 'Another erroring test',
errors: [
{ message: 'Just one error' },
],
},
{
name: 'Test with output',
systemOut: [
'First output',
'Second output',
],
},
{
name: 'Test with single output',
systemOut: [
'Only output',
],
},
{
name: 'Test with error output',
systemErr: [
'First error output',
'Second error output',
],
},
{
name: 'Test with single error output',
systemErr: [
'Only error output',
],
},
],
},
{
name: 'Suite without test cases',
testCases: [],
},
{
name: 'Another suite',
testCases: [
{ name: 'Some successful test' },
],
},
],
};
it('should be correctly serialised', () => {
expect(getJunitXml(maximalReport)).toMatchSnapshot();
});
describe('in the Ant JUnit format', () => {
it('should be correctly serialised', () => {
expect(getJunitXml(maximalReport, { schema: 'ant-junit' })).toMatchSnapshot();
});
it('should be valid w.r.t. the XML Schema', () => {
const xsd = parseXml(antJunitSchema);
const reportXml = parseXml(getJunitXml(maximalReport, { schema: 'ant-junit' }));
expect(reportXml.validate(xsd)).toBe(true);
expect(reportXml.validationErrors).toEqual([]);
});
});
});