Skip to content

Commit 539cdbc

Browse files
committed
Remove invalid ESC control sequence from XML output
1 parent ff4efe6 commit 539cdbc

3 files changed

Lines changed: 105 additions & 2 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"numFailedTestSuites": 0,
3+
"numFailedTests": 0,
4+
"numPassedTestSuites": 1,
5+
"numPassedTests": 1,
6+
"numPendingTestSuites": 0,
7+
"numPendingTests": 0,
8+
"numRuntimeErrorTestSuites": 0,
9+
"numTotalTestSuites": 1,
10+
"numTotalTests": 1,
11+
"snapshot": {
12+
"added": 0,
13+
"failure": false,
14+
"filesAdded": 0,
15+
"filesRemoved": 0,
16+
"filesUnmatched": 0,
17+
"filesUpdated": 0,
18+
"matched": 0,
19+
"total": 0,
20+
"unchecked": 0,
21+
"unmatched": 0,
22+
"updated": 0
23+
},
24+
"startTime": 1489712747092,
25+
"success": true,
26+
"testResults": [
27+
{
28+
"console": [],
29+
"failureMessage": "\u001b[1m\u001b[31m \u001b[1m● \u001b[1mSample Failing Test › Should fail\u001b[39m\u001b[22m\n\n foo\u001bbar\n\u001b[2m \n \u001b[2mat _callee$ (\u001b[2m\u001b[0m\u001b[36mpath/to/failing.test.js\u001b[39m\u001b[0m\u001b[2m:26:15)\u001b[2m\n \u001b[2mat tryCatch (\u001b[2m\u001b[0m\u001b[36mnode_modules/regenerator-runtime/runtime.js\u001b[39m\u001b[0m\u001b[2m:64:40)\u001b[2m\n \u001b[2mat GeneratorFunctionPrototype.invoke [as _invoke] (\u001b[2m\u001b[0m\u001b[36mnode_modules/regenerator-runtime/runtime.js\u001b[39m\u001b[0m\u001b[2m:299:22)\u001b[2m\n \u001b[2mat GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (\u001b[2m\u001b[0m\u001b[36mnode_modules/regenerator-runtime/runtime.js\u001b[39m\u001b[0m\u001b[2m:116:21)\u001b[2m\n \u001b[2mat step (\u001b[2m\u001b[0m\u001b[36mpath/to/failing.test.js\u001b[39m\u001b[0m\u001b[2m:2:394)\u001b[2m\n \u001b[2mat \u001b[2m\u001b[0m\u001b[36mpath/to/failing.test.js\u001b[39m\u001b[0m\u001b[2m:2:554\u001b[2m\u001b[22m\n",
30+
"numFailingTests": 1,
31+
"numPassingTests": 0,
32+
"numPendingTests": 0,
33+
"perfStats": {
34+
"end": 1499904221109,
35+
"start": 1499904215586
36+
},
37+
"snapshot": {
38+
"added": 0,
39+
"fileDeleted": false,
40+
"matched": 0,
41+
"unchecked": 0,
42+
"unmatched": 0,
43+
"updated": 0
44+
},
45+
"testFilePath": "/path/to/failing.test.js",
46+
"testResults": [
47+
{
48+
"ancestorTitles": [
49+
"Sample Failing Test",
50+
"Inner",
51+
"Inner Inner"
52+
],
53+
"duration": 3930,
54+
"failureMessages": [
55+
"\u001b[1m\u001b[31m \u001b[1m● \u001b[1mSample Failing Test › Inner › Inner Inner › Should fail\u001b[39m\u001b[22m\n\n foo\u001bbar\n\u001b[2m \n \u001b[2mat _callee$ (\u001b[2m\u001b[0m\u001b[36mpath/to/failing.test.js\u001b[39m\u001b[0m\u001b[2m:26:15)\u001b[2m\n \u001b[2mat tryCatch (\u001b[2m\u001b[0m\u001b[36mnode_modules/regenerator-runtime/runtime.js\u001b[39m\u001b[0m\u001b[2m:64:40)\u001b[2m\n \u001b[2mat GeneratorFunctionPrototype.invoke [as _invoke] (\u001b[2m\u001b[0m\u001b[36mnode_modules/regenerator-runtime/runtime.js\u001b[39m\u001b[0m\u001b[2m:299:22)\u001b[2m\n \u001b[2mat GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (\u001b[2m\u001b[0m\u001b[36mnode_modules/regenerator-runtime/runtime.js\u001b[39m\u001b[0m\u001b[2m:116:21)\u001b[2m\n \u001b[2mat step (\u001b[2m\u001b[0m\u001b[36mpath/to/failing.test.js\u001b[39m\u001b[0m\u001b[2m:2:394)\u001b[2m\n \u001b[2mat \u001b[2m\u001b[0m\u001b[36mpath/to/failing.test.js\u001b[39m\u001b[0m\u001b[2m:2:554\u001b[2m\u001b[22m\n"
56+
],
57+
"fullName": "Sample Failing Test Inner Inner Inner Should fail",
58+
"numPassingAsserts": 0,
59+
"status": "failed",
60+
"title": "Should fail"
61+
}
62+
],
63+
"sourceMaps": {},
64+
"skipped": false
65+
}
66+
],
67+
"wasInterrupted": false
68+
}

__tests__/testResultProcessor.test.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,24 @@ const path = require('path');
2727
const testResultProcessor = require('../');
2828

2929
describe('jest-junit', () => {
30+
beforeEach(() => {
31+
const foundKeys = Object.keys(process.env).filter(k => k.startsWith('JEST_JUNIT'));
32+
if (foundKeys.length > 0) {
33+
throw new Error(`process.env should not have JEST_JUNIT keys set. Found: ${foundKeys.join(', ')}`);
34+
}
35+
});
3036

31-
afterEach(() => jest.clearAllMocks())
37+
afterEach(() => {
38+
for (let key in process.env) {
39+
if (key.startsWith('JEST_JUNIT')) {
40+
delete process.env[key];
41+
}
42+
}
43+
});
44+
45+
afterEach(() => {
46+
jest.clearAllMocks();
47+
});
3248

3349
it('should generate valid xml with default name', () => {
3450
const noFailingTestsReport = require('../__mocks__/no-failing-tests.json');
@@ -67,6 +83,20 @@ describe('jest-junit', () => {
6783
expect(xmlDoc).toBeTruthy();
6884
});
6985

86+
it('should generate valid xml despite illegal characters', () => {
87+
const failingTestsWithEscReport = require('../__mocks__/failing-tests-with-esc.json');
88+
testResultProcessor(failingTestsWithEscReport);
89+
90+
// Ensure fs.writeFileSync is called
91+
expect(fs.writeFileSync).toHaveBeenCalledTimes(1);
92+
93+
// Ensure file would have been generated
94+
expect(fs.writeFileSync).toHaveBeenLastCalledWith(path.resolve('junit.xml'), expect.any(String));
95+
96+
// Ensure generated file is valid xml
97+
const xmlDoc = libxmljs.parseXml(fs.writeFileSync.mock.calls[0][1]);
98+
expect(xmlDoc).toBeTruthy();
99+
});
70100

71101
it('should generate xml at the output filepath defined by JEST_JUNIT_OUTPUT_FILE', () => {
72102
process.env.JEST_JUNIT_OUTPUT_FILE = 'path_to_output/output_name.xml'
@@ -83,6 +113,7 @@ describe('jest-junit', () => {
83113
});
84114

85115
it('should generate xml at the output filepath defined by outputFile config', () => {
116+
process.env.JEST_JUNIT_OUTPUT_FILE = 'path_to_output/output_name.xml'
86117
const noFailingTestsReport = require('../__mocks__/no-failing-tests.json');
87118
testResultProcessor(noFailingTestsReport, {outputFile: 'path_to_output/output_name.xml' });
88119

index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@ const processor = (report, reporterOptions = {}, jestRootDir = null) => {
3434
// Ensure output path exists
3535
mkdirp.sync(path.dirname(outputPath));
3636

37+
// Clean ESC character, which is invalid XML and some libraries include in error messages
38+
const xmlString = xml(jsonResults, { indent: ' ', declaration: true });
39+
const cleanedXmlString = xmlString.replace(/\u001b/g, '');
40+
3741
// Write data to file
38-
fs.writeFileSync(outputPath, xml(jsonResults, { indent: ' ', declaration: true }));
42+
fs.writeFileSync(outputPath, cleanedXmlString);
3943

4044
// Jest 18 compatibility
4145
return report;

0 commit comments

Comments
 (0)