diff --git a/spec/core/matchers/async/toBeRejectedWithErrorSpec.js b/spec/core/matchers/async/toBeRejectedWithErrorSpec.js new file mode 100644 index 00000000..0bb53edc --- /dev/null +++ b/spec/core/matchers/async/toBeRejectedWithErrorSpec.js @@ -0,0 +1,100 @@ +describe('#toBeRejectedWithError', function () { + it('passes when Error type matches', function () { + jasmine.getEnv().requirePromises(); + + var matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(jasmineUnderTest.matchersUtil), + actual = Promise.reject(new TypeError('foo')); + + return matcher.compare(actual, TypeError).then(function (result) { + expect(result).toEqual(jasmine.objectContaining({ pass: true })); + }); + }); + + it('passes when Error type and message matches', function () { + jasmine.getEnv().requirePromises(); + + var matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(jasmineUnderTest.matchersUtil), + actual = Promise.reject(new TypeError('foo')); + + return matcher.compare(actual, TypeError, 'foo').then(function (result) { + expect(result).toEqual(jasmine.objectContaining({ pass: true })); + }); + }); + + it('passes when Error message matches a string', function () { + jasmine.getEnv().requirePromises(); + + var matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(jasmineUnderTest.matchersUtil), + actual = Promise.reject(new Error('foo')); + + return matcher.compare(actual, 'foo').then(function (result) { + expect(result).toEqual(jasmine.objectContaining({ pass: true })); + }); + }); + + it('passes when Error message matches a RegExp', function () { + jasmine.getEnv().requirePromises(); + + var matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(jasmineUnderTest.matchersUtil), + actual = Promise.reject(new Error('foo')); + + return matcher.compare(actual, /foo/).then(function (result) { + expect(result).toEqual(jasmine.objectContaining({ pass: true })); + }); + }); + + it('passes when Error message is empty', function () { + jasmine.getEnv().requirePromises(); + + var matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(jasmineUnderTest.matchersUtil), + actual = Promise.reject(new Error()); + + return matcher.compare(actual, '').then(function (result) { + expect(result).toEqual(jasmine.objectContaining({ pass: true })); + }); + }); + + it('fails when resolved', function () { + jasmine.getEnv().requirePromises(); + + var matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(jasmineUnderTest.matchersUtil), + actual = Promise.resolve(new Error('foo')); + + return matcher.compare(actual, 'foo').then(function (result) { + expect(result).toEqual(jasmine.objectContaining({ pass: false })); + }); + }); + + it('fails when rejected with non Error type', function () { + jasmine.getEnv().requirePromises(); + + var matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(jasmineUnderTest.matchersUtil), + actual = Promise.reject('foo'); + + return matcher.compare(actual, 'foo').then(function (result) { + expect(result).toEqual(jasmine.objectContaining({ pass: false })); + }); + }); + + it('fails when Error type mismatches', function () { + jasmine.getEnv().requirePromises(); + + var matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(jasmineUnderTest.matchersUtil), + actual = Promise.reject(new Error('foo')); + + return matcher.compare(actual, TypeError, 'foo').then(function (result) { + expect(result).toEqual(jasmine.objectContaining({ pass: false })); + }); + }); + + it('fails when Error message mismatches', function () { + jasmine.getEnv().requirePromises(); + + var matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(jasmineUnderTest.matchersUtil), + actual = Promise.reject(new Error('foo')); + + return matcher.compare(actual, 'bar').then(function (result) { + expect(result).toEqual(jasmine.objectContaining({ pass: false })); + }); + }); +}); diff --git a/src/core/matchers/async/toBeRejectedWithError.js b/src/core/matchers/async/toBeRejectedWithError.js new file mode 100644 index 00000000..28f07890 --- /dev/null +++ b/src/core/matchers/async/toBeRejectedWithError.js @@ -0,0 +1,88 @@ +getJasmineRequireObj().toBeRejectedWithError = function(j$) { + /** + * Expect a promise to be rejected with a value matched to the expected + * @function + * @async + * @name async-matchers#toBeRejectedWithError + * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. + * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` + * @example + * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, 'Error message'); + * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, /Error message/); + * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError); + * await expectAsync(aPromise).toBeRejectedWithError('Error message'); + * return expectAsync(aPromise).toBeRejectedWithError(/Error message/); + */ + return function toBeRejectedWithError() { + return { + compare: function(actualPromise, arg1, arg2) { + var expected = getExpectedFromArgs(arg1, arg2); + + return actualPromise.then( + function() { + return { + pass: false, + message: 'Expected a promise to be rejected but it was resolved.' + }; + }, + function(actualValue) { return matchError(actualValue, expected); } + ); + } + }; + }; + + function matchError(actual, expected) { + if (!j$.isError_(actual)) { + return fail(expected, 'not rejected with Error.'); + } + + if (!(actual instanceof expected.error)) { + return fail(expected, 'rejected with type ' + j$.fnNameFor(actual.constructor) + '.'); + } + + var actualMessage = actual.message; + + if (actualMessage === expected.message || typeof expected.message === 'undefined') { + return pass(expected); + } + + if (expected.message instanceof RegExp && expected.message.test(actualMessage)) { + return pass(expected); + } + + return fail(expected, 'rejected with ' + j$.pp(actual) + '.'); + } + + function pass(expected) { + return { + pass: true, + message: 'Expected a promise to be rejected with ' + expected.printValue + '.' + }; + } + + function fail(expected, message) { + return { + pass: false, + message: 'Expected a promise to be rejected with ' + expected.printValue + ' but it was ' + message + }; + } + + + function getExpectedFromArgs(arg1, arg2) { + var error, message; + + if (typeof arg1 === 'function' && j$.isError_(arg1.prototype)) { + error = arg1; + message = arg2; + } else { + error = Error; + message = arg1; + } + + return { + error: error, + message: message, + printValue: j$.fnNameFor(error) + ': ' + j$.pp(message) + }; + } +}; diff --git a/src/core/matchers/requireAsyncMatchers.js b/src/core/matchers/requireAsyncMatchers.js index 1474c22d..faa91c02 100644 --- a/src/core/matchers/requireAsyncMatchers.js +++ b/src/core/matchers/requireAsyncMatchers.js @@ -3,7 +3,8 @@ getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { 'toBeResolved', 'toBeRejected', 'toBeResolvedTo', - 'toBeRejectedWith' + 'toBeRejectedWith', + 'toBeRejectedWithError' ], matchers = {};