/* * should.js - assertion library * Copyright(c) 2010-2013 TJ Holowaychuk * Copyright(c) 2013-2016 Denis Bardadym * MIT Licensed */ var util = require('../util'); var PromisedAssertion = require('../assertion').PromisedAssertion; var Assertion = require('../assertion'); module.exports = function(should) { /** * Assert given object is a Promise * * @name Promise * @memberOf Assertion * @category assertion promises * @example * * promise.should.be.Promise() * (new Promise(function(resolve, reject) { resolve(10); })).should.be.a.Promise() * (10).should.not.be.a.Promise() */ Assertion.add('Promise', function() { this.params = {operator: 'to be promise'}; var obj = this.obj; should(obj).have.property('then') .which.is.a.Function(); }); /** * Assert given promise will be fulfilled. Result of assertion is still .thenable and should be handled accordingly. * * @name fulfilled * @memberOf Assertion * @returns {Promise} * @category assertion promises * @example * * // don't forget to handle async nature * (new Promise(function(resolve, reject) { resolve(10); })).should.be.fulfilled(); * * // test example with mocha it is possible to return promise * it('is async', () => { * return new Promise(resolve => resolve(10)) * .should.be.fulfilled(); * }); */ Assertion.prototype.fulfilled = function Assertion$fulfilled() { this.params = {operator: 'to be fulfilled'}; should(this.obj).be.a.Promise(); var that = this; return this.obj.then(function next$onResolve(value) { if (that.negate) { that.fail(); } return value; }, function next$onReject(err) { if (!that.negate) { that.params.operator += ', but it was rejected with ' + should.format(err); that.fail(); } return err; }); }; /** * Assert given promise will be rejected. Result of assertion is still .thenable and should be handled accordingly. * * @name rejected * @memberOf Assertion * @category assertion promises * @returns {Promise} * @example * * // don't forget to handle async nature * (new Promise(function(resolve, reject) { resolve(10); })) * .should.not.be.rejected(); * * // test example with mocha it is possible to return promise * it('is async', () => { * return new Promise((resolve, reject) => reject(new Error('boom'))) * .should.be.rejected(); * }); */ Assertion.prototype.rejected = function() { this.params = {operator: 'to be rejected'}; should(this.obj).be.a.Promise(); var that = this; return this.obj.then(function(value) { if (!that.negate) { that.params.operator += ', but it was fulfilled'; if (arguments.length != 0) { that.params.operator += ' with ' + should.format(value); } that.fail(); } return value; }, function next$onError(err) { if (that.negate) { that.fail(); } return err; }); }; /** * Assert given promise will be fulfilled with some expected value (value compared using .eql). * Result of assertion is still .thenable and should be handled accordingly. * * @name fulfilledWith * @memberOf Assertion * @category assertion promises * @returns {Promise} * @example * * // don't forget to handle async nature * (new Promise(function(resolve, reject) { resolve(10); })) * .should.be.fulfilledWith(10); * * // test example with mocha it is possible to return promise * it('is async', () => { * return new Promise((resolve, reject) => resolve(10)) * .should.be.fulfilledWith(10); * }); */ Assertion.prototype.fulfilledWith = function(expectedValue) { this.params = {operator: 'to be fulfilled with ' + should.format(expectedValue)}; should(this.obj).be.a.Promise(); var that = this; return this.obj.then(function(value) { if (that.negate) { that.fail(); } should(value).eql(expectedValue); return value; }, function next$onError(err) { if (!that.negate) { that.params.operator += ', but it was rejected with ' + should.format(err); that.fail(); } return err; }); }; /** * Assert given promise will be rejected with some sort of error. Arguments is the same for Assertion#throw. * Result of assertion is still .thenable and should be handled accordingly. * * @name rejectedWith * @memberOf Assertion * @category assertion promises * @returns {Promise} * @example * * function failedPromise() { * return new Promise(function(resolve, reject) { * reject(new Error('boom')) * }) * } * failedPromise().should.be.rejectedWith(Error); * failedPromise().should.be.rejectedWith('boom'); * failedPromise().should.be.rejectedWith(/boom/); * failedPromise().should.be.rejectedWith(Error, { message: 'boom' }); * failedPromise().should.be.rejectedWith({ message: 'boom' }); * * // test example with mocha it is possible to return promise * it('is async', () => { * return failedPromise().should.be.rejectedWith({ message: 'boom' }); * }); */ Assertion.prototype.rejectedWith = function(message, properties) { this.params = {operator: 'to be rejected'}; should(this.obj).be.a.Promise(); var that = this; return this.obj.then(function(value) { if (!that.negate) { that.fail(); } return value; }, function next$onError(err) { if (that.negate) { that.fail(); } var errorMatched = true; var errorInfo = ''; if ('string' === typeof message) { errorMatched = message === err.message; } else if (message instanceof RegExp) { errorMatched = message.test(err.message); } else if ('function' === typeof message) { errorMatched = err instanceof message; } else if (message !== null && typeof message === 'object') { try { should(err).match(message); } catch (e) { if (e instanceof should.AssertionError) { errorInfo = ': ' + e.message; errorMatched = false; } else { throw e; } } } if (!errorMatched) { if ( typeof message === 'string' || message instanceof RegExp) { errorInfo = ' with a message matching ' + should.format(message) + ", but got '" + err.message + "'"; } else if ('function' === typeof message) { errorInfo = ' of type ' + util.functionName(message) + ', but got ' + util.functionName(err.constructor); } } else if ('function' === typeof message && properties) { try { should(err).match(properties); } catch (e) { if (e instanceof should.AssertionError) { errorInfo = ': ' + e.message; errorMatched = false; } else { throw e; } } } that.params.operator += errorInfo; that.assert(errorMatched); return err; }); }; /** * Assert given object is promise and wrap it in PromisedAssertion, which has all properties of Assertion. * That means you can chain as with usual Assertion. * Result of assertion is still .thenable and should be handled accordingly. * * @name finally * @memberOf Assertion * @alias Assertion#eventually * @category assertion promises * @returns {PromisedAssertion} Like Assertion, but .then this.obj in Assertion * @example * * (new Promise(function(resolve, reject) { resolve(10); })) * .should.be.eventually.equal(10); * * // test example with mocha it is possible to return promise * it('is async', () => { * return new Promise(resolve => resolve(10)) * .should.be.finally.equal(10); * }); */ Object.defineProperty(Assertion.prototype, 'finally', { get: function() { should(this.obj).be.a.Promise(); var that = this; return new PromisedAssertion(this.obj.then(function(obj) { var a = should(obj); a.negate = that.negate; a.anyOne = that.anyOne; return a; })); } }); Assertion.alias('finally', 'eventually'); };