Skip to content

Commit

Permalink
Merge pull request #214 from mastrzyz/master
Browse files Browse the repository at this point in the history
Failures in Test hooks like "AfterAll" don't get shown in the Junit Results
  • Loading branch information
palmerj3 committed Jun 25, 2022
2 parents 27b4389 + fb44ed0 commit 3009192
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 49 deletions.
65 changes: 65 additions & 0 deletions __mocks__/no-failing-tests-with-testexec-failure.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"numFailedTestSuites": 0,
"numFailedTests": 0,
"numPassedTestSuites": 1,
"numPassedTests": 1,
"numPendingTestSuites": 0,
"numPendingTests": 0,
"numRuntimeErrorTestSuites": 0,
"numTotalTestSuites": 1,
"numTotalTests": 1,
"snapshot": {
"added": 0,
"failure": false,
"filesAdded": 0,
"filesRemoved": 0,
"filesUnmatched": 0,
"filesUpdated": 0,
"matched": 0,
"total": 0,
"unchecked": 0,
"unmatched": 0,
"updated": 0
},
"startTime": 1489712747092,
"success": true,
"testResults": [
{
"console": null,
"failureMessage": null,
"testExecError": "beforeAll has crashed",
"numFailingTests": 0,
"numPassingTests": 1,
"numPendingTests": 0,
"perfStats": {
"end": 1489712747644,
"start": 1489712747524
},
"snapshot": {
"added": 0,
"fileDeleted": false,
"matched": 0,
"unchecked": 0,
"unmatched": 0,
"updated": 0
},
"testFilePath": "/path/to/test/__tests__/foo.test.js",
"testResults": [
{
"ancestorTitles": [
"foo",
"baz"
],
"duration": 1,
"failureMessages": [],
"fullName": "foo baz should bar",
"numPassingAsserts": 0,
"status": "passed",
"title": "should bar"
}
],
"skipped": false
}
],
"wasInterrupted": false
}
11 changes: 11 additions & 0 deletions __tests__/buildJsonResults.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,17 @@ describe('buildJsonResults', () => {
expect(errorSuite.testcase[1].error).toContain("Cannot find module './mult'");
});

it('should include a failing testcase from a suite with passing testcases but a failure from "testExec" ', () => {
const failingTestsReport = require('../__mocks__/no-failing-tests-with-testexec-failure.json');

jsonResults = buildJsonResults(failingTestsReport, '/path/to/test',
Object.assign({}, constants.DEFAULT_OPTIONS, {}));

const errorSuite = jsonResults.testsuites[1].testsuite[3];
expect(slash(errorSuite.testcase[0]._attr.name)).toContain('Test execution failure');
expect(errorSuite.testcase[1].failure).toContain("beforeAll has crashed");
});

it('should report empty suites as error', () => {
const failingTestsReport = require('../__mocks__/empty-suite.json');

Expand Down
129 changes: 80 additions & 49 deletions utils/buildJsonResults.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const toTemplateTag = function (varName) {
return "{" + varName + "}";
}

const testFailureStatus = 'failed';
const testErrorStatus = 'error';

// Replaces var using a template string or a function.
// When strOrFunc is a template string replaces {varname} with the value from the variables map.
// When strOrFunc is a function it returns the result of the function to which the variables are passed.
Expand All @@ -35,6 +38,58 @@ const executionTime = function (startTime, endTime) {
return (endTime - startTime) / 1000;
}

const generateTestCase = function(junitOptions, suiteOptions, tc, filepath, filename, suiteTitle, displayName){
const classname = tc.ancestorTitles.join(suiteOptions.ancestorSeparator);
const testTitle = tc.title;

// Build replacement map
let testVariables = {};
testVariables[constants.FILEPATH_VAR] = filepath;
testVariables[constants.FILENAME_VAR] = filename;
testVariables[constants.SUITENAME_VAR] = suiteTitle;
testVariables[constants.CLASSNAME_VAR] = classname;
testVariables[constants.TITLE_VAR] = testTitle;
testVariables[constants.DISPLAY_NAME_VAR] = displayName;

let testCase = {
'testcase': [{
_attr: {
classname: replaceVars(suiteOptions.classNameTemplate, testVariables),
name: replaceVars(suiteOptions.titleTemplate, testVariables),
time: tc.duration / 1000
}
}]
};

if (suiteOptions.addFileAttribute === 'true') {
testCase.testcase[0]._attr.file = filepath;
}

// Write out all failure messages as <failure> tags
// Nested underneath <testcase> tag
if (tc.status === testFailureStatus || tc.status === testErrorStatus) {
const failureMessages = junitOptions.noStackTrace === 'true' && tc.failureDetails ?
tc.failureDetails.map(detail => detail.message) : tc.failureMessages;

failureMessages.forEach((failure) => {
const tagName = tc.status === testFailureStatus ? 'failure': testErrorStatus
testCase.testcase.push({
[tagName]: stripAnsi(failure)
});
})
}

// Write out a <skipped> tag if test is skipped
// Nested underneath <testcase> tag
if (tc.status === 'pending') {
testCase.testcase.push({
skipped: {}
});
}

return testCase;
}

const addErrorTestResult = function (suite) {
suite.testResults.push({
"ancestorTitles": [],
Expand All @@ -43,7 +98,7 @@ const addErrorTestResult = function (suite) {
suite.failureMessage
],
"numPassingAsserts": 0,
"status": "error"
"status": testErrorStatus
})
}

Expand Down Expand Up @@ -163,57 +218,33 @@ module.exports = function (report, appDirectory, options, rootDir = null) {

// Iterate through test cases
suite.testResults.forEach((tc) => {
const classname = tc.ancestorTitles.join(suiteOptions.ancestorSeparator);
const testTitle = tc.title;

// Build replacement map
let testVariables = {};
testVariables[constants.FILEPATH_VAR] = filepath;
testVariables[constants.FILENAME_VAR] = filename;
testVariables[constants.SUITENAME_VAR] = suiteTitle;
testVariables[constants.CLASSNAME_VAR] = classname;
testVariables[constants.TITLE_VAR] = testTitle;
testVariables[constants.DISPLAY_NAME_VAR] = displayName;

let testCase = {
'testcase': [{
_attr: {
classname: replaceVars(suiteOptions.classNameTemplate, testVariables),
name: replaceVars(suiteOptions.titleTemplate, testVariables),
time: tc.duration / 1000
}
}]
};

if (suiteOptions.addFileAttribute === 'true') {
testCase.testcase[0]._attr.file = filepath;
}

// Write out all failure messages as <failure> tags
// Nested underneath <testcase> tag
if (tc.status === 'failed'|| tc.status === 'error') {
const failureMessages = options.noStackTrace === 'true' && tc.failureDetails ?
tc.failureDetails.map(detail => detail.message) : tc.failureMessages;

failureMessages.forEach((failure) => {
const tagName = tc.status === 'failed' ? 'failure': 'error'
testCase.testcase.push({
[tagName]: stripAnsi(failure)
});
})
}

// Write out a <skipped> tag if test is skipped
// Nested underneath <testcase> tag
if (tc.status === 'pending') {
testCase.testcase.push({
skipped: {}
});
}

const testCase = generateTestCase(options, suiteOptions, tc, filepath, filename, suiteTitle, displayName)
testSuite.testsuite.push(testCase);
});

// We have all tests passed but a failure in a test hook like in the `beforeAll` method
// Make sure we log them since Jest still reports the suite as failed
if (suite.testExecError !== undefined) {
const fakeTC = {
status: testFailureStatus,
failureMessages: [JSON.stringify(suite.testExecError)],
classname: undefined,
title: "Test execution failure: could be caused by test hooks like 'afterAll'.",
ancestorTitles: [""],
duration: 0,
};
const testCase = generateTestCase(
options,
suiteOptions,
fakeTC,
filepath,
filename,
suiteTitle,
displayName
);
testSuite.testsuite.push(testCase);
}

// Write stdout console output if available
if (suiteOptions.includeConsoleOutput === 'true' && suite.console && suite.console.length) {
// Stringify the entire console object
Expand Down

0 comments on commit 3009192

Please sign in to comment.