Skip to content

Commit 22db501

Browse files
authored
Merge pull request #230 from stevenxu-db/escape-fix
Remove invalid ESC control sequence from XML output
2 parents ff4efe6 + 862b858 commit 22db501

File tree

3 files changed

+104
-2
lines changed

3 files changed

+104
-2
lines changed
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: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,22 @@ 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+
});
36+
37+
afterEach(() => {
38+
jest.clearAllMocks();
3039

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

3347
it('should generate valid xml with default name', () => {
3448
const noFailingTestsReport = require('../__mocks__/no-failing-tests.json');
@@ -67,6 +81,20 @@ describe('jest-junit', () => {
6781
expect(xmlDoc).toBeTruthy();
6882
});
6983

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

7199
it('should generate xml at the output filepath defined by JEST_JUNIT_OUTPUT_FILE', () => {
72100
process.env.JEST_JUNIT_OUTPUT_FILE = 'path_to_output/output_name.xml'
@@ -83,6 +111,7 @@ describe('jest-junit', () => {
83111
});
84112

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

utils/buildJsonResults.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ const generateTestCase = function(junitOptions, suiteOptions, tc, filepath, file
7474
failureMessages.forEach((failure) => {
7575
const tagName = tc.status === testFailureStatus ? 'failure': testErrorStatus
7676
testCase.testcase.push({
77-
[tagName]: stripAnsi(failure)
77+
[tagName]: strip(failure)
7878
});
7979
})
8080
}
@@ -102,6 +102,11 @@ const addErrorTestResult = function (suite) {
102102
})
103103
}
104104

105+
// Strips escape codes for readability and illegal XML characters to produce valid output.
106+
const strip = function (str) {
107+
return stripAnsi(str).replace(/\u001b/g, '');
108+
}
109+
105110
module.exports = function (report, appDirectory, options, rootDir = null) {
106111
// Check if there is a junitProperties.js (or whatever they called it)
107112
const junitSuitePropertiesFilePath = getTestSuitePropertiesPath(

0 commit comments

Comments
 (0)