Skip to content

Add promises rejection support #105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
language: node_js

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

Expand Down
54 changes: 52 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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