Report async expectations that complete after the runable completes

It's very easy to forget to `await` or `return` the promise returned
from `expectAsync`. When that happens, the expectation failure will
occur after the spec or suite's result has been reported to reporters,
and the failure will typically not be shown to the user. This change
adds a top-level suite failure in that case, similar to the way we
report unhandled exceptions or promise rejections that occur after the
runable completes. Adding the error at the top level gives us the best
chance of getting in before the set of failures we add it to is sent
to reporters.

See #1752.
This commit is contained in:
Steve Gravrock
2019-09-27 18:31:01 -07:00
parent 9a41154e3b
commit a497d0942a
3 changed files with 170 additions and 9 deletions

View File

@@ -321,7 +321,33 @@ getJasmineRequireObj().Env = function(j$) {
}
};
var asyncExpectationFactory = function(actual, spec) {
function recordLateExpectation(runable, runableType, result) {
var delayedExpectationResult = {};
Object.keys(result).forEach(function(k) {
delayedExpectationResult[k] = result[k];
});
delayedExpectationResult.passed = false;
delayedExpectationResult.globalErrorType = 'lateExpectation';
delayedExpectationResult.message =
runableType +
' "' +
runable.getFullName() +
'" ran a "' +
result.matcherName +
'" expectation after it finished.\n';
if (result.message) {
delayedExpectationResult.message +=
'Message: "' + result.message + '"\n';
}
delayedExpectationResult.message +=
'Did you forget to return or await the result of expectAsync?';
topSuite.result.failedExpectations.push(delayedExpectationResult);
}
var asyncExpectationFactory = function(actual, spec, runableType) {
return j$.Expectation.asyncFactory({
util: j$.matchersUtil,
customEqualityTesters: runnableResources[spec.id].customEqualityTesters,
@@ -331,9 +357,19 @@ getJasmineRequireObj().Env = function(j$) {
});
function addExpectationResult(passed, result) {
if (currentRunnable() !== spec) {
recordLateExpectation(spec, runableType, result);
}
return spec.addExpectationResult(passed, result);
}
};
var suiteAsyncExpectationFactory = function(actual, suite) {
return asyncExpectationFactory(actual, suite, 'Suite');
};
var specAsyncExpectationFactory = function(actual, suite) {
return asyncExpectationFactory(actual, suite, 'Spec');
};
var defaultResourcesForRunnable = function(id, parentRunnableId) {
var resources = {
@@ -551,7 +587,7 @@ getJasmineRequireObj().Env = function(j$) {
id: getNextSuiteId(),
description: 'Jasmine__TopLevel__Suite',
expectationFactory: expectationFactory,
asyncExpectationFactory: asyncExpectationFactory,
asyncExpectationFactory: suiteAsyncExpectationFactory,
expectationResultFactory: expectationResultFactory
});
defaultResourcesForRunnable(topSuite.id);
@@ -884,7 +920,7 @@ getJasmineRequireObj().Env = function(j$) {
description: description,
parentSuite: currentDeclarationSuite,
expectationFactory: expectationFactory,
asyncExpectationFactory: asyncExpectationFactory,
asyncExpectationFactory: suiteAsyncExpectationFactory,
expectationResultFactory: expectationResultFactory,
throwOnExpectationFailure: config.oneFailurePerSpec
});
@@ -978,7 +1014,7 @@ getJasmineRequireObj().Env = function(j$) {
id: getNextSpecId(),
beforeAndAfterFns: beforeAndAfterFns(suite),
expectationFactory: expectationFactory,
asyncExpectationFactory: asyncExpectationFactory,
asyncExpectationFactory: specAsyncExpectationFactory,
resultCallback: specResultCallback,
getSpecName: function(spec) {
return getSpecName(spec, suite);