diff --git a/package.json b/package.json index 5c61a94c..ede9acc6 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,10 @@ "watchPlugins": [ "jest-watch-typeahead/filename", "jest-watch-typeahead/testname" + ], + "reporters": [ + "/test/reporters/ExceptionlessExpectedFailureReporter.js", + "default" ] }, "babel": { diff --git a/src/matchers/fail.js b/src/matchers/fail.js index 410f4146..d654c05a 100644 --- a/src/matchers/fail.js +++ b/src/matchers/fail.js @@ -1,4 +1,5 @@ export function fail(_, message) { + this.dontThrow(); return { pass: false, message: () => (message ? message : 'fails by .fail() assertion'), diff --git a/test/matchers/__snapshots__/fail.test.js.snap b/test/matchers/__snapshots__/fail.test.js.snap deleted file mode 100644 index 788a25ab..00000000 --- a/test/matchers/__snapshots__/fail.test.js.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`.fail fails with message 1`] = `"This shouldn't fail!"`; - -exports[`.fail fails without message 1`] = `"fails by .fail() assertion"`; diff --git a/test/matchers/fail.test.js b/test/matchers/fail.test.js index 7bfa1002..9d88eb5e 100644 --- a/test/matchers/fail.test.js +++ b/test/matchers/fail.test.js @@ -1,13 +1,19 @@ import * as matcher from 'src/matchers/fail'; expect.extend(matcher); - describe('.fail', () => { test('fails without message', () => { - expect(() => expect().fail()).toThrowErrorMatchingSnapshot(); + expect().fail(); // This should fail! }); test('fails with message', () => { - expect(() => expect().fail("This shouldn't fail!")).toThrowErrorMatchingSnapshot(); + expect().fail('This should fail!'); + }); + test('fails when invoked in a try/catch', () => { + try { + expect().fail(); + } catch (error) { + expect('this assertion').toBe('not checked'); + } }); }); @@ -16,6 +22,6 @@ describe('.not.fail', () => { expect().not.fail(); }); test('does not fail with message', () => { - expect().not.fail('this should fail!'); + expect().not.fail('this should not fail!'); }); }); diff --git a/test/reporters/ExceptionlessExpectedFailureReporter.js b/test/reporters/ExceptionlessExpectedFailureReporter.js new file mode 100644 index 00000000..20c31638 --- /dev/null +++ b/test/reporters/ExceptionlessExpectedFailureReporter.js @@ -0,0 +1,83 @@ +/** + * Flips the test results for fail.test.js > .fail > + */ +class ExceptionlessExpectedFailureReporter { + constructor(globalConfig, reporterOptions, reporterContext) { + this._globalConfig = globalConfig; + this._options = reporterOptions; + this._context = reporterContext; + } + onTestCaseResult(test, testCaseResult) { + this._processTestCaseResult(testCaseResult); + } + onTestFileResult(test, testResult, results) { + if (testResult.testFilePath.endsWith('fail.test.js')) { + this._processTestResults(results); + } + } + _processTestResults(results) { + for (let testSuiteResult of results.testResults) { + if (testSuiteResult.testFilePath.endsWith('fail.test.js')) { + let switchedToFailing = 0; + let switchedToPassing = 0; + for (let testCaseResult of testSuiteResult.testResults) { + const processResult = this._processTestCaseResult(testCaseResult); + if (processResult === 'switch-to-failing') switchedToFailing++; + if (processResult === 'switch-to-passing') switchedToPassing++; + } + const originalFailureCount = testSuiteResult.numFailingTests; + testSuiteResult.numFailingTests += switchedToFailing - switchedToPassing; + results.numFailedTests += switchedToFailing - switchedToPassing; + testSuiteResult.numPassingTests += switchedToPassing - switchedToFailing; + results.numPassedTests += switchedToPassing - switchedToFailing; + if (originalFailureCount === switchedToPassing) { + testSuiteResult.failureMessage = ''; + results.numFailedTestSuites -= 1; + results.numPassedTestSuites += 1; + if (results.numFailedTestSuites === 0) results.success = true; + console.log('marking failing test suite as passing', testSuiteResult.testFilePath); + } + } + } + } + + _processTestCaseResult(testCaseResult) { + if (this._hasDotFailAncestor(testCaseResult)) { + if (testCaseResult.status === 'failed') { + this._markPassing(testCaseResult); + return 'switch-to-passing'; + } else if (testCaseResult.status === 'passed') { + this._markFailing(testCaseResult); + return 'switch-to-failing'; + } + } + return 'unchanged'; + } + _hasDotFailAncestor(result) { + return result.ancestorTitles.length > 0 && result.ancestorTitles[0] === '.fail'; + } + _markPassing(result) { + result.status = 'passed'; + result.failureDetails = []; + result.failureMessages = []; + result.numPassingAsserts = 1; + } + _markFailing(result) { + const message = `${result.fullName} was expected to fail, but did not.`; + result.status = 'failed'; + result.failureDetails = [ + { + matcherResult: { + pass: false, + message: message, + }, + message: message, + stack: `${message}\n\tNo stack trace.\n\tThis is a placeholder message generated inside ExceptionlessExpectedFailureReporter`, + }, + ]; + result.failureMessages = [message]; + result.numPassingAsserts = 0; + } +} + +module.exports = ExceptionlessExpectedFailureReporter;