From 1b2681dd56713c953d4928af15dfd7b38bb526e2 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 26 Jan 2024 16:00:40 -0800 Subject: [PATCH] [Fix] `throws`: fix crash when throwing primitives with a non-empty expected object --- lib/test.js | 34 ++++++++++-------- test/throws.js | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 15 deletions(-) diff --git a/lib/test.js b/lib/test.js index 678d423e..fd651966 100644 --- a/lib/test.js +++ b/lib/test.js @@ -838,22 +838,26 @@ Test.prototype['throws'] = function (fn, expected, msg, extra) { passed = $exec(expected, caught.error) !== null; expected = inspect(expected); } else if (expected && typeof expected === 'object') { // Handle validation objects. - var keys = objectKeys(expected); - // Special handle errors to make sure the name and the message are compared as well. - if (expected instanceof Error) { - $push(keys, 'name', 'message'); - } else if (keys.length === 0) { - throw new TypeError('`throws` validation object must not be empty'); - } - passed = every(keys, function (key) { - if (typeof caught.error[key] === 'string' && isRegExp(expected[key]) && $exec(expected[key], caught.error[key]) !== null) { - return true; - } - if (key in caught.error && deepEqual(caught.error[key], expected[key], { strict: true })) { - return true; + if (caught.error && typeof caught.error === 'object') { + var keys = objectKeys(expected); + // Special handle errors to make sure the name and the message are compared as well. + if (expected instanceof Error) { + $push(keys, 'name', 'message'); + } else if (keys.length === 0) { + throw new TypeError('`throws` validation object must not be empty'); } - return false; - }); + passed = every(keys, function (key) { + if (typeof caught.error[key] === 'string' && isRegExp(expected[key]) && $exec(expected[key], caught.error[key]) !== null) { + return true; + } + if (key in caught.error && deepEqual(caught.error[key], expected[key], { strict: true })) { + return true; + } + return false; + }); + } else { + passed = false; + } } } diff --git a/test/throws.js b/test/throws.js index 0ace2e48..e0366213 100644 --- a/test/throws.js +++ b/test/throws.js @@ -84,15 +84,112 @@ tap.test('failures', function (tt) { 'ok ' + ++count + ' unexpected error', '# throwing primitives', 'ok ' + ++count + ' primitive: null, no expected', + 'not ok ' + ++count + ' primitive: null, with non-empty expected object', + ' ---', + ' operator: throws', + ' expected: |-', + ' { a: \'b\' }', + ' actual: |-', + ' null', + ' at: ($TEST/throws.js:$LINE:$COL)', + ' ...', 'ok ' + ++count + ' primitive: undefined, no expected', + 'not ok ' + ++count + ' primitive: undefined, with non-empty expected object', + ' ---', + ' operator: throws', + ' expected: |-', + ' { a: \'b\' }', + ' actual: |-', + ' undefined', + ' at: ($TEST/throws.js:$LINE:$COL)', + ' stack: |-', + ' Error: primitive: undefined, with non-empty expected object', + ' [... stack stripped ...]', + ' at $TEST/throws.js:$LINE:$COL', + ' [... stack stripped ...]', + ' at Test. ($TEST/throws.js:$LINE:$COL)', + ' [... stack stripped ...]', + ' ...', 'ok ' + ++count + ' primitive: 0, no expected', + 'not ok ' + ++count + ' primitive: 0, with non-empty expected object', + ' ---', + ' operator: throws', + ' expected: |-', + ' { a: \'b\' }', + ' actual: |-', + ' 0', + ' at: ($TEST/throws.js:$LINE:$COL)', + ' ...', 'ok ' + ++count + ' primitive: NaN, no expected', + 'not ok ' + ++count + ' primitive: NaN, with non-empty expected object', + ' ---', + ' operator: throws', + ' expected: |-', + ' { a: \'b\' }', + ' actual: |-', + ' NaN', + ' at: ($TEST/throws.js:$LINE:$COL)', + ' ...', 'ok ' + ++count + ' primitive: 42, no expected', + 'not ok ' + ++count + ' primitive: 42, with non-empty expected object', + ' ---', + ' operator: throws', + ' expected: |-', + ' { a: \'b\' }', + ' actual: |-', + ' 42', + ' at: ($TEST/throws.js:$LINE:$COL)', + ' ...', 'ok ' + ++count + ' primitive: Infinity, no expected', + 'not ok ' + ++count + ' primitive: Infinity, with non-empty expected object', + ' ---', + ' operator: throws', + ' expected: |-', + ' { a: \'b\' }', + ' actual: |-', + ' Infinity', + ' at: ($TEST/throws.js:$LINE:$COL)', + ' ...', 'ok ' + ++count + ' primitive: \'\', no expected', + 'not ok ' + ++count + ' primitive: \'\', with non-empty expected object', + ' ---', + ' operator: throws', + ' expected: |-', + ' { a: \'b\' }', + ' actual: |-', + ' \'\'', + ' at: ($TEST/throws.js:$LINE:$COL)', + ' ...', 'ok ' + ++count + ' primitive: \'foo\', no expected', + 'not ok ' + ++count + ' primitive: \'foo\', with non-empty expected object', + ' ---', + ' operator: throws', + ' expected: |-', + ' { a: \'b\' }', + ' actual: |-', + ' \'foo\'', + ' at: ($TEST/throws.js:$LINE:$COL)', + ' ...', 'ok ' + ++count + ' primitive: true, no expected', + 'not ok ' + ++count + ' primitive: true, with non-empty expected object', + ' ---', + ' operator: throws', + ' expected: |-', + ' { a: \'b\' }', + ' actual: |-', + ' true', + ' at: ($TEST/throws.js:$LINE:$COL)', + ' ...', 'ok ' + ++count + ' primitive: false, no expected', + 'not ok ' + ++count + ' primitive: false, with non-empty expected object', + ' ---', + ' operator: throws', + ' expected: |-', + ' { a: \'b\' }', + ' actual: |-', + ' false', + ' at: ($TEST/throws.js:$LINE:$COL)', + ' ...', '# ambiguous arguments', 'ok ' + ++count + ' Second', 'ok ' + ++count + ' Second', @@ -283,6 +380,7 @@ tap.test('failures', function (tt) { test('throwing primitives', function (t) { [null, undefined, 0, NaN, 42, Infinity, '', 'foo', true, false].forEach(function (primitive) { t.throws(function () { throw primitive; }, 'primitive: ' + inspect(primitive) + ', no expected'); + t.throws(function () { throw primitive; }, { a: 'b' }, 'primitive: ' + inspect(primitive) + ', with non-empty expected object'); }); t.end();