Skip to content

Commit 6a84a3e

Browse files
authored
Merge pull request #160 from sginter/noStackTrace
Add noStackTrace config setting to omit stack traces from the reports
2 parents 017af76 + 8aab1d5 commit 6a84a3e

File tree

5 files changed

+137
-2
lines changed

5 files changed

+137
-2
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Environment variable and package.json configuration should be **strings**.
5555
Reporter options should also be strings exception for suiteNameTemplate, classNameTemplate, titleNameTemplate that can also accept a function returning a string.
5656

5757
| Environment Variable Name | Reporter Config Name| Description | Default | Possible Injection Values
58-
|--|--|--|--|--|
58+
|---|---|---|---|---|
5959
| `JEST_SUITE_NAME` | `suiteName` | `name` attribute of `<testsuites>` | `"jest tests"` | N/A
6060
| `JEST_JUNIT_OUTPUT_DIR` | `outputDirectory` | Directory to save the output. | `process.cwd()` | N/A
6161
| `JEST_JUNIT_OUTPUT_NAME` | `outputName` | File name for the output. | `"junit.xml"` | N/A
@@ -69,6 +69,7 @@ Reporter options should also be strings exception for suiteNameTemplate, classNa
6969
| `JEST_JUNIT_INCLUDE_CONSOLE_OUTPUT` | `includeConsoleOutput` | Adds console output to any testSuite that generates stdout during a test run. | `false` | N/A
7070
| `JEST_JUNIT_INCLUDE_SHORT_CONSOLE_OUTPUT` | `includeShortConsoleOutput` | Adds short console output (only message value) to any testSuite that generates stdout during a test run. | `false` | N/A
7171
| `JEST_JUNIT_REPORT_TEST_SUITE_ERRORS` | `reportTestSuiteErrors` | Reports test suites that failed to execute altogether as `error`. _Note:_ since the suite name cannot be determined from files that fail to load, it will default to file path.| `false` | N/A
72+
| `JEST_JUNIT_NO_STACK_TRACE` | `noStackTrace` | Omit stack traces from test failure reports, similar to `jest --noStackTrace` | `false` | N/A
7273
| `JEST_USE_PATH_FOR_SUITE_NAME` | `usePathForSuiteName` | **DEPRECATED. Use `suiteNameTemplate` instead.** Use file path as the `name` attribute of `<testsuite>` | `"false"` | N/A
7374

7475

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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 foobar\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+
"failureDetails": [
55+
{
56+
"error": {
57+
"matcherResult": {
58+
},
59+
"message": "\u001b[1m\u001b[31m \u001b[1m● \u001b[1mSample Failing Test › Inner › Inner Inner › Should fail\u001b[39m\u001b[22m\n\n foobar\n\u001b[2m \n \u001b[2m",
60+
"stack": "\u001b[1m\u001b[31m \u001b[1m● \u001b[1mSample Failing Test › Inner › Inner Inner › Should fail\u001b[39m\u001b[22m\n\n foobar\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"
61+
},
62+
"message": "\u001b[1m\u001b[31m \u001b[1m● \u001b[1mSample Failing Test › Inner › Inner Inner › Should fail\u001b[39m\u001b[22m\n\n foobar\n\u001b[2m \n \u001b[2m",
63+
"passed": false,
64+
"stack": "\u001b[1m\u001b[31m \u001b[1m● \u001b[1mSample Failing Test › Inner › Inner Inner › Should fail\u001b[39m\u001b[22m\n\n foobar\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"
65+
}
66+
],
67+
"failureMessages": [
68+
"\u001b[1m\u001b[31m \u001b[1m● \u001b[1mSample Failing Test › Inner › Inner Inner › Should fail\u001b[39m\u001b[22m\n\n foobar\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"
69+
],
70+
"fullName": "Sample Failing Test Inner Inner Inner Should fail",
71+
"numPassingAsserts": 0,
72+
"status": "failed",
73+
"title": "Should fail"
74+
}
75+
],
76+
"sourceMaps": {},
77+
"skipped": false
78+
}
79+
],
80+
"wasInterrupted": false
81+
}

__tests__/buildJsonResults.test.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,54 @@ describe('buildJsonResults', () => {
273273

274274
});
275275

276+
it('should parse messages without stack trace when notStackTrace set to true and jest >= 26.3.0', () => {
277+
const failingTestsReport = require('../__mocks__/failing-tests-with-failure-details.json');
278+
jsonResults = buildJsonResults(failingTestsReport, '/path/to/test',
279+
Object.assign({}, constants.DEFAULT_OPTIONS, {
280+
noStackTrace: "true"
281+
}));
282+
283+
const failureMsg = jsonResults.testsuites[1].testsuite[2].testcase[1].failure;
284+
285+
// Make sure no escape codes are there that exist in the mock
286+
expect(failureMsg.includes('\u001b')).toBe(false);
287+
expect(failureMsg).toMatch('Should fail');
288+
expect(failureMsg).not.toMatch('at _callee$ (path/to/failing.test.js:26:15)');
289+
expect(failureMsg).not.toMatch('at path/to/failing.test.js:2:554');
290+
291+
});
292+
293+
it('should parse messages with stack trace when notStackTrace set to false and jest >= 26.3.0', () => {
294+
const failingTestsReport = require('../__mocks__/failing-tests-with-failure-details.json');
295+
jsonResults = buildJsonResults(failingTestsReport, '/path/to/test',
296+
Object.assign({}, constants.DEFAULT_OPTIONS, {
297+
noStackTrace: "false"
298+
}));
299+
300+
const failureMsg = jsonResults.testsuites[1].testsuite[2].testcase[1].failure;
301+
302+
// Make sure no escape codes are there that exist in the mock
303+
expect(failureMsg.includes('\u001b')).toBe(false);
304+
expect(failureMsg).toMatch('Should fail');
305+
expect(failureMsg).toMatch('at _callee$ (path/to/failing.test.js:26:15)');
306+
expect(failureMsg).toMatch('at path/to/failing.test.js:2:554');
307+
308+
});
309+
310+
it('should parse failure messages for failing tests and not crash when notStackTrace set to true and jest < 26.3.0', () => {
311+
const failingTestsReport = require('../__mocks__/failing-tests.json'); // no failure details
312+
jsonResults = buildJsonResults(failingTestsReport, '/path/to/test',
313+
Object.assign({}, constants.DEFAULT_OPTIONS, {
314+
noStackTrace: "true"
315+
}));
316+
317+
const failureMsg = jsonResults.testsuites[1].testsuite[2].testcase[1].failure;
318+
319+
// Make sure no escape codes are there that exist in the mock
320+
expect(failureMsg.includes('\u001b')).toBe(false);
321+
expect(failureMsg).toMatch('Should fail');
322+
});
323+
276324
it('should support displayName template var for jest multi-project', () => {
277325
const multiProjectNoFailingTestsReport = require('../__mocks__/multi-project-no-failing-tests.json');
278326

constants/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module.exports = {
1515
JEST_JUNIT_INCLUDE_CONSOLE_OUTPUT: 'includeConsoleOutput',
1616
JEST_JUNIT_INCLUDE_SHORT_CONSOLE_OUTPUT: 'includeShortConsoleOutput',
1717
JEST_JUNIT_REPORT_TEST_SUITE_ERRORS: 'reportTestSuiteErrors',
18+
JEST_JUNIT_NO_STACK_TRACE: "noStackTrace",
1819
JEST_USE_PATH_FOR_SUITE_NAME: 'usePathForSuiteName',
1920
JEST_JUNIT_TEST_SUITE_PROPERTIES_JSON_FILE: 'testSuitePropertiesFile'
2021
},
@@ -32,6 +33,7 @@ module.exports = {
3233
includeConsoleOutput: 'false',
3334
includeShortConsoleOutput: 'false',
3435
reportTestSuiteErrors: 'false',
36+
noStackTrace: 'false',
3537
testSuitePropertiesFile: 'junitProperties.js'
3638
},
3739
SUITENAME_VAR: 'suitename',

utils/buildJsonResults.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@ module.exports = function (report, appDirectory, options) {
188188
// Write out all failure messages as <failure> tags
189189
// Nested underneath <testcase> tag
190190
if (tc.status === 'failed'|| tc.status === 'error') {
191-
tc.failureMessages.forEach((failure) => {
191+
const failureMessages = options.noStackTrace === 'true' && tc.failureDetails ?
192+
tc.failureDetails.map(detail => detail.message) : tc.failureMessages;
193+
194+
failureMessages.forEach((failure) => {
192195
const tagName = tc.status === 'failed' ? 'failure': 'error'
193196
testCase.testcase.push({
194197
[tagName]: stripAnsi(failure)

0 commit comments

Comments
 (0)