diff --git a/src/assert.js b/src/assert.js index 2a2c015..0b14ded 100644 --- a/src/assert.js +++ b/src/assert.js @@ -97,6 +97,25 @@ export function throws(blk, exp, msg) { } } +export async function rejects(blk, exp, msg) { + if (!msg && typeof exp === 'string') { + msg = exp; exp = null; + } + + try { + await blk(); + assert(false, false, true, 'rejects', false, 'Expected promise to reject', msg); + } catch (err) { + if (err instanceof Assertion) throw err; + + if (typeof exp === 'function') { + assert(exp(err), false, true, 'rejects', false, 'Expected promise to reject matching exception', msg); + } else if (exp instanceof RegExp) { + assert(exp.test(err.message), false, true, 'rejects', false, `Expected promise to reject matching exception \`${String(exp)}\` pattern`, msg); + } + } +} + // --- export function not(val, msg) { @@ -158,3 +177,21 @@ not.throws = function (blk, exp, msg) { } } } + +not.rejects = async function (blk, exp, msg) { + if (!msg && typeof exp === 'string') { + msg = exp; exp = null; + } + + try { + await blk(); + } catch (err) { + if (typeof exp === 'function') { + assert(!exp(err), true, false, 'not.rejects', false, 'Expected promise not to reject matching exception', msg); + } else if (exp instanceof RegExp) { + assert(!exp.test(err.message), true, false, 'not.rejects', false, `Expected function not to reject promise exception matching \`${String(exp)}\` pattern`, msg); + } else if (!exp) { + assert(false, true, false, 'not.rejects', false, 'Expected promise not to reject', msg); + } + } +} diff --git a/test/assert.js b/test/assert.js index ac0ecac..7a95471 100644 --- a/test/assert.js +++ b/test/assert.js @@ -366,6 +366,7 @@ throws('should be a function', () => { throws('should throw if function does not throw Error :: generic', () => { try { $.throws(() => 123); + assert.unreachable('Function threw when it shouldn’t have'); } catch (err) { assert.is(err.message, 'Expected function to throw'); isError(err, '', false, true, 'throws', false); // no details (true vs false) @@ -375,6 +376,7 @@ throws('should throw if function does not throw Error :: generic', () => { throws('should throw if function does not throw matching Error :: RegExp', () => { try { $.throws(() => { throw new Error('hello') }, /world/); + assert.unreachable('Function threw correct pattern when it should have thrown incorrect one'); } catch (err) { assert.is(err.message, 'Expected function to throw exception matching `/world/` pattern'); isError(err, '', false, true, 'throws', false); // no details @@ -418,6 +420,91 @@ throws.run(); // --- +const rejects = suite('rejects'); + +rejects('should be a function', () => { + assert.type($.rejects, 'function'); +}); + +rejects('should throw if function does not reject Error :: generic', async () => { + try { + await $.rejects(() => { + return new Promise((resolve, reject) => { + setTimeout(() => resolve(), 1); + }); + }); + assert.unreachable('Promise rejected when it shouldn’t have'); + } catch (err) { + assert.is(err.message, 'Expected promise to reject'); + isError(err, '', false, true, 'rejects', false); // no details (true vs false) + } +}); + +rejects('should throw if function does not reject matching Error :: RegExp', async () => { + try { + await $.rejects(() => { + return new Promise((resolve, reject) => { + setTimeout(() => reject('hello'), 1) + }); + }, /world/); + assert.unreachable('Promise rejected with correct pattern when it should have with incorrect one'); + } catch (err) { + assert.is(err.message, 'Expected promise to reject matching exception `/world/` pattern'); + isError(err, '', false, true, 'rejects', false); // no details + } +}); + +rejects('should throw if function does not reject matching Error :: Function', async () => { + try { + await $.rejects( + () => { + return new Promise((resolve, reject) => { + reject(new Error()) + }) + }, + (err) => err.message.includes('foobar') + ); + assert.unreachable('Promise should have rejected without matching exception'); + } catch (err) { + assert.is(err.message, 'Expected promise to reject matching exception'); + isError(err, '', false, true, 'rejects', false); // no details + } +}); + +rejects('should not reject if promise does reject Error :: generic', async () => { + await assert.not.rejects( + async () => await $.rejects(() => { + return new Promise ((resolve, reject) => setTimeout(() => reject(), 1)); + }) + , 'should not reject if function does reject Error :: generic'); +}); + +rejects('should not reject if promise does reject matching Error :: RegExp', async () => { + await assert.not.rejects( + async () => await $.rejects( + () => { + return new Promise((resolve, reject) => setTimeout(() => reject(new Error('hello')), 1)); + }, + /hello/ + ) + ); +}); + +rejects('should not reject if function does reject matching Error :: Function', async () => { + await assert.not.rejects( + async () => await $.rejects( + () => { + return new Promise((resolve, reject) => setTimeout(() => reject(new Error('foobar')), 1)) + }, + (err) => err.message.includes('foobar') + ) + ); +}) + +rejects.run(); + +// --- + const not = suite('not'); not('should be a function', () => { @@ -793,7 +880,7 @@ notMatch.run(); const notThrows = suite('not.throws'); notThrows('should be a function', () => { - assert.type($.throws, 'function'); + assert.type($.not.throws, 'function'); }); notThrows('should not throw if function does not throw Error :: generic', () => { @@ -848,3 +935,85 @@ notThrows('should throw if function does throw matching Error :: Function', () = }); notThrows.run(); + +// --- + +const notRejects = suite('not.throws'); + +notRejects('should be a function', () => { + assert.type($.not.rejects, 'function'); +}); + +notRejects('should not reject if function does not reject Error :: generic', async () => { + await assert.not.rejects( + async () => await $.not.rejects(() => new Promise((resolve, reject) => setTimeout(() => resolve(123), 1))) + ); +}); + +notRejects('should not reject if function does not reject matching Error :: RegExp', async () => { + await assert.not.rejects( + async () => { + await $.not.rejects(() => { + return new Promise((resolve, reject) => { + setTimeout(() => reject(new Error('hello')), 1); + }) + }, /world/); + } + ); +}); + +notRejects('should not reject if function does not reject matching Error :: Function', async () => { + await assert.not.rejects(async () => { + await $.not.rejects(() => { + return new Promise ((resolve, reject) => { + setTimeout(() => reject(new Error('hello')), 1); + }) + }, (err) => err.message.includes('world')); + }); +}); + +notRejects('should reject if function does reject Error :: generic', async () => { + try { + await $.not.rejects(() => { + return new Promise((resolve, reject) => { + setTimeout(() => reject(new Error()), 1); + }); + }); + assert.unreachable('Function resolved when we were expecting it to reject'); + } catch (err) { + assert.is(err.message, 'Expected promise not to reject'); + isError(err, '', true, false, 'not.rejects', false); // no details + } +}); + +notRejects('should reject if function does reject matching Error :: RegExp', async () => { + try { + await $.not.rejects(() => { + return new Promise((resolve, reject) => { + setTimeout(() => reject(new Error('hello')), 1) + }) + }, /hello/); + } catch (err) { + assert.is(err.message, 'Expected function not to reject promise exception matching `/hello/` pattern'); + isError(err, '', true, false, 'not.rejects', false); // no details + } +}); + +notRejects('should reject if function does reject matching Error :: Function', async () => { + try { + await $.not.rejects( + () => { + return new Promise((resolve, reject) => { + setTimeout(() => reject(new Error()), 1) + }) + }, + (err) => err instanceof Error + ); + assert.unreachable('Expected the function to reject with Error instance but it rejected with other value') + } catch (err) { + assert.is(err.message, 'Expected promise not to reject matching exception'); + isError(err, '', true, false, 'not.rejects', false); // no details + } +}); + +notRejects.run();