diff --git a/spec/core/matchers/async/toBeRejectedWithMatchingSpec.js b/spec/core/matchers/async/toBeRejectedWithMatchingSpec.js new file mode 100644 index 00000000..1805cea2 --- /dev/null +++ b/spec/core/matchers/async/toBeRejectedWithMatchingSpec.js @@ -0,0 +1,78 @@ +describe('#toBeRejectedWithMatching', function() { + it('passes if the promise is rejected with something matching the predicate', function() { + const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(), + actual = Promise.reject(new Error('nope')), + predicate = value => value.message === 'nope'; + + return matcher.compare(actual, predicate).then(function(result) { + expect(result).toEqual(jasmine.objectContaining({ pass: true })); + }); + }); + + it('fails if the promise resolves', function() { + const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(), + actual = Promise.resolve(), + predicate = () => true; + + return matcher.compare(actual, predicate).then(function(result) { + expect(result).toEqual(jasmine.objectContaining({ pass: false })); + }); + }); + + it('fails if the promise is rejected with something not matching the predicate', function() { + const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(), + actual = Promise.reject('A Bad Apple'), + predicate = value => value === 'A Good Orange'; + + return matcher.compare(actual, predicate).then(function(result) { + expect(result).toEqual( + jasmine.objectContaining({ + pass: false, + message: + 'Expected a promise to be rejected matching a predicate, but the predicate returned "false".' + }) + ); + }); + }); + + it('should build its error correctly when negated', function() { + const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(), + actual = Promise.reject(true), + predicate = () => true; + + return matcher.compare(actual, predicate).then(function(result) { + expect(result).toEqual( + jasmine.objectContaining({ + pass: true, + message: + 'Expected a promise not to be rejected matching a predicate, but the predicate returned "true".' + }) + ); + }); + }); + + it('fails if actual is not a promise', function() { + const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(), + actual = 'not a promise'; + + function f() { + return matcher.compare(actual); + } + + expect(f).toThrowError( + 'Expected toBeRejectedWithMatching to be called on a promise but was on a string.' + ); + }); + + it('fails if predicate is not a function', function() { + const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(), + actual = Promise.resolve(), + predicate = 'not a function'; + + function f() { + return matcher.compare(actual, predicate); + } + + expect(f).toThrowError(/Predicate is not a Function/); + }); +}); diff --git a/src/core/matchers/async/toBeRejectedWithMatching.js b/src/core/matchers/async/toBeRejectedWithMatching.js new file mode 100644 index 00000000..c27345db --- /dev/null +++ b/src/core/matchers/async/toBeRejectedWithMatching.js @@ -0,0 +1,64 @@ +getJasmineRequireObj().toBeRejectedWithMatching = function(j$) { + 'use strict'; + + const usageError = j$.private.formatErrorMsg( + '', + 'expect(function() {}).toBeRejectedWithMatching()' + ); + + /** + * Expect a promise to be rejected with a value matching a predicate. + * @function + * @async + * @name async-matchers#toBeRejectedWithMatching + * @since 6.2.0 + * @param {Function} predicate - A function that takes the rejected promise value as its parameter and returns true if it matches. + * @example + * await expectAsync(aPromise).toBeRejectedWithMatching((error) => error.message === 'Some error'); + * @example + * return expectAsync(aPromise).toBeRejectedWithMatching((error) => error.message === 'Some error'); + */ + return function toBeRejectedWithMatching() { + return { + compare: function(actualPromise, predicate) { + if (!j$.private.isPromiseLike(actualPromise)) { + throw new Error( + `Expected toBeRejectedWithMatching to be called on a promise but was on a ${typeof actualPromise}.` + ); + } + + if (typeof predicate !== 'function') { + throw new Error(usageError('Predicate is not a Function')); + } + + function prefix(passed) { + return ( + 'Expected a promise ' + + (passed ? 'not ' : '') + + 'to be rejected matching a predicate' + ); + } + + return actualPromise.then( + function() { + return { + pass: false, + message: prefix(false) + ', but it was resolved.' + }; + }, + function(actualValue) { + const result = predicate(actualValue); + return { + pass: Boolean(result), + message: + prefix(result) + + ', but the predicate returned "' + + result + + '".' + }; + } + ); + } + }; + }; +}; diff --git a/src/core/matchers/requireAsyncMatchers.js b/src/core/matchers/requireAsyncMatchers.js index b24717db..0794f88e 100644 --- a/src/core/matchers/requireAsyncMatchers.js +++ b/src/core/matchers/requireAsyncMatchers.js @@ -7,7 +7,8 @@ getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { 'toBeRejected', 'toBeResolvedTo', 'toBeRejectedWith', - 'toBeRejectedWithError' + 'toBeRejectedWithError', + 'toBeRejectedWithMatching' ], matchers = {};