Skip to content

Commit

Permalink
Add promises rejection support (#105)
Browse files Browse the repository at this point in the history
* Add promises rejection support

* node v8

* node v8
  • Loading branch information
hueniverse authored and cjihrig committed Sep 16, 2017
1 parent cf692d0 commit 41b4106
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 6 deletions.
2 changes: 0 additions & 2 deletions .travis.yml
@@ -1,8 +1,6 @@
language: node_js

node_js:
- "4"
- "6"
- "8"
- "node"

Expand Down
54 changes: 52 additions & 2 deletions lib/index.js
Expand Up @@ -399,10 +399,60 @@ internals.throw = function (/* type, message */) {
internals.addMethod(['throw', 'throws'], internals.throw);


internals.reject = async function (/* type, message */) {

try {
internals.assert(this, internals.isPromise(this._ref), 'Can only assert reject on promises');

const type = arguments.length && typeof arguments[0] !== 'string' && !(arguments[0] instanceof RegExp) ? arguments[0] : null;
const lastArg = arguments[1] || arguments[0];
const message = typeof lastArg === 'string' || lastArg instanceof RegExp ? lastArg : null;

let thrown;
try {
await this._ref;
}
catch (err) {
thrown = err;
}

internals.assert(this, !this._flags.not || !arguments.length, 'Cannot specify arguments when expecting not to reject');

if (thrown) {
if (type) {
this.assert(thrown instanceof type, 'reject with ' + (type.name || 'provided type'));
}

if (message !== null) {
const error = thrown.message || '';
this.assert(typeof message === 'string' ? error === message : error.match(message), 'reject with an error with specified message', error, message);
}

this.assert(thrown, 'reject with an error', thrown);
}

return this.assert(thrown, 'reject with an error');
}
catch (err) {
return new Promise((resolve, reject) => {

reject(err);
});
}
};

internals.addMethod(['reject', 'rejects'], internals.reject);


internals.isPromise = function (promise) {

return promise && typeof promise.then === 'function';
};


internals.display = function (value) {

const string = value instanceof Error ? `[${value.toString()}]` :
NodeUtil.inspect(value);
const string = value instanceof Error ? `[${value.toString()}]` : (internals.isPromise(value) ? '[Promise]' : NodeUtil.inspect(value));

if (!exports.settings.truncateMessages || string.length <= 40) {
return string;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -10,7 +10,7 @@
"assertion"
],
"engines": {
"node": ">=4.0.0"
"node": ">=8.0.0"
},
"dependencies": {
"hoek": "4.x.x"
Expand Down
228 changes: 227 additions & 1 deletion test/index.js
Expand Up @@ -304,7 +304,7 @@ describe('expect()', () => {
Code.expect('abc').to.not.contain('d').and.to.contain('a');
Code.expect('abc').to.not.contain('d').and.to.not.contain('e');
Code.expect('abc').to.contain('a').and.to.not.contain('d').and.to.contain('c').to.not.contain('f');
Code.expect(() => {}).to.not.throw().and.to.be.a.function();
Code.expect(() => { }).to.not.throw().and.to.be.a.function();
Code.expect(10).to.not.be.about(8, 1).and.to.be.about(9, 1);
Code.expect(10).to.be.about(9, 1).and.to.not.be.about(8, 1);
}
Expand Down Expand Up @@ -2350,6 +2350,232 @@ describe('expect()', () => {
done();
});
});

describe('reject()', () => {

const rejects = function () {

return new Promise((resolve, reject) => {

reject(new Error('kaboom'));
});
};

it('validates rejection', async () => {

let exception = false;
try {
await Code.expect(rejects()).to.reject();
}
catch (err) {
exception = err;
}

Hoek.assert(!exception, exception);
});

it('validates resolution', async () => {

let exception = false;
try {
await Code.expect(new Promise((resolve, reject) => resolve(3))).to.not.reject();
}
catch (err) {
exception = err;
}

Hoek.assert(!exception, exception);
});

it('invalidates rejection', async () => {

let exception = false;
try {
await Code.expect(new Promise((resolve, reject) => resolve(3))).to.reject();
}
catch (err) {
exception = err;
}

Hoek.assert(exception.message === 'Expected [Promise] to reject with an error', exception);
});

it('validates rejection (alias)', async () => {

let exception = false;
try {
await Code.expect(rejects()).rejects();
}
catch (err) {
exception = err;
}

Hoek.assert(!exception, exception);
});

it('invalidates rejection (not a promise)', async () => {

let exception = false;
try {
await Code.expect(() => { }).to.reject();
}
catch (err) {
exception = err;
}

Hoek.assert(exception.message === 'Can only assert reject on promises', exception);
});

it('forbids arguments on negative rejection', async () => {

let exception = false;
try {
await Code.expect(rejects()).to.not.reject('message');
}
catch (err) {
exception = err;
}

Hoek.assert(exception.message === 'Cannot specify arguments when expecting not to reject', exception);
});

it('validates rejection (message)', async () => {

let exception = false;
try {
await Code.expect(rejects()).to.reject('kaboom');
}
catch (err) {
exception = err;
}

Hoek.assert(!exception, exception);
});

it('validates rejection (empty message)', async () => {

let exception = false;
try {
await Code.expect(new Promise((resolve, reject) => reject(new Error('')))).to.reject('');
}
catch (err) {
exception = err;
}

Hoek.assert(!exception, exception);
});

it('validates rejection (message regex)', async () => {

let exception = false;
try {
await Code.expect(rejects()).to.reject(/boom/);
}
catch (err) {
exception = err;
}

Hoek.assert(!exception, exception);
});

it('validates rejection (missing message)', async () => {

const Custom = function () { };

let exception = false;
try {
await Code.expect(new Promise((resolve, reject) => reject(new Custom()))).to.reject('kaboom');
}
catch (err) {
exception = err;
}

Hoek.assert(exception.message === 'Expected [Promise] to reject with an error with specified message', exception);
});

it('invalidates rejection (message)', async () => {

let exception = false;
try {
await Code.expect(rejects()).to.reject('steve');
}
catch (err) {
exception = err;
}

Hoek.assert(exception.message === 'Expected [Promise] to reject with an error with specified message', exception);
});

it('invalidates rejection (empty message)', async () => {

let exception = false;
try {
await Code.expect(rejects()).to.rejects('');
}
catch (err) {
exception = err;
}

Hoek.assert(exception.message === 'Expected [Promise] to reject with an error with specified message', exception);
});

it('validates rejection (type)', async () => {

let exception = false;
try {
await Code.expect(rejects()).to.reject(Error);
}
catch (err) {
exception = err;
}

Hoek.assert(!exception, exception);
});

it('invalidates rejection (known type)', async () => {

const Custom = function () { };

let exception = false;
try {
await Code.expect(new Promise((resolve, reject) => reject(new Custom()))).to.reject(Error);
}
catch (err) {
exception = err;
}

Hoek.assert(exception.message === 'Expected [Promise] to reject with Error', exception);
});

it('invalidates rejection (anonymous type)', async () => {

const Custom = function () { };
delete Custom.name; // Ensure that the type is anonymous

let exception = false;
try {
await Code.expect(rejects()).to.reject(Custom);
}
catch (err) {
exception = err;
}

Hoek.assert(/Expected \[Promise\] to reject with provided type/.test(exception.message), exception);
});

it('validates rejection (type and message)', async () => {

let exception = false;
try {
await Code.expect(rejects()).to.reject(Error, 'kaboom');
}
catch (err) {
exception = err;
}

Hoek.assert(!exception, exception);
});
});
});
});

Expand Down

0 comments on commit 41b4106

Please sign in to comment.