Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Bridgewater <ruben@bridgewater.de>2017-12-10 03:54:44 +0300
committerMyles Borins <mylesborins@google.com>2018-10-31 20:45:06 +0300
commitf2af930ebbd1ab4e4b8b929701d110c0c1983832 (patch)
tree929b8fb54059b0579b4b194cabb3d789fc38b170
parent147aeedc8d6d58313778d3bb030aad7858b84d48 (diff)
assert: .throws accept objects
From now on it is possible to use a validation object in throws instead of the other possibilites. Backport-PR-URL: https://github.com/nodejs/node/pull/23223 PR-URL: https://github.com/nodejs/node/pull/17584 Refs: https://github.com/nodejs/node/pull/17557 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ron Korving <ron@ronkorving.nl> Reviewed-By: Yuta Hiroto <hello@about-hiroppy.com>
-rw-r--r--doc/api/assert.md26
-rw-r--r--lib/assert.js43
-rw-r--r--test/parallel/test-assert.js86
3 files changed, 138 insertions, 17 deletions
diff --git a/doc/api/assert.md b/doc/api/assert.md
index b1c7168e94c..dbd32096b5c 100644
--- a/doc/api/assert.md
+++ b/doc/api/assert.md
@@ -635,18 +635,21 @@ If the values are not strictly equal, an `AssertionError` is thrown with a
<!-- YAML
added: v0.1.21
changes:
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/REPLACEME
+ description: The `error` parameter can now be an object as well.
- version: v4.2.0
pr-url: https://github.com/nodejs/node/pull/3276
description: The `error` parameter can now be an arrow function.
-->
* `block` {Function}
-* `error` {RegExp|Function}
+* `error` {RegExp|Function|object}
* `message` {any}
Expects the function `block` to throw an error.
-If specified, `error` can be a constructor, [`RegExp`][], or validation
-function.
+If specified, `error` can be a constructor, [`RegExp`][], a validation
+function, or an object where each property will be tested for.
If specified, `message` will be the message provided by the `AssertionError` if
the block fails to throw.
@@ -689,6 +692,23 @@ assert.throws(
);
```
+Custom error object / error instance:
+
+```js
+assert.throws(
+ () => {
+ const err = new TypeError('Wrong value');
+ err.code = 404;
+ throw err;
+ },
+ {
+ name: 'TypeError',
+ message: 'Wrong value'
+ // Note that only properties on the error object will be tested!
+ }
+);
+```
+
Note that `error` can not be a string. If a string is provided as the second
argument, then `error` is assumed to be omitted and the string will be used for
`message` instead. This can lead to easy-to-miss mistakes. Please read the
diff --git a/lib/assert.js b/lib/assert.js
index b0513c9795b..4fdb4847705 100644
--- a/lib/assert.js
+++ b/lib/assert.js
@@ -25,6 +25,7 @@ const { isSet, isMap, isDate, isRegExp } = process.binding('util');
const { objectToString } = require('internal/util');
const { isArrayBufferView } = require('internal/util/types');
const errors = require('internal/errors');
+const { inspect } = require('util');
// The assert module provides functions that throw
// AssertionError's when particular conditions are not met. The
@@ -660,10 +661,44 @@ assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
}
};
-function expectedException(actual, expected) {
+function createMsg(msg, key, actual, expected) {
+ if (msg)
+ return msg;
+ return `${key}: expected ${inspect(expected[key])}, ` +
+ `not ${inspect(actual[key])}`;
+}
+
+function expectedException(actual, expected, msg) {
if (typeof expected !== 'function') {
- // Should be a RegExp, if not fail hard
- return expected.test(actual);
+ if (expected instanceof RegExp)
+ return expected.test(actual);
+ // assert.doesNotThrow does not accept objects.
+ if (arguments.length === 2) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'expected',
+ ['Function', 'RegExp'], expected);
+ }
+ // The name and message could be non enumerable. Therefore test them
+ // explicitly.
+ if ('name' in expected) {
+ assert.strictEqual(
+ actual.name,
+ expected.name,
+ createMsg(msg, 'name', actual, expected));
+ }
+ if ('message' in expected) {
+ assert.strictEqual(
+ actual.message,
+ expected.message,
+ createMsg(msg, 'message', actual, expected));
+ }
+ const keys = Object.keys(expected);
+ for (const key of keys) {
+ assert.deepStrictEqual(
+ actual[key],
+ expected[key],
+ createMsg(msg, key, actual, expected));
+ }
+ return true;
}
// Guard instanceof against arrow functions as they don't have a prototype.
if (expected.prototype !== undefined && actual instanceof expected) {
@@ -716,7 +751,7 @@ assert.throws = function throws(block, error, message) {
stackStartFn: throws
});
}
- if (error && expectedException(actual, error) === false) {
+ if (error && expectedException(actual, error, message) === false) {
throw actual;
}
};
diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js
index 72606a911e5..ca3cca1fa0d 100644
--- a/test/parallel/test-assert.js
+++ b/test/parallel/test-assert.js
@@ -36,16 +36,6 @@ assert.ok(a.AssertionError.prototype instanceof Error,
assert.throws(makeBlock(a, false), a.AssertionError, 'ok(false)');
-// Using a object as second arg results in a failure
-assert.throws(
- () => { assert.throws(() => { throw new Error(); }, { foo: 'bar' }); },
- common.expectsError({
- type: TypeError,
- message: 'expected.test is not a function'
- })
-);
-
-
assert.doesNotThrow(makeBlock(a, true), a.AssertionError, 'ok(true)');
assert.doesNotThrow(makeBlock(a, 'test', 'ok(\'test\')'));
@@ -742,3 +732,79 @@ common.expectsError(
'Received type string'
}
);
+
+{
+ const errFn = () => {
+ const err = new TypeError('Wrong value');
+ err.code = 404;
+ throw err;
+ };
+ const errObj = {
+ name: 'TypeError',
+ message: 'Wrong value'
+ };
+ assert.throws(errFn, errObj);
+
+ errObj.code = 404;
+ assert.throws(errFn, errObj);
+
+ errObj.code = '404';
+ common.expectsError(
+ // eslint-disable-next-line no-restricted-syntax
+ () => assert.throws(errFn, errObj),
+ {
+ code: 'ERR_ASSERTION',
+ type: assert.AssertionError,
+ message: 'code: expected \'404\', not 404'
+ }
+ );
+
+ errObj.code = 404;
+ errObj.foo = 'bar';
+ common.expectsError(
+ // eslint-disable-next-line no-restricted-syntax
+ () => assert.throws(errFn, errObj),
+ {
+ code: 'ERR_ASSERTION',
+ type: assert.AssertionError,
+ message: 'foo: expected \'bar\', not undefined'
+ }
+ );
+
+ common.expectsError(
+ () => assert.throws(() => { throw new Error(); }, { foo: 'bar' }, 'foobar'),
+ {
+ type: assert.AssertionError,
+ code: 'ERR_ASSERTION',
+ message: 'foobar'
+ }
+ );
+
+ common.expectsError(
+ () => assert.doesNotThrow(() => { throw new Error(); }, { foo: 'bar' }),
+ {
+ type: TypeError,
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: 'The "expected" argument must be one of type Function or ' +
+ 'RegExp. Received type object'
+ }
+ );
+
+ assert.throws(() => { throw new Error('e'); }, new Error('e'));
+ common.expectsError(
+ () => assert.throws(() => { throw new TypeError('e'); }, new Error('e')),
+ {
+ type: assert.AssertionError,
+ code: 'ERR_ASSERTION',
+ message: "name: expected 'Error', not 'TypeError'"
+ }
+ );
+ common.expectsError(
+ () => assert.throws(() => { throw new Error('foo'); }, new Error('')),
+ {
+ type: assert.AssertionError,
+ code: 'ERR_ASSERTION',
+ message: "message: expected '', not 'foo'"
+ }
+ );
+}