├── .airtap.yml ├── .eslintrc ├── .github ├── FUNDING.yml ├── SECURITY.md └── workflows │ ├── node-aught.yml │ ├── node-pretest.yml │ ├── node-tens.yml │ ├── rebase.yml │ └── require-allow-edits.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assert.js ├── babel.config.js ├── internal ├── assert │ └── assertion_error.js ├── errors.js └── util │ └── comparisons.js ├── package.json ├── test.js └── test ├── common └── index.js ├── parallel ├── test-assert-async.js ├── test-assert-checktag.js ├── test-assert-deep.js ├── test-assert-fail-deprecation.js ├── test-assert-fail.js ├── test-assert-if-error.js ├── test-assert-match.js ├── test-assert-typedarray-deepequal.js └── test-assert.js └── pseudo-tty ├── test-assert-colors.js ├── test-assert-no-color.js └── test-assert-position-indicator.js /.airtap.yml: -------------------------------------------------------------------------------- 1 | browsers: 2 | - name: chrome 3 | version: latest 4 | - name: firefox 5 | version: latest 6 | - name: safari 7 | version: latest 8 | - name: ie 9 | version: latest 10 | - name: microsoftedge 11 | version: latest 12 | sauce_connect: true 13 | loopback: airtap.local 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | 4 | "extends": "@ljharb/eslint-config/node/8", 5 | 6 | "rules": { 7 | "comma-dangle": "warn", 8 | "func-style": "off", 9 | "id-length": "off", 10 | "indent": ["error", 2], 11 | "multiline-comment-style": "off", 12 | "no-unused-vars": "warn", 13 | "sort-keys": "off", 14 | }, 15 | 16 | "ignorePatterns": [ 17 | "build", 18 | ], 19 | 20 | "overrides": [ 21 | { 22 | "files": "test.js", 23 | "rules": { 24 | "global-require": "off", 25 | "max-nested-callbacks": "off", 26 | }, 27 | }, 28 | { 29 | "files": "assert.js", // mostly from node core 30 | "rules": { 31 | "array-bracket-newline": "off", 32 | "array-element-newline": "off", 33 | "arrow-body-style": "off", 34 | "arrow-parens": "off", 35 | "curly": "off", 36 | "eqeqeq": "off", 37 | "func-name-matching": "off", 38 | "function-call-argument-newline": "off", 39 | "function-paren-newline": "off", 40 | "global-require": "off", 41 | "indent": "off", 42 | "max-lines": "off", 43 | "max-params": "off", 44 | "new-cap": "off", 45 | "no-mixed-operators": "off", 46 | "no-multi-assign": "off", 47 | "no-negated-condition": "off", 48 | "no-param-reassign": "off", 49 | "operator-linebreak": ["error", "after"], 50 | "no-prototype-builtins": "off", 51 | "no-restricted-syntax": "off", 52 | "no-use-before-define": "off", 53 | "no-var": "off", 54 | "nonblock-statement-body-position": "off", 55 | "object-curly-newline": "off", 56 | "prefer-destructuring": "off", 57 | "prefer-template": "off", 58 | "sort-keys": "off", 59 | }, 60 | }, 61 | { 62 | "files": "internal/**", 63 | "rules": { 64 | "arrow-parens": "off", 65 | "camelcase": "off", 66 | "complexity": "off", 67 | "curly": "off", 68 | "default-case": "off", 69 | "eqeqeq": "off", 70 | "function-call-argument-newline": "off", 71 | "function-paren-newline": "off", 72 | "global-require": "off", 73 | "indent": "off", 74 | "max-depth": "off", 75 | "max-lines-per-function": "off", 76 | "max-lines": "off", 77 | "max-params": "off", 78 | "max-statements": "off", 79 | "no-else-return": "off", 80 | "no-extra-parens": "off", 81 | "no-implicit-coercion": "off", 82 | "no-mixed-operators": "off", 83 | "no-negated-condition": "off", 84 | "no-param-reassign": "off", 85 | "no-plusplus": "off", 86 | "no-restricted-syntax": "off", 87 | "no-shadow": "off", 88 | "no-unused-expressions": "off", 89 | "no-use-before-define": "off", 90 | "no-var": "off", 91 | "nonblock-statement-body-position": "off", 92 | "object-curly-newline": "off", 93 | "operator-linebreak": "off", 94 | "prefer-template": "off", 95 | "semi": "off", 96 | "space-before-function-paren": "off", 97 | "wrap-regex": "off", 98 | }, 99 | }, 100 | { 101 | "files": "test/**", // test files from node core 102 | "rules": { 103 | "array-bracket-spacing": "off", 104 | "arrow-parens": "off", 105 | "comma-dangle": "off", 106 | "consistent-return": "off", 107 | "curly": "off", 108 | "default-param-last": "off", 109 | "eqeqeq": "off", 110 | "function-call-argument-newline": "off", 111 | "function-paren-newline": "off", 112 | "global-require": "off", 113 | "indent": "off", 114 | "keyword-spacing": "off", 115 | "lines-around-directive": "off", 116 | "max-lines-per-function": "off", 117 | "new-cap": "off", 118 | "no-else-return": "off", 119 | "no-eval": "off", 120 | "operator-linebreak": ["error", "after"], 121 | "no-extra-parens": "off", 122 | "no-inner-declarations": "off", 123 | "no-invalid-this": "off", 124 | "no-lone-blocks": "off", 125 | "no-multi-spaces": "off", 126 | "no-multiple-empty-lines": "off", 127 | "no-new-func": "off", 128 | "no-new-wrappers": "off", 129 | "no-param-reassign": "off", 130 | "no-plusplus": "off", 131 | "no-prototype-builtins": "off", 132 | "no-shadow": "off", 133 | "no-sparse-arrays": "off", 134 | "no-undef": "off", 135 | "no-underscore-dangle": "off", 136 | "no-use-before-define": "off", 137 | "no-var": "off", 138 | "nonblock-statement-body-position": "off", 139 | "object-curly-spacing": "off", 140 | "object-shorthand": "off", 141 | "prefer-arrow-callback": "off", 142 | "prefer-destructuring": "off", 143 | "prefer-promise-reject-errors": "off", 144 | "prefer-rest-params": "off", 145 | "prefer-template": "off", 146 | "quote-props": "off", 147 | "space-before-function-paren": "off", 148 | "strict": "off", 149 | "wrap-iife": "off", 150 | "wrap-regex": "off", 151 | }, 152 | }, 153 | ], 154 | } 155 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ljharb] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: npm/assert 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | Only the latest major version is supported at any given time. 5 | 6 | ## Reporting a Vulnerability 7 | 8 | To report a security vulnerability, please use the 9 | [Tidelift security contact](https://tidelift.com/security). 10 | Tidelift will coordinate the fix and disclosure. 11 | -------------------------------------------------------------------------------- /.github/workflows/node-aught.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: node.js 6 - 10' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | tests: 7 | uses: ljharb/actions/.github/workflows/node-majors.yml@main 8 | with: 9 | range: '>= 6 < 10' 10 | command: npm run tests-only 11 | 12 | node: 13 | name: 'node 6 - 10' 14 | needs: [tests] 15 | runs-on: ubuntu-latest 16 | steps: 17 | - run: 'echo tests completed' 18 | -------------------------------------------------------------------------------- /.github/workflows/node-pretest.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: pretest/posttest' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | tests: 7 | uses: ljharb/actions/.github/workflows/pretest.yml@main 8 | -------------------------------------------------------------------------------- /.github/workflows/node-tens.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: node.js >= 10' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | tests: 7 | uses: ljharb/actions/.github/workflows/node-majors.yml@main 8 | with: 9 | range: '>= 10' 10 | command: npm run tests-only 11 | 12 | node: 13 | name: 'node >= 10' 14 | needs: [tests] 15 | runs-on: ubuntu-latest 16 | steps: 17 | - run: 'echo tests completed' 18 | -------------------------------------------------------------------------------- /.github/workflows/rebase.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Rebase 2 | 3 | on: [pull_request_target] 4 | 5 | jobs: 6 | _: 7 | name: "Automatic Rebase" 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: ljharb/rebase@master 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/require-allow-edits.yml: -------------------------------------------------------------------------------- 1 | name: Require “Allow Edits” 2 | 3 | on: [pull_request_target] 4 | 5 | jobs: 6 | _: 7 | name: "Require “Allow Edits”" 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: ljharb/require-allow-edits@main 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | 3 | node_modules 4 | npm-debug.log 5 | package-lock.json 6 | yarn.lock 7 | *.swp 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'stable' 4 | script: 5 | # Run browser tests on one node version. 6 | - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_NODE_VERSION}" = "stable" ]; then npm run test:browsers; fi' 7 | addons: 8 | sauce_connect: true 9 | hosts: 10 | - airtap.local 11 | env: 12 | global: 13 | - secure: qThuKBZQtkooAvzaYldECGNqvKGPRTnXx62IVyhSbFlsCY1VCmjhLldhyPDiZQ3JqL1XvSkK8OMDupiHqZnNE0nGijoO4M/kaEdjBB+jpjg3f8I6te2SNU935SbkfY9KHAaFXMZwdcq7Fk932AxWEu+FMSDM+080wNKpEATXDe4= 14 | - secure: O/scKjHLRcPN5ILV5qsSkksQ7qcZQdHWEUUPItmj/4+vmCc28bHpicoUxXG5A96iHvkBbdmky/nGCg464ZaNLk68m6hfEMDAR3J6mhM2Pf5C4QI/LlFlR1fob9sQ8lztwSGOItwdK8Rfrgb30RRVV71f6FxnaJ6PKMuMNT5S1AQ= 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # assert change log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | This project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## 2.1.0 8 | 9 | * [New] Implement `assert.match()` and `assert.doesNotMatch()` 10 | * [Refactor] switching to a maintained Object.assign package 11 | * [readme] Add description for usage with webpack and vite (#60) 12 | * [readme] Remove duplicate line under usage section (#48) 13 | * [Deps] update `is-nan`, `object-is`, `util` 14 | * [Dev Deps] update `@babel/cli`, `@babel/core`, `@babel/preset-env`, `airtap`, `core-js`, `cross-eng`, `object.entries`, `object.getownpropertydescriptors`, `tape` 15 | 16 | ## 2.0.0 17 | 18 | * Sync with Node.js master. ([@lukechilds](https://github.com/lukechilds) in [#44](https://github.com/browserify/commonjs-assert/pull/44)) 19 | 20 | **Note:** Support for IE9 and IE10 has been dropped. IE11 is still supported. 21 | 22 | ## 1.5.1 23 | * [Deps] switch to `object.assign`, and unpin `util` 24 | 25 | ## 1.5.0 26 | * Add strict mode APIs. ([@lukechilds](https://github.com/lukechilds) in [#41](https://github.com/browserify/commonjs-assert/pull/41)) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | Permission is hereby granted, free of charge, to any person obtaining a copy 3 | of this software and associated documentation files (the "Software"), to 4 | deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 6 | sell copies of the Software, and to permit persons to whom the Software is 7 | furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in 10 | all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 17 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 18 | IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # assert 2 | 3 | > The [`assert`](https://nodejs.org/api/assert.html) module from Node.js, for the browser. 4 | 5 | [![Build Status](https://travis-ci.org/browserify/commonjs-assert.svg?branch=master)](https://travis-ci.org/browserify/commonjs-assert) 6 | [![npm](https://img.shields.io/npm/dm/assert.svg)](https://www.npmjs.com/package/assert) 7 | [![npm](https://img.shields.io/npm/v/assert.svg)](https://www.npmjs.com/package/assert) 8 | 9 | With browserify, simply `require('assert')` or use the `assert` global and you will get this module. 10 | 11 | The goal is to provide an API that is as functionally identical to the [Node.js `assert` API](https://nodejs.org/api/assert.html) as possible. Read the [official docs](https://nodejs.org/api/assert.html) for API documentation. 12 | 13 | ## Install 14 | 15 | To use this module directly (without browserify), install it as a dependency: 16 | 17 | ``` 18 | npm install assert 19 | ``` 20 | 21 | ## Inconsistencies with Node.js `assert` 22 | 23 | Due to differences between browsers, some error properties such as `message` and `stack` will be inconsistent. However the assertion behaviour is as close as possible to Node.js and the same error `code` will always be used. 24 | 25 | ## Usage with bundlers that don't automatically include polyfills for Node.js APIs 26 | 27 | Bundlers like `webpack 5` and `Vite.js` (and possibly others) don't automatically include polyfills for Node.js APIs. Like most packages on npm, this module depends on other Node.js APIs, so it won't work with these bundlers without also including a polyfill for the `process` Node.js global. You can use [this library](https://github.com/defunctzombie/node-process) to polyfill the `process` global. 28 | 29 | Note that this is not a flaw in this package - this package will work without any manual configuration with `browserify` and `webpack 4` and other working bundlers. Unfortunately, some bundlers decided to require an explicit allow-list of all Node.js API specific packages instead of having them work out of the box, hence the incompatibility. See https://github.com/browserify/commonjs-assert/issues/55 for some more context. 30 | 31 | ## Contributing 32 | 33 | To contribute, work on the source files. Then build and run the tests against the built files. Be careful to not introduce syntax that will be transpiled down to unsupported syntax. For example, `for...of` loops will be transpiled to use `Symbol.iterator` which is unavailable in IE. 34 | 35 | ### Build scripts 36 | 37 | #### `npm run build` 38 | 39 | Builds the project into the `build` dir. 40 | 41 | #### `npm run dev` 42 | 43 | Watches source files for changes and rebuilds them into the `build` dir. 44 | 45 | #### `npm run test` 46 | 47 | Builds the source files into the `build` dir and then runs the tests against the built project. 48 | 49 | #### `npm run test:nobuild` 50 | 51 | Runs the tests against the built project without rebuilding first. 52 | 53 | This is useful if you're debugging in the transpiled code and want to re-run the tests without overwriting any changes you may have made. 54 | 55 | #### `npm run test:source` 56 | 57 | Runs the tests against the unbuilt source files. 58 | 59 | This will only work on modern Node.js versions. 60 | 61 | #### `npm run test:browsers` 62 | 63 | Run browser tests against the all targets in the cloud. 64 | 65 | Requires airtap credentials to be configured on your machine. 66 | 67 | #### `npm run test:browsers:local` 68 | 69 | Run a local browser test server. No airtap configuration required. 70 | 71 | When paired with `npm run dev` any changes you make to the source files will be automatically transpiled and served on the next request to the test server. 72 | 73 | ## License 74 | 75 | MIT © Joyent, Inc. and other Node contributors 76 | -------------------------------------------------------------------------------- /assert.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js lib/assert.js 2 | // https://github.com/nodejs/node/commit/2a51ae424a513ec9a6aa3466baa0cc1d55dd4f3b 3 | 4 | // Originally from narwhal.js (http://narwhaljs.org) 5 | // Copyright (c) 2009 Thomas Robinson <280north.com> 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the 'Software'), to 9 | // deal in the Software without restriction, including without limitation the 10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | // sell copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | 'use strict'; 25 | 26 | const { codes: { 27 | ERR_AMBIGUOUS_ARGUMENT, 28 | ERR_INVALID_ARG_TYPE, 29 | ERR_INVALID_ARG_VALUE, 30 | ERR_INVALID_RETURN_VALUE, 31 | ERR_MISSING_ARGS 32 | } } = require('./internal/errors'); 33 | const AssertionError = require('./internal/assert/assertion_error'); 34 | const { inspect } = require('util/'); 35 | const { isPromise, isRegExp } = require('util/').types; 36 | 37 | const objectAssign = require('object.assign/polyfill')(); 38 | const objectIs = require('object-is/polyfill')(); 39 | 40 | const RegExpPrototypeTest = require('call-bind/callBound')('RegExp.prototype.test'); 41 | 42 | const errorCache = new Map(); 43 | 44 | let isDeepEqual; 45 | let isDeepStrictEqual; 46 | let parseExpressionAt; 47 | let findNodeAround; 48 | let decoder; 49 | 50 | function lazyLoadComparison() { 51 | const comparison = require('./internal/util/comparisons'); 52 | isDeepEqual = comparison.isDeepEqual; 53 | isDeepStrictEqual = comparison.isDeepStrictEqual; 54 | } 55 | 56 | // Escape control characters but not \n and \t to keep the line breaks and 57 | // indentation intact. 58 | // eslint-disable-next-line no-control-regex 59 | const escapeSequencesRegExp = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g; 60 | const meta = [ 61 | '\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004', 62 | '\\u0005', '\\u0006', '\\u0007', '\\b', '', 63 | '', '\\u000b', '\\f', '', '\\u000e', 64 | '\\u000f', '\\u0010', '\\u0011', '\\u0012', '\\u0013', 65 | '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018', 66 | '\\u0019', '\\u001a', '\\u001b', '\\u001c', '\\u001d', 67 | '\\u001e', '\\u001f' 68 | ]; 69 | 70 | const escapeFn = (str) => meta[str.charCodeAt(0)]; 71 | 72 | let warned = false; 73 | 74 | // The assert module provides functions that throw 75 | // AssertionError's when particular conditions are not met. The 76 | // assert module must conform to the following interface. 77 | 78 | const assert = module.exports = ok; 79 | 80 | const NO_EXCEPTION_SENTINEL = {}; 81 | 82 | // All of the following functions must throw an AssertionError 83 | // when a corresponding condition is not met, with a message that 84 | // may be undefined if not provided. All assertion methods provide 85 | // both the actual and expected values to the assertion error for 86 | // display purposes. 87 | 88 | function innerFail(obj) { 89 | if (obj.message instanceof Error) throw obj.message; 90 | 91 | throw new AssertionError(obj); 92 | } 93 | 94 | function fail(actual, expected, message, operator, stackStartFn) { 95 | const argsLen = arguments.length; 96 | 97 | let internalMessage; 98 | if (argsLen === 0) { 99 | internalMessage = 'Failed'; 100 | } else if (argsLen === 1) { 101 | message = actual; 102 | actual = undefined; 103 | } else { 104 | if (warned === false) { 105 | warned = true; 106 | const warn = process.emitWarning ? process.emitWarning : console.warn.bind(console); 107 | warn( 108 | 'assert.fail() with more than one argument is deprecated. ' + 109 | 'Please use assert.strictEqual() instead or only pass a message.', 110 | 'DeprecationWarning', 111 | 'DEP0094' 112 | ); 113 | } 114 | if (argsLen === 2) 115 | operator = '!='; 116 | } 117 | 118 | if (message instanceof Error) throw message; 119 | 120 | const errArgs = { 121 | actual, 122 | expected, 123 | operator: operator === undefined ? 'fail' : operator, 124 | stackStartFn: stackStartFn || fail 125 | }; 126 | if (message !== undefined) { 127 | errArgs.message = message; 128 | } 129 | const err = new AssertionError(errArgs); 130 | if (internalMessage) { 131 | err.message = internalMessage; 132 | err.generatedMessage = true; 133 | } 134 | throw err; 135 | } 136 | 137 | assert.fail = fail; 138 | 139 | // The AssertionError is defined in internal/error. 140 | assert.AssertionError = AssertionError; 141 | 142 | function innerOk(fn, argLen, value, message) { 143 | if (!value) { 144 | let generatedMessage = false; 145 | 146 | if (argLen === 0) { 147 | generatedMessage = true; 148 | message = 'No value argument passed to `assert.ok()`'; 149 | } else if (message instanceof Error) { 150 | throw message; 151 | } 152 | 153 | const err = new AssertionError({ 154 | actual: value, 155 | expected: true, 156 | message, 157 | operator: '==', 158 | stackStartFn: fn 159 | }); 160 | err.generatedMessage = generatedMessage; 161 | throw err; 162 | } 163 | } 164 | 165 | // Pure assertion tests whether a value is truthy, as determined 166 | // by !!value. 167 | function ok(...args) { 168 | innerOk(ok, args.length, ...args); 169 | } 170 | assert.ok = ok; 171 | 172 | // The equality assertion tests shallow, coercive equality with ==. 173 | /* eslint-disable no-restricted-properties */ 174 | assert.equal = function equal(actual, expected, message) { 175 | if (arguments.length < 2) { 176 | throw new ERR_MISSING_ARGS('actual', 'expected'); 177 | } 178 | // eslint-disable-next-line eqeqeq 179 | if (actual != expected) { 180 | innerFail({ 181 | actual, 182 | expected, 183 | message, 184 | operator: '==', 185 | stackStartFn: equal 186 | }); 187 | } 188 | }; 189 | 190 | // The non-equality assertion tests for whether two objects are not 191 | // equal with !=. 192 | assert.notEqual = function notEqual(actual, expected, message) { 193 | if (arguments.length < 2) { 194 | throw new ERR_MISSING_ARGS('actual', 'expected'); 195 | } 196 | // eslint-disable-next-line eqeqeq 197 | if (actual == expected) { 198 | innerFail({ 199 | actual, 200 | expected, 201 | message, 202 | operator: '!=', 203 | stackStartFn: notEqual 204 | }); 205 | } 206 | }; 207 | 208 | // The equivalence assertion tests a deep equality relation. 209 | assert.deepEqual = function deepEqual(actual, expected, message) { 210 | if (arguments.length < 2) { 211 | throw new ERR_MISSING_ARGS('actual', 'expected'); 212 | } 213 | if (isDeepEqual === undefined) lazyLoadComparison(); 214 | if (!isDeepEqual(actual, expected)) { 215 | innerFail({ 216 | actual, 217 | expected, 218 | message, 219 | operator: 'deepEqual', 220 | stackStartFn: deepEqual 221 | }); 222 | } 223 | }; 224 | 225 | // The non-equivalence assertion tests for any deep inequality. 226 | assert.notDeepEqual = function notDeepEqual(actual, expected, message) { 227 | if (arguments.length < 2) { 228 | throw new ERR_MISSING_ARGS('actual', 'expected'); 229 | } 230 | if (isDeepEqual === undefined) lazyLoadComparison(); 231 | if (isDeepEqual(actual, expected)) { 232 | innerFail({ 233 | actual, 234 | expected, 235 | message, 236 | operator: 'notDeepEqual', 237 | stackStartFn: notDeepEqual 238 | }); 239 | } 240 | }; 241 | /* eslint-enable */ 242 | 243 | assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { 244 | if (arguments.length < 2) { 245 | throw new ERR_MISSING_ARGS('actual', 'expected'); 246 | } 247 | if (isDeepEqual === undefined) lazyLoadComparison(); 248 | if (!isDeepStrictEqual(actual, expected)) { 249 | innerFail({ 250 | actual, 251 | expected, 252 | message, 253 | operator: 'deepStrictEqual', 254 | stackStartFn: deepStrictEqual 255 | }); 256 | } 257 | }; 258 | 259 | assert.notDeepStrictEqual = notDeepStrictEqual; 260 | function notDeepStrictEqual(actual, expected, message) { 261 | if (arguments.length < 2) { 262 | throw new ERR_MISSING_ARGS('actual', 'expected'); 263 | } 264 | if (isDeepEqual === undefined) lazyLoadComparison(); 265 | if (isDeepStrictEqual(actual, expected)) { 266 | innerFail({ 267 | actual, 268 | expected, 269 | message, 270 | operator: 'notDeepStrictEqual', 271 | stackStartFn: notDeepStrictEqual 272 | }); 273 | } 274 | } 275 | 276 | assert.strictEqual = function strictEqual(actual, expected, message) { 277 | if (arguments.length < 2) { 278 | throw new ERR_MISSING_ARGS('actual', 'expected'); 279 | } 280 | if (!objectIs(actual, expected)) { 281 | innerFail({ 282 | actual, 283 | expected, 284 | message, 285 | operator: 'strictEqual', 286 | stackStartFn: strictEqual 287 | }); 288 | } 289 | }; 290 | 291 | assert.notStrictEqual = function notStrictEqual(actual, expected, message) { 292 | if (arguments.length < 2) { 293 | throw new ERR_MISSING_ARGS('actual', 'expected'); 294 | } 295 | if (objectIs(actual, expected)) { 296 | innerFail({ 297 | actual, 298 | expected, 299 | message, 300 | operator: 'notStrictEqual', 301 | stackStartFn: notStrictEqual 302 | }); 303 | } 304 | }; 305 | 306 | class Comparison { 307 | constructor(obj, keys, actual) { 308 | keys.forEach(key => { 309 | if (key in obj) { 310 | if (actual !== undefined && 311 | typeof actual[key] === 'string' && 312 | isRegExp(obj[key]) && 313 | RegExpPrototypeTest(obj[key], actual[key]) 314 | ) { 315 | this[key] = actual[key]; 316 | } else { 317 | this[key] = obj[key]; 318 | } 319 | } 320 | }); 321 | } 322 | } 323 | 324 | function compareExceptionKey(actual, expected, key, message, keys, fn) { 325 | if (!(key in actual) || !isDeepStrictEqual(actual[key], expected[key])) { 326 | if (!message) { 327 | // Create placeholder objects to create a nice output. 328 | const a = new Comparison(actual, keys); 329 | const b = new Comparison(expected, keys, actual); 330 | 331 | const err = new AssertionError({ 332 | actual: a, 333 | expected: b, 334 | operator: 'deepStrictEqual', 335 | stackStartFn: fn 336 | }); 337 | err.actual = actual; 338 | err.expected = expected; 339 | err.operator = fn.name; 340 | throw err; 341 | } 342 | innerFail({ 343 | actual, 344 | expected, 345 | message, 346 | operator: fn.name, 347 | stackStartFn: fn 348 | }); 349 | } 350 | } 351 | 352 | function expectedException(actual, expected, msg, fn) { 353 | if (typeof expected !== 'function') { 354 | if (isRegExp(expected)) 355 | return RegExpPrototypeTest(expected, actual); 356 | // assert.doesNotThrow does not accept objects. 357 | if (arguments.length === 2) { 358 | throw new ERR_INVALID_ARG_TYPE( 359 | 'expected', ['Function', 'RegExp'], expected 360 | ); 361 | } 362 | 363 | // Handle primitives properly. 364 | if (typeof actual !== 'object' || actual === null) { 365 | const err = new AssertionError({ 366 | actual, 367 | expected, 368 | message: msg, 369 | operator: 'deepStrictEqual', 370 | stackStartFn: fn 371 | }); 372 | err.operator = fn.name; 373 | throw err; 374 | } 375 | 376 | const keys = Object.keys(expected); 377 | // Special handle errors to make sure the name and the message are compared 378 | // as well. 379 | if (expected instanceof Error) { 380 | keys.push('name', 'message'); 381 | } else if (keys.length === 0) { 382 | throw new ERR_INVALID_ARG_VALUE('error', 383 | expected, 'may not be an empty object'); 384 | } 385 | if (isDeepEqual === undefined) lazyLoadComparison(); 386 | keys.forEach(key => { 387 | if ( 388 | typeof actual[key] === 'string' && 389 | isRegExp(expected[key]) && 390 | RegExpPrototypeTest(expected[key], actual[key]) 391 | ) { 392 | return; 393 | } 394 | compareExceptionKey(actual, expected, key, msg, keys, fn); 395 | }); 396 | return true; 397 | } 398 | // Guard instanceof against arrow functions as they don't have a prototype. 399 | if (expected.prototype !== undefined && actual instanceof expected) { 400 | return true; 401 | } 402 | if (Error.isPrototypeOf(expected)) { 403 | return false; 404 | } 405 | return expected.call({}, actual) === true; 406 | } 407 | 408 | function getActual(fn) { 409 | if (typeof fn !== 'function') { 410 | throw new ERR_INVALID_ARG_TYPE('fn', 'Function', fn); 411 | } 412 | try { 413 | fn(); 414 | } catch (e) { 415 | return e; 416 | } 417 | return NO_EXCEPTION_SENTINEL; 418 | } 419 | 420 | function checkIsPromise(obj) { 421 | // Accept native ES6 promises and promises that are implemented in a similar 422 | // way. Do not accept thenables that use a function as `obj` and that have no 423 | // `catch` handler. 424 | 425 | // TODO: thenables are checked up until they have the correct methods, 426 | // but according to documentation, the `then` method should receive 427 | // the `fulfill` and `reject` arguments as well or it may be never resolved. 428 | 429 | return isPromise(obj) || 430 | obj !== null && typeof obj === 'object' && 431 | typeof obj.then === 'function' && 432 | typeof obj.catch === 'function'; 433 | } 434 | 435 | function waitForActual(promiseFn) { 436 | return Promise.resolve().then(() => { 437 | let resultPromise; 438 | if (typeof promiseFn === 'function') { 439 | // Return a rejected promise if `promiseFn` throws synchronously. 440 | resultPromise = promiseFn(); 441 | // Fail in case no promise is returned. 442 | if (!checkIsPromise(resultPromise)) { 443 | throw new ERR_INVALID_RETURN_VALUE('instance of Promise', 444 | 'promiseFn', resultPromise); 445 | } 446 | } else if (checkIsPromise(promiseFn)) { 447 | resultPromise = promiseFn; 448 | } else { 449 | throw new ERR_INVALID_ARG_TYPE('promiseFn', ['Function', 'Promise'], promiseFn); 450 | } 451 | 452 | return Promise.resolve().then(() => resultPromise) 453 | .then(() => NO_EXCEPTION_SENTINEL) 454 | .catch(e => e); 455 | }); 456 | } 457 | 458 | function expectsError(stackStartFn, actual, error, message) { 459 | if (typeof error === 'string') { 460 | if (arguments.length === 4) { 461 | throw new ERR_INVALID_ARG_TYPE('error', 462 | ['Object', 'Error', 'Function', 'RegExp'], 463 | error); 464 | } 465 | if (typeof actual === 'object' && actual !== null) { 466 | if (actual.message === error) { 467 | throw new ERR_AMBIGUOUS_ARGUMENT( 468 | 'error/message', 469 | `The error message "${actual.message}" is identical to the message.` 470 | ); 471 | } 472 | } else if (actual === error) { 473 | throw new ERR_AMBIGUOUS_ARGUMENT( 474 | 'error/message', 475 | `The error "${actual}" is identical to the message.` 476 | ); 477 | } 478 | message = error; 479 | error = undefined; 480 | } else if (error != null && 481 | typeof error !== 'object' && 482 | typeof error !== 'function') { 483 | throw new ERR_INVALID_ARG_TYPE('error', 484 | ['Object', 'Error', 'Function', 'RegExp'], 485 | error); 486 | } 487 | 488 | if (actual === NO_EXCEPTION_SENTINEL) { 489 | let details = ''; 490 | if (error && error.name) { 491 | details += ` (${error.name})`; 492 | } 493 | details += message ? `: ${message}` : '.'; 494 | const fnType = stackStartFn.name === 'rejects' ? 'rejection' : 'exception'; 495 | innerFail({ 496 | actual: undefined, 497 | expected: error, 498 | operator: stackStartFn.name, 499 | message: `Missing expected ${fnType}${details}`, 500 | stackStartFn 501 | }); 502 | } 503 | if (error && !expectedException(actual, error, message, stackStartFn)) { 504 | throw actual; 505 | } 506 | } 507 | 508 | function expectsNoError(stackStartFn, actual, error, message) { 509 | if (actual === NO_EXCEPTION_SENTINEL) 510 | return; 511 | 512 | if (typeof error === 'string') { 513 | message = error; 514 | error = undefined; 515 | } 516 | 517 | if (!error || expectedException(actual, error)) { 518 | const details = message ? `: ${message}` : '.'; 519 | const fnType = stackStartFn.name === 'doesNotReject' ? 520 | 'rejection' : 'exception'; 521 | innerFail({ 522 | actual, 523 | expected: error, 524 | operator: stackStartFn.name, 525 | message: `Got unwanted ${fnType}${details}\n` + 526 | `Actual message: "${actual && actual.message}"`, 527 | stackStartFn 528 | }); 529 | } 530 | throw actual; 531 | } 532 | 533 | assert.throws = function throws(promiseFn, ...args) { 534 | expectsError(throws, getActual(promiseFn), ...args); 535 | }; 536 | 537 | assert.rejects = function rejects(promiseFn, ...args) { 538 | return waitForActual(promiseFn).then(result => { 539 | return expectsError(rejects, result, ...args); 540 | }); 541 | }; 542 | 543 | assert.doesNotThrow = function doesNotThrow(fn, ...args) { 544 | expectsNoError(doesNotThrow, getActual(fn), ...args); 545 | }; 546 | 547 | assert.doesNotReject = function doesNotReject(fn, ...args) { 548 | return waitForActual(fn).then(result => { 549 | return expectsNoError(doesNotReject, result, ...args); 550 | }); 551 | }; 552 | 553 | assert.ifError = function ifError(err) { 554 | if (err !== null && err !== undefined) { 555 | let message = 'ifError got unwanted exception: '; 556 | if (typeof err === 'object' && typeof err.message === 'string') { 557 | if (err.message.length === 0 && err.constructor) { 558 | message += err.constructor.name; 559 | } else { 560 | message += err.message; 561 | } 562 | } else { 563 | message += inspect(err); 564 | } 565 | 566 | const newErr = new AssertionError({ 567 | actual: err, 568 | expected: null, 569 | operator: 'ifError', 570 | message, 571 | stackStartFn: ifError 572 | }); 573 | 574 | // Make sure we actually have a stack trace! 575 | const origStack = err.stack; 576 | 577 | if (typeof origStack === 'string') { 578 | // This will remove any duplicated frames from the error frames taken 579 | // from within `ifError` and add the original error frames to the newly 580 | // created ones. 581 | const tmp2 = origStack.split('\n'); 582 | tmp2.shift(); 583 | // Filter all frames existing in err.stack. 584 | let tmp1 = newErr.stack.split('\n'); 585 | for (var i = 0; i < tmp2.length; i++) { 586 | // Find the first occurrence of the frame. 587 | const pos = tmp1.indexOf(tmp2[i]); 588 | if (pos !== -1) { 589 | // Only keep new frames. 590 | tmp1 = tmp1.slice(0, pos); 591 | break; 592 | } 593 | } 594 | newErr.stack = `${tmp1.join('\n')}\n${tmp2.join('\n')}`; 595 | } 596 | 597 | throw newErr; 598 | } 599 | }; 600 | 601 | // Currently in sync with Node.js lib/assert.js 602 | // https://github.com/nodejs/node/commit/2a871df3dfb8ea663ef5e1f8f62701ec51384ecb 603 | function internalMatch(string, regexp, message, fn, fnName) { 604 | if (!isRegExp(regexp)) { 605 | throw new ERR_INVALID_ARG_TYPE( 606 | 'regexp', 'RegExp', regexp 607 | ); 608 | } 609 | const match = fnName === 'match'; 610 | if (typeof string !== 'string' || 611 | RegExpPrototypeTest(regexp, string) !== match) { 612 | if (message instanceof Error) { 613 | throw message; 614 | } 615 | 616 | const generatedMessage = !message; 617 | 618 | // 'The input was expected to not match the regular expression ' + 619 | message = message || (typeof string !== 'string' ? 620 | 'The "string" argument must be of type string. Received type ' + 621 | `${typeof string} (${inspect(string)})` : 622 | (match ? 623 | 'The input did not match the regular expression ' : 624 | 'The input was expected to not match the regular expression ') + 625 | `${inspect(regexp)}. Input:\n\n${inspect(string)}\n`); 626 | const err = new AssertionError({ 627 | actual: string, 628 | expected: regexp, 629 | message, 630 | operator: fnName, 631 | stackStartFn: fn 632 | }); 633 | err.generatedMessage = generatedMessage; 634 | throw err; 635 | } 636 | } 637 | 638 | assert.match = function match(string, regexp, message) { 639 | internalMatch(string, regexp, message, match, 'match'); 640 | }; 641 | 642 | assert.doesNotMatch = function doesNotMatch(string, regexp, message) { 643 | internalMatch(string, regexp, message, doesNotMatch, 'doesNotMatch'); 644 | }; 645 | 646 | // Expose a strict only variant of assert 647 | function strict(...args) { 648 | innerOk(strict, args.length, ...args); 649 | } 650 | assert.strict = objectAssign(strict, assert, { 651 | equal: assert.strictEqual, 652 | deepEqual: assert.deepStrictEqual, 653 | notEqual: assert.notStrictEqual, 654 | notDeepEqual: assert.notDeepStrictEqual 655 | }); 656 | assert.strict.strict = assert.strict; 657 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const presets = [ 4 | [ 5 | '@babel/env', 6 | { 7 | targets: { 8 | edge: '18', 9 | firefox: '66', 10 | chrome: '73', 11 | safari: '12', 12 | ie: '11', 13 | }, 14 | }, 15 | ], 16 | ]; 17 | 18 | module.exports = { presets }; 19 | -------------------------------------------------------------------------------- /internal/assert/assertion_error.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js lib/internal/assert/assertion_error.js 2 | // https://github.com/nodejs/node/commit/0817840f775032169ddd70c85ac059f18ffcc81c 3 | 4 | 'use strict'; 5 | 6 | const { inspect } = require('util/'); 7 | const { codes: { 8 | ERR_INVALID_ARG_TYPE 9 | } } = require('../errors'); 10 | 11 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith 12 | function endsWith(str, search, this_len) { 13 | if (this_len === undefined || this_len > str.length) { 14 | this_len = str.length; 15 | } 16 | return str.substring(this_len - search.length, this_len) === search; 17 | } 18 | 19 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat 20 | function repeat(str, count) { 21 | count = Math.floor(count); 22 | if (str.length == 0 || count == 0) 23 | return ''; 24 | 25 | var maxCount = str.length * count; 26 | count = Math.floor(Math.log(count) / Math.log(2)); 27 | while (count) { 28 | str += str; 29 | count--; 30 | } 31 | str += str.substring(0, maxCount - str.length); 32 | return str; 33 | } 34 | 35 | let blue = ''; 36 | let green = ''; 37 | let red = ''; 38 | let white = ''; 39 | 40 | const kReadableOperator = { 41 | deepStrictEqual: 'Expected values to be strictly deep-equal:', 42 | strictEqual: 'Expected values to be strictly equal:', 43 | strictEqualObject: 'Expected "actual" to be reference-equal to "expected":', 44 | deepEqual: 'Expected values to be loosely deep-equal:', 45 | equal: 'Expected values to be loosely equal:', 46 | notDeepStrictEqual: 'Expected "actual" not to be strictly deep-equal to:', 47 | notStrictEqual: 'Expected "actual" to be strictly unequal to:', 48 | notStrictEqualObject: 49 | 'Expected "actual" not to be reference-equal to "expected":', 50 | notDeepEqual: 'Expected "actual" not to be loosely deep-equal to:', 51 | notEqual: 'Expected "actual" to be loosely unequal to:', 52 | notIdentical: 'Values identical but not reference-equal:', 53 | }; 54 | 55 | // Comparing short primitives should just show === / !== instead of using the 56 | // diff. 57 | const kMaxShortLength = 10; 58 | 59 | function copyError(source) { 60 | const keys = Object.keys(source); 61 | const target = Object.create(Object.getPrototypeOf(source)); 62 | keys.forEach(key => { 63 | target[key] = source[key]; 64 | }); 65 | Object.defineProperty(target, 'message', { value: source.message }); 66 | return target; 67 | } 68 | 69 | function inspectValue(val) { 70 | // The util.inspect default values could be changed. This makes sure the 71 | // error messages contain the necessary information nevertheless. 72 | return inspect( 73 | val, 74 | { 75 | compact: false, 76 | customInspect: false, 77 | depth: 1000, 78 | maxArrayLength: Infinity, 79 | // Assert compares only enumerable properties (with a few exceptions). 80 | showHidden: false, 81 | // Having a long line as error is better than wrapping the line for 82 | // comparison for now. 83 | // TODO(BridgeAR): `breakLength` should be limited as soon as soon as we 84 | // have meta information about the inspected properties (i.e., know where 85 | // in what line the property starts and ends). 86 | breakLength: Infinity, 87 | // Assert does not detect proxies currently. 88 | showProxy: false, 89 | sorted: true, 90 | // Inspect getters as we also check them when comparing entries. 91 | getters: true 92 | } 93 | ); 94 | } 95 | 96 | function createErrDiff(actual, expected, operator) { 97 | let other = ''; 98 | let res = ''; 99 | let lastPos = 0; 100 | let end = ''; 101 | let skipped = false; 102 | const actualInspected = inspectValue(actual); 103 | const actualLines = actualInspected.split('\n'); 104 | const expectedLines = inspectValue(expected).split('\n'); 105 | 106 | let i = 0; 107 | let indicator = ''; 108 | 109 | // In case both values are objects explicitly mark them as not reference equal 110 | // for the `strictEqual` operator. 111 | if (operator === 'strictEqual' && 112 | typeof actual === 'object' && 113 | typeof expected === 'object' && 114 | actual !== null && 115 | expected !== null) { 116 | operator = 'strictEqualObject'; 117 | } 118 | 119 | // If "actual" and "expected" fit on a single line and they are not strictly 120 | // equal, check further special handling. 121 | if (actualLines.length === 1 && expectedLines.length === 1 && 122 | actualLines[0] !== expectedLines[0]) { 123 | const inputLength = actualLines[0].length + expectedLines[0].length; 124 | // If the character length of "actual" and "expected" together is less than 125 | // kMaxShortLength and if neither is an object and at least one of them is 126 | // not `zero`, use the strict equal comparison to visualize the output. 127 | if (inputLength <= kMaxShortLength) { 128 | if ((typeof actual !== 'object' || actual === null) && 129 | (typeof expected !== 'object' || expected === null) && 130 | (actual !== 0 || expected !== 0)) { // -0 === +0 131 | return `${kReadableOperator[operator]}\n\n` + 132 | `${actualLines[0]} !== ${expectedLines[0]}\n`; 133 | } 134 | } else if (operator !== 'strictEqualObject') { 135 | // If the stderr is a tty and the input length is lower than the current 136 | // columns per line, add a mismatch indicator below the output. If it is 137 | // not a tty, use a default value of 80 characters. 138 | const maxLength = process.stderr && process.stderr.isTTY ? process.stderr.columns : 80; 139 | if (inputLength < maxLength) { 140 | while (actualLines[0][i] === expectedLines[0][i]) { 141 | i++; 142 | } 143 | // Ignore the first characters. 144 | if (i > 2) { 145 | // Add position indicator for the first mismatch in case it is a 146 | // single line and the input length is less than the column length. 147 | indicator = `\n ${repeat(' ', i)}^`; 148 | i = 0; 149 | } 150 | } 151 | } 152 | } 153 | 154 | // Remove all ending lines that match (this optimizes the output for 155 | // readability by reducing the number of total changed lines). 156 | let a = actualLines[actualLines.length - 1]; 157 | let b = expectedLines[expectedLines.length - 1]; 158 | while (a === b) { 159 | if (i++ < 2) { 160 | end = `\n ${a}${end}`; 161 | } else { 162 | other = a; 163 | } 164 | actualLines.pop(); 165 | expectedLines.pop(); 166 | if (actualLines.length === 0 || expectedLines.length === 0) 167 | break; 168 | a = actualLines[actualLines.length - 1]; 169 | b = expectedLines[expectedLines.length - 1]; 170 | } 171 | 172 | const maxLines = Math.max(actualLines.length, expectedLines.length); 173 | // Strict equal with identical objects that are not identical by reference. 174 | // E.g., assert.deepStrictEqual({ a: Symbol() }, { a: Symbol() }) 175 | if (maxLines === 0) { 176 | // We have to get the result again. The lines were all removed before. 177 | const actualLines = actualInspected.split('\n'); 178 | 179 | // Only remove lines in case it makes sense to collapse those. 180 | // TODO: Accept env to always show the full error. 181 | if (actualLines.length > 30) { 182 | actualLines[26] = `${blue}...${white}`; 183 | while (actualLines.length > 27) { 184 | actualLines.pop(); 185 | } 186 | } 187 | 188 | return `${kReadableOperator.notIdentical}\n\n${actualLines.join('\n')}\n`; 189 | } 190 | 191 | if (i > 3) { 192 | end = `\n${blue}...${white}${end}`; 193 | skipped = true; 194 | } 195 | if (other !== '') { 196 | end = `\n ${other}${end}`; 197 | other = ''; 198 | } 199 | 200 | let printedLines = 0; 201 | const msg = kReadableOperator[operator] + 202 | `\n${green}+ actual${white} ${red}- expected${white}`; 203 | const skippedMsg = ` ${blue}...${white} Lines skipped`; 204 | 205 | for (i = 0; i < maxLines; i++) { 206 | // Only extra expected lines exist 207 | const cur = i - lastPos; 208 | if (actualLines.length < i + 1) { 209 | // If the last diverging line is more than one line above and the 210 | // current line is at least line three, add some of the former lines and 211 | // also add dots to indicate skipped entries. 212 | if (cur > 1 && i > 2) { 213 | if (cur > 4) { 214 | res += `\n${blue}...${white}`; 215 | skipped = true; 216 | } else if (cur > 3) { 217 | res += `\n ${expectedLines[i - 2]}`; 218 | printedLines++; 219 | } 220 | res += `\n ${expectedLines[i - 1]}`; 221 | printedLines++; 222 | } 223 | // Mark the current line as the last diverging one. 224 | lastPos = i; 225 | // Add the expected line to the cache. 226 | other += `\n${red}-${white} ${expectedLines[i]}`; 227 | printedLines++; 228 | // Only extra actual lines exist 229 | } else if (expectedLines.length < i + 1) { 230 | // If the last diverging line is more than one line above and the 231 | // current line is at least line three, add some of the former lines and 232 | // also add dots to indicate skipped entries. 233 | if (cur > 1 && i > 2) { 234 | if (cur > 4) { 235 | res += `\n${blue}...${white}`; 236 | skipped = true; 237 | } else if (cur > 3) { 238 | res += `\n ${actualLines[i - 2]}`; 239 | printedLines++; 240 | } 241 | res += `\n ${actualLines[i - 1]}`; 242 | printedLines++; 243 | } 244 | // Mark the current line as the last diverging one. 245 | lastPos = i; 246 | // Add the actual line to the result. 247 | res += `\n${green}+${white} ${actualLines[i]}`; 248 | printedLines++; 249 | // Lines diverge 250 | } else { 251 | const expectedLine = expectedLines[i]; 252 | let actualLine = actualLines[i]; 253 | // If the lines diverge, specifically check for lines that only diverge by 254 | // a trailing comma. In that case it is actually identical and we should 255 | // mark it as such. 256 | let divergingLines = actualLine !== expectedLine && 257 | (!endsWith(actualLine, ',') || 258 | actualLine.slice(0, -1) !== expectedLine); 259 | // If the expected line has a trailing comma but is otherwise identical, 260 | // add a comma at the end of the actual line. Otherwise the output could 261 | // look weird as in: 262 | // 263 | // [ 264 | // 1 // No comma at the end! 265 | // + 2 266 | // ] 267 | // 268 | if (divergingLines && 269 | endsWith(expectedLine, ',') && 270 | expectedLine.slice(0, -1) === actualLine) { 271 | divergingLines = false; 272 | actualLine += ','; 273 | } 274 | if (divergingLines) { 275 | // If the last diverging line is more than one line above and the 276 | // current line is at least line three, add some of the former lines and 277 | // also add dots to indicate skipped entries. 278 | if (cur > 1 && i > 2) { 279 | if (cur > 4) { 280 | res += `\n${blue}...${white}`; 281 | skipped = true; 282 | } else if (cur > 3) { 283 | res += `\n ${actualLines[i - 2]}`; 284 | printedLines++; 285 | } 286 | res += `\n ${actualLines[i - 1]}`; 287 | printedLines++; 288 | } 289 | // Mark the current line as the last diverging one. 290 | lastPos = i; 291 | // Add the actual line to the result and cache the expected diverging 292 | // line so consecutive diverging lines show up as +++--- and not +-+-+-. 293 | res += `\n${green}+${white} ${actualLine}`; 294 | other += `\n${red}-${white} ${expectedLine}`; 295 | printedLines += 2; 296 | // Lines are identical 297 | } else { 298 | // Add all cached information to the result before adding other things 299 | // and reset the cache. 300 | res += other; 301 | other = ''; 302 | // If the last diverging line is exactly one line above or if it is the 303 | // very first line, add the line to the result. 304 | if (cur === 1 || i === 0) { 305 | res += `\n ${actualLine}`; 306 | printedLines++; 307 | } 308 | } 309 | } 310 | // Inspected object to big (Show ~20 rows max) 311 | if (printedLines > 20 && i < maxLines - 2) { 312 | return `${msg}${skippedMsg}\n${res}\n${blue}...${white}${other}\n` + 313 | `${blue}...${white}`; 314 | } 315 | } 316 | 317 | return `${msg}${skipped ? skippedMsg : ''}\n${res}${other}${end}${indicator}`; 318 | } 319 | 320 | class AssertionError extends Error { 321 | constructor(options) { 322 | if (typeof options !== 'object' || options === null) { 323 | throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); 324 | } 325 | const { 326 | message, 327 | operator, 328 | stackStartFn 329 | } = options; 330 | let { 331 | actual, 332 | expected 333 | } = options; 334 | 335 | const limit = Error.stackTraceLimit; 336 | Error.stackTraceLimit = 0; 337 | 338 | if (message != null) { 339 | super(String(message)); 340 | } else { 341 | if (process.stderr && process.stderr.isTTY) { 342 | // Reset on each call to make sure we handle dynamically set environment 343 | // variables correct. 344 | if ( 345 | process.stderr && 346 | process.stderr.getColorDepth && 347 | process.stderr.getColorDepth() !== 1 348 | ) { 349 | blue = '\u001b[34m'; 350 | green = '\u001b[32m'; 351 | white = '\u001b[39m'; 352 | red = '\u001b[31m'; 353 | } else { 354 | blue = ''; 355 | green = ''; 356 | white = ''; 357 | red = ''; 358 | } 359 | } 360 | // Prevent the error stack from being visible by duplicating the error 361 | // in a very close way to the original in case both sides are actually 362 | // instances of Error. 363 | if (typeof actual === 'object' && actual !== null && 364 | typeof expected === 'object' && expected !== null && 365 | 'stack' in actual && actual instanceof Error && 366 | 'stack' in expected && expected instanceof Error) { 367 | actual = copyError(actual); 368 | expected = copyError(expected); 369 | } 370 | 371 | if (operator === 'deepStrictEqual' || operator === 'strictEqual') { 372 | super(createErrDiff(actual, expected, operator)); 373 | } else if (operator === 'notDeepStrictEqual' || 374 | operator === 'notStrictEqual') { 375 | // In case the objects are equal but the operator requires unequal, show 376 | // the first object and say A equals B 377 | let base = kReadableOperator[operator]; 378 | const res = inspectValue(actual).split('\n'); 379 | 380 | // In case "actual" is an object, it should not be reference equal. 381 | if (operator === 'notStrictEqual' && 382 | typeof actual === 'object' && 383 | actual !== null) { 384 | base = kReadableOperator.notStrictEqualObject; 385 | } 386 | 387 | // Only remove lines in case it makes sense to collapse those. 388 | // TODO: Accept env to always show the full error. 389 | if (res.length > 30) { 390 | res[26] = `${blue}...${white}`; 391 | while (res.length > 27) { 392 | res.pop(); 393 | } 394 | } 395 | 396 | // Only print a single input. 397 | if (res.length === 1) { 398 | super(`${base} ${res[0]}`); 399 | } else { 400 | super(`${base}\n\n${res.join('\n')}\n`); 401 | } 402 | } else { 403 | let res = inspectValue(actual); 404 | let other = ''; 405 | const knownOperators = kReadableOperator[operator]; 406 | if (operator === 'notDeepEqual' || operator === 'notEqual') { 407 | res = `${kReadableOperator[operator]}\n\n${res}`; 408 | if (res.length > 1024) { 409 | res = `${res.slice(0, 1021)}...`; 410 | } 411 | } else { 412 | other = `${inspectValue(expected)}`; 413 | if (res.length > 512) { 414 | res = `${res.slice(0, 509)}...`; 415 | } 416 | if (other.length > 512) { 417 | other = `${other.slice(0, 509)}...`; 418 | } 419 | if (operator === 'deepEqual' || operator === 'equal') { 420 | res = `${knownOperators}\n\n${res}\n\nshould equal\n\n`; 421 | } else { 422 | other = ` ${operator} ${other}`; 423 | } 424 | } 425 | super(`${res}${other}`); 426 | } 427 | } 428 | 429 | Error.stackTraceLimit = limit; 430 | 431 | this.generatedMessage = !message; 432 | Object.defineProperty(this, 'name', { 433 | value: 'AssertionError [ERR_ASSERTION]', 434 | enumerable: false, 435 | writable: true, 436 | configurable: true 437 | }); 438 | this.code = 'ERR_ASSERTION'; 439 | this.actual = actual; 440 | this.expected = expected; 441 | this.operator = operator; 442 | if (Error.captureStackTrace) { 443 | // eslint-disable-next-line no-restricted-syntax 444 | Error.captureStackTrace(this, stackStartFn); 445 | } 446 | // Create error message including the error code in the name. 447 | this.stack; 448 | // Reset the name. 449 | this.name = 'AssertionError'; 450 | } 451 | 452 | toString() { 453 | return `${this.name} [${this.code}]: ${this.message}`; 454 | } 455 | 456 | [inspect.custom](recurseTimes, ctx) { 457 | // This limits the `actual` and `expected` property default inspection to 458 | // the minimum depth. Otherwise those values would be too verbose compared 459 | // to the actual error message which contains a combined view of these two 460 | // input values. 461 | return inspect(this, { ...ctx, customInspect: false, depth: 0 }); 462 | } 463 | } 464 | 465 | module.exports = AssertionError; 466 | -------------------------------------------------------------------------------- /internal/errors.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js lib/internal/errors.js 2 | // https://github.com/nodejs/node/commit/3b044962c48fe313905877a96b5d0894a5404f6f 3 | 4 | 'use strict'; 5 | 6 | // The whole point behind this internal module is to allow Node.js to no 7 | // longer be forced to treat every error message change as a semver-major 8 | // change. The NodeError classes here all expose a `code` property whose 9 | // value statically and permanently identifies the error. While the error 10 | // message may change, the code should not. 11 | 12 | const codes = {}; 13 | 14 | // Lazy loaded 15 | let assert; 16 | let util; 17 | 18 | function createErrorType(code, message, Base) { 19 | if (!Base) { 20 | Base = Error 21 | } 22 | 23 | function getMessage (arg1, arg2, arg3) { 24 | if (typeof message === 'string') { 25 | return message 26 | } else { 27 | return message(arg1, arg2, arg3) 28 | } 29 | } 30 | 31 | class NodeError extends Base { 32 | constructor (arg1, arg2, arg3) { 33 | super(getMessage(arg1, arg2, arg3)); 34 | this.code = code; 35 | } 36 | } 37 | 38 | codes[code] = NodeError; 39 | } 40 | 41 | // https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js 42 | function oneOf(expected, thing) { 43 | if (Array.isArray(expected)) { 44 | const len = expected.length; 45 | expected = expected.map((i) => String(i)); 46 | if (len > 2) { 47 | return `one of ${thing} ${expected.slice(0, len - 1).join(', ')}, or ` + 48 | expected[len - 1]; 49 | } else if (len === 2) { 50 | return `one of ${thing} ${expected[0]} or ${expected[1]}`; 51 | } else { 52 | return `of ${thing} ${expected[0]}`; 53 | } 54 | } else { 55 | return `of ${thing} ${String(expected)}`; 56 | } 57 | } 58 | 59 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith 60 | function startsWith(str, search, pos) { 61 | return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; 62 | } 63 | 64 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith 65 | function endsWith(str, search, this_len) { 66 | if (this_len === undefined || this_len > str.length) { 67 | this_len = str.length; 68 | } 69 | return str.substring(this_len - search.length, this_len) === search; 70 | } 71 | 72 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes 73 | function includes(str, search, start) { 74 | if (typeof start !== 'number') { 75 | start = 0; 76 | } 77 | 78 | if (start + search.length > str.length) { 79 | return false; 80 | } else { 81 | return str.indexOf(search, start) !== -1; 82 | } 83 | } 84 | 85 | createErrorType('ERR_AMBIGUOUS_ARGUMENT', 'The "%s" argument is ambiguous. %s', TypeError); 86 | createErrorType('ERR_INVALID_ARG_TYPE', 87 | (name, expected, actual) => { 88 | if (assert === undefined) assert = require('../assert'); 89 | assert(typeof name === 'string', "'name' must be a string"); 90 | 91 | // determiner: 'must be' or 'must not be' 92 | let determiner; 93 | if (typeof expected === 'string' && startsWith(expected, 'not ')) { 94 | determiner = 'must not be'; 95 | expected = expected.replace(/^not /, ''); 96 | } else { 97 | determiner = 'must be'; 98 | } 99 | 100 | let msg; 101 | if (endsWith(name, ' argument')) { 102 | // For cases like 'first argument' 103 | msg = `The ${name} ${determiner} ${oneOf(expected, 'type')}`; 104 | } else { 105 | const type = includes(name, '.') ? 'property' : 'argument'; 106 | msg = `The "${name}" ${type} ${determiner} ${oneOf(expected, 'type')}`; 107 | } 108 | 109 | // TODO(BridgeAR): Improve the output by showing `null` and similar. 110 | msg += `. Received type ${typeof actual}`; 111 | return msg; 112 | }, TypeError); 113 | createErrorType('ERR_INVALID_ARG_VALUE', (name, value, reason = 'is invalid') => { 114 | if (util === undefined) util = require('util/'); 115 | let inspected = util.inspect(value); 116 | if (inspected.length > 128) { 117 | inspected = `${inspected.slice(0, 128)}...`; 118 | } 119 | return `The argument '${name}' ${reason}. Received ${inspected}`; 120 | }, TypeError, RangeError); 121 | createErrorType('ERR_INVALID_RETURN_VALUE', (input, name, value) => { 122 | let type; 123 | if (value && value.constructor && value.constructor.name) { 124 | type = `instance of ${value.constructor.name}`; 125 | } else { 126 | type = `type ${typeof value}`; 127 | } 128 | return `Expected ${input} to be returned from the "${name}"` + 129 | ` function but got ${type}.`; 130 | }, TypeError); 131 | createErrorType('ERR_MISSING_ARGS', 132 | (...args) => { 133 | if (assert === undefined) assert = require('../assert'); 134 | assert(args.length > 0, 'At least one arg needs to be specified'); 135 | let msg = 'The '; 136 | const len = args.length; 137 | args = args.map((a) => `"${a}"`); 138 | switch (len) { 139 | case 1: 140 | msg += `${args[0]} argument`; 141 | break; 142 | case 2: 143 | msg += `${args[0]} and ${args[1]} arguments`; 144 | break; 145 | default: 146 | msg += args.slice(0, len - 1).join(', '); 147 | msg += `, and ${args[len - 1]} arguments`; 148 | break; 149 | } 150 | return `${msg} must be specified`; 151 | }, TypeError); 152 | 153 | module.exports.codes = codes; 154 | -------------------------------------------------------------------------------- /internal/util/comparisons.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js lib/internal/util/comparisons.js 2 | // https://github.com/nodejs/node/commit/112cc7c27551254aa2b17098fb774867f05ed0d9 3 | 4 | 'use strict'; 5 | 6 | const regexFlagsSupported = /a/g.flags !== undefined; 7 | 8 | const arrayFromSet = set => { 9 | const array = []; 10 | set.forEach(value => array.push(value)); 11 | 12 | return array; 13 | }; 14 | 15 | const arrayFromMap = map => { 16 | const array = []; 17 | map.forEach((value, key) => array.push([key, value])); 18 | 19 | return array; 20 | }; 21 | 22 | const objectIs = Object.is ? Object.is : require('object-is'); 23 | const objectGetOwnPropertySymbols = Object.getOwnPropertySymbols ? Object.getOwnPropertySymbols : () => []; 24 | const numberIsNaN = Number.isNaN ? Number.isNaN : require('is-nan'); 25 | 26 | function uncurryThis(f) { 27 | return f.call.bind(f); 28 | } 29 | 30 | const hasOwnProperty = uncurryThis(Object.prototype.hasOwnProperty); 31 | const propertyIsEnumerable = uncurryThis(Object.prototype.propertyIsEnumerable); 32 | const objectToString = uncurryThis(Object.prototype.toString); 33 | 34 | const { 35 | isAnyArrayBuffer, 36 | isArrayBufferView, 37 | isDate, 38 | isMap, 39 | isRegExp, 40 | isSet, 41 | isNativeError, 42 | isBoxedPrimitive, 43 | isNumberObject, 44 | isStringObject, 45 | isBooleanObject, 46 | isBigIntObject, 47 | isSymbolObject, 48 | isFloat32Array, 49 | isFloat64Array 50 | } = require('util/').types; 51 | 52 | function isNonIndex(key) { 53 | if (key.length === 0 || key.length > 10) 54 | return true; 55 | for (var i = 0; i < key.length; i++) { 56 | const code = key.charCodeAt(i); 57 | if (code < 48 || code > 57) 58 | return true; 59 | } 60 | // The maximum size for an array is 2 ** 32 -1. 61 | return key.length === 10 && key >= 2 ** 32; 62 | } 63 | 64 | function getOwnNonIndexProperties(value) { 65 | return Object.keys(value) 66 | .filter(isNonIndex) 67 | .concat( 68 | objectGetOwnPropertySymbols(value) 69 | .filter(Object.prototype.propertyIsEnumerable.bind(value)) 70 | ); 71 | } 72 | 73 | // Taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js 74 | // original notice: 75 | /*! 76 | * The buffer module from node.js, for the browser. 77 | * 78 | * @author Feross Aboukhadijeh 79 | * @license MIT 80 | */ 81 | function compare(a, b) { 82 | if (a === b) { 83 | return 0; 84 | } 85 | 86 | var x = a.length; 87 | var y = b.length; 88 | 89 | for (var i = 0, len = Math.min(x, y); i < len; ++i) { 90 | if (a[i] !== b[i]) { 91 | x = a[i]; 92 | y = b[i]; 93 | break; 94 | } 95 | } 96 | 97 | if (x < y) { 98 | return -1; 99 | } 100 | if (y < x) { 101 | return 1; 102 | } 103 | return 0; 104 | } 105 | 106 | const ONLY_ENUMERABLE = undefined; 107 | 108 | const kStrict = true; 109 | const kLoose = false; 110 | 111 | const kNoIterator = 0; 112 | const kIsArray = 1; 113 | const kIsSet = 2; 114 | const kIsMap = 3; 115 | 116 | // Check if they have the same source and flags 117 | function areSimilarRegExps(a, b) { 118 | return regexFlagsSupported 119 | ? a.source === b.source && a.flags === b.flags 120 | : RegExp.prototype.toString.call(a) === RegExp.prototype.toString.call(b); 121 | } 122 | 123 | function areSimilarFloatArrays(a, b) { 124 | if (a.byteLength !== b.byteLength) { 125 | return false; 126 | } 127 | for (var offset = 0; offset < a.byteLength; offset++) { 128 | if (a[offset] !== b[offset]) { 129 | return false; 130 | } 131 | } 132 | return true; 133 | } 134 | 135 | function areSimilarTypedArrays(a, b) { 136 | if (a.byteLength !== b.byteLength) { 137 | return false; 138 | } 139 | return compare(new Uint8Array(a.buffer, a.byteOffset, a.byteLength), 140 | new Uint8Array(b.buffer, b.byteOffset, b.byteLength)) === 0; 141 | } 142 | 143 | function areEqualArrayBuffers(buf1, buf2) { 144 | return buf1.byteLength === buf2.byteLength && 145 | compare(new Uint8Array(buf1), new Uint8Array(buf2)) === 0; 146 | } 147 | 148 | function isEqualBoxedPrimitive(val1, val2) { 149 | if (isNumberObject(val1)) { 150 | return isNumberObject(val2) && 151 | objectIs(Number.prototype.valueOf.call(val1), 152 | Number.prototype.valueOf.call(val2)); 153 | } 154 | if (isStringObject(val1)) { 155 | return isStringObject(val2) && 156 | String.prototype.valueOf.call(val1) === String.prototype.valueOf.call(val2); 157 | } 158 | if (isBooleanObject(val1)) { 159 | return isBooleanObject(val2) && 160 | Boolean.prototype.valueOf.call(val1) === Boolean.prototype.valueOf.call(val2); 161 | } 162 | if (isBigIntObject(val1)) { 163 | return isBigIntObject(val2) && 164 | BigInt.prototype.valueOf.call(val1) === BigInt.prototype.valueOf.call(val2); 165 | } 166 | return isSymbolObject(val2) && 167 | Symbol.prototype.valueOf.call(val1) === Symbol.prototype.valueOf.call(val2); 168 | } 169 | 170 | // Notes: Type tags are historical [[Class]] properties that can be set by 171 | // FunctionTemplate::SetClassName() in C++ or Symbol.toStringTag in JS 172 | // and retrieved using Object.prototype.toString.call(obj) in JS 173 | // See https://tc39.github.io/ecma262/#sec-object.prototype.tostring 174 | // for a list of tags pre-defined in the spec. 175 | // There are some unspecified tags in the wild too (e.g. typed array tags). 176 | // Since tags can be altered, they only serve fast failures 177 | // 178 | // Typed arrays and buffers are checked by comparing the content in their 179 | // underlying ArrayBuffer. This optimization requires that it's 180 | // reasonable to interpret their underlying memory in the same way, 181 | // which is checked by comparing their type tags. 182 | // (e.g. a Uint8Array and a Uint16Array with the same memory content 183 | // could still be different because they will be interpreted differently). 184 | // 185 | // For strict comparison, objects should have 186 | // a) The same built-in type tags 187 | // b) The same prototypes. 188 | 189 | function innerDeepEqual(val1, val2, strict, memos) { 190 | // All identical values are equivalent, as determined by ===. 191 | if (val1 === val2) { 192 | if (val1 !== 0) 193 | return true; 194 | return strict ? objectIs(val1, val2) : true; 195 | } 196 | 197 | // Check more closely if val1 and val2 are equal. 198 | if (strict) { 199 | if (typeof val1 !== 'object') { 200 | return typeof val1 === 'number' && numberIsNaN(val1) && 201 | numberIsNaN(val2); 202 | } 203 | if (typeof val2 !== 'object' || val1 === null || val2 === null) { 204 | return false; 205 | } 206 | if (Object.getPrototypeOf(val1) !== Object.getPrototypeOf(val2)) { 207 | return false; 208 | } 209 | } else { 210 | if (val1 === null || typeof val1 !== 'object') { 211 | if (val2 === null || typeof val2 !== 'object') { 212 | // eslint-disable-next-line eqeqeq 213 | return val1 == val2; 214 | } 215 | return false; 216 | } 217 | if (val2 === null || typeof val2 !== 'object') { 218 | return false; 219 | } 220 | } 221 | const val1Tag = objectToString(val1); 222 | const val2Tag = objectToString(val2); 223 | 224 | if (val1Tag !== val2Tag) { 225 | return false; 226 | } 227 | if (Array.isArray(val1)) { 228 | // Check for sparse arrays and general fast path 229 | if (val1.length !== val2.length) { 230 | return false; 231 | } 232 | const keys1 = getOwnNonIndexProperties(val1, ONLY_ENUMERABLE); 233 | const keys2 = getOwnNonIndexProperties(val2, ONLY_ENUMERABLE); 234 | if (keys1.length !== keys2.length) { 235 | return false; 236 | } 237 | return keyCheck(val1, val2, strict, memos, kIsArray, keys1); 238 | } 239 | // [browserify] This triggers on certain types in IE (Map/Set) so we don't 240 | // wan't to early return out of the rest of the checks. However we can check 241 | // if the second value is one of these values and the first isn't. 242 | if (val1Tag === '[object Object]') { 243 | // return keyCheck(val1, val2, strict, memos, kNoIterator); 244 | if ( 245 | (!isMap(val1) && isMap(val2)) || 246 | (!isSet(val1) && isSet(val2)) 247 | ) { 248 | return false; 249 | } 250 | } 251 | if (isDate(val1)) { 252 | if (!isDate(val2) || Date.prototype.getTime.call(val1) !== Date.prototype.getTime.call(val2)) { 253 | return false; 254 | } 255 | } else if (isRegExp(val1)) { 256 | if (!isRegExp(val2) || !areSimilarRegExps(val1, val2)) { 257 | return false; 258 | } 259 | } else if (isNativeError(val1) || val1 instanceof Error) { 260 | // Do not compare the stack as it might differ even though the error itself 261 | // is otherwise identical. 262 | if (val1.message !== val2.message || val1.name !== val2.name) { 263 | return false; 264 | } 265 | } else if (isArrayBufferView(val1)) { 266 | if (!strict && (isFloat32Array(val1) || isFloat64Array(val1))) { 267 | if (!areSimilarFloatArrays(val1, val2)) { 268 | return false; 269 | } 270 | } else if (!areSimilarTypedArrays(val1, val2)) { 271 | return false; 272 | } 273 | // Buffer.compare returns true, so val1.length === val2.length. If they both 274 | // only contain numeric keys, we don't need to exam further than checking 275 | // the symbols. 276 | const keys1 = getOwnNonIndexProperties(val1, ONLY_ENUMERABLE); 277 | const keys2 = getOwnNonIndexProperties(val2, ONLY_ENUMERABLE); 278 | if (keys1.length !== keys2.length) { 279 | return false; 280 | } 281 | return keyCheck(val1, val2, strict, memos, kNoIterator, keys1); 282 | } else if (isSet(val1)) { 283 | if (!isSet(val2) || val1.size !== val2.size) { 284 | return false; 285 | } 286 | return keyCheck(val1, val2, strict, memos, kIsSet); 287 | } else if (isMap(val1)) { 288 | if (!isMap(val2) || val1.size !== val2.size) { 289 | return false; 290 | } 291 | return keyCheck(val1, val2, strict, memos, kIsMap); 292 | } else if (isAnyArrayBuffer(val1)) { 293 | if (!areEqualArrayBuffers(val1, val2)) { 294 | return false; 295 | } 296 | } else if (isBoxedPrimitive(val1) && !isEqualBoxedPrimitive(val1, val2)) { 297 | return false; 298 | } 299 | return keyCheck(val1, val2, strict, memos, kNoIterator); 300 | } 301 | 302 | function getEnumerables(val, keys) { 303 | return keys.filter((k) => propertyIsEnumerable(val, k)); 304 | } 305 | 306 | function keyCheck(val1, val2, strict, memos, iterationType, aKeys) { 307 | // For all remaining Object pairs, including Array, objects and Maps, 308 | // equivalence is determined by having: 309 | // a) The same number of owned enumerable properties 310 | // b) The same set of keys/indexes (although not necessarily the same order) 311 | // c) Equivalent values for every corresponding key/index 312 | // d) For Sets and Maps, equal contents 313 | // Note: this accounts for both named and indexed properties on Arrays. 314 | if (arguments.length === 5) { 315 | aKeys = Object.keys(val1); 316 | const bKeys = Object.keys(val2); 317 | 318 | // The pair must have the same number of owned properties. 319 | if (aKeys.length !== bKeys.length) { 320 | return false; 321 | } 322 | } 323 | 324 | // Cheap key test 325 | let i = 0; 326 | for (; i < aKeys.length; i++) { 327 | if (!hasOwnProperty(val2, aKeys[i])) { 328 | return false; 329 | } 330 | } 331 | 332 | if (strict && arguments.length === 5) { 333 | const symbolKeysA = objectGetOwnPropertySymbols(val1); 334 | if (symbolKeysA.length !== 0) { 335 | let count = 0; 336 | for (i = 0; i < symbolKeysA.length; i++) { 337 | const key = symbolKeysA[i]; 338 | if (propertyIsEnumerable(val1, key)) { 339 | if (!propertyIsEnumerable(val2, key)) { 340 | return false; 341 | } 342 | aKeys.push(key); 343 | count++; 344 | } else if (propertyIsEnumerable(val2, key)) { 345 | return false; 346 | } 347 | } 348 | const symbolKeysB = objectGetOwnPropertySymbols(val2); 349 | if (symbolKeysA.length !== symbolKeysB.length && 350 | getEnumerables(val2, symbolKeysB).length !== count) { 351 | return false; 352 | } 353 | } else { 354 | const symbolKeysB = objectGetOwnPropertySymbols(val2); 355 | if (symbolKeysB.length !== 0 && 356 | getEnumerables(val2, symbolKeysB).length !== 0) { 357 | return false; 358 | } 359 | } 360 | } 361 | 362 | if (aKeys.length === 0 && 363 | (iterationType === kNoIterator || 364 | iterationType === kIsArray && val1.length === 0 || 365 | val1.size === 0)) { 366 | return true; 367 | } 368 | 369 | // Use memos to handle cycles. 370 | if (memos === undefined) { 371 | memos = { 372 | val1: new Map(), 373 | val2: new Map(), 374 | position: 0 375 | }; 376 | } else { 377 | // We prevent up to two map.has(x) calls by directly retrieving the value 378 | // and checking for undefined. The map can only contain numbers, so it is 379 | // safe to check for undefined only. 380 | const val2MemoA = memos.val1.get(val1); 381 | if (val2MemoA !== undefined) { 382 | const val2MemoB = memos.val2.get(val2); 383 | if (val2MemoB !== undefined) { 384 | return val2MemoA === val2MemoB; 385 | } 386 | } 387 | memos.position++; 388 | } 389 | 390 | memos.val1.set(val1, memos.position); 391 | memos.val2.set(val2, memos.position); 392 | 393 | const areEq = objEquiv(val1, val2, strict, aKeys, memos, iterationType); 394 | 395 | memos.val1.delete(val1); 396 | memos.val2.delete(val2); 397 | 398 | return areEq; 399 | } 400 | 401 | function setHasEqualElement(set, val1, strict, memo) { 402 | // Go looking. 403 | const setValues = arrayFromSet(set); 404 | for (let i = 0; i < setValues.length; i++) { 405 | const val2 = setValues[i]; 406 | if (innerDeepEqual(val1, val2, strict, memo)) { 407 | // Remove the matching element to make sure we do not check that again. 408 | set.delete(val2); 409 | return true; 410 | } 411 | } 412 | 413 | return false; 414 | } 415 | 416 | // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#Loose_equality_using 417 | // Sadly it is not possible to detect corresponding values properly in case the 418 | // type is a string, number, bigint or boolean. The reason is that those values 419 | // can match lots of different string values (e.g., 1n == '+00001'). 420 | function findLooseMatchingPrimitives(prim) { 421 | switch (typeof prim) { 422 | case 'undefined': 423 | return null; 424 | case 'object': // Only pass in null as object! 425 | return undefined; 426 | case 'symbol': 427 | return false; 428 | case 'string': 429 | prim = +prim; 430 | // Loose equal entries exist only if the string is possible to convert to 431 | // a regular number and not NaN. 432 | // Fall through 433 | case 'number': 434 | if (numberIsNaN(prim)) { 435 | return false; 436 | } 437 | } 438 | return true; 439 | } 440 | 441 | function setMightHaveLoosePrim(a, b, prim) { 442 | const altValue = findLooseMatchingPrimitives(prim); 443 | if (altValue != null) 444 | return altValue; 445 | 446 | return b.has(altValue) && !a.has(altValue); 447 | } 448 | 449 | function mapMightHaveLoosePrim(a, b, prim, item, memo) { 450 | const altValue = findLooseMatchingPrimitives(prim); 451 | if (altValue != null) { 452 | return altValue; 453 | } 454 | const curB = b.get(altValue); 455 | if (curB === undefined && !b.has(altValue) || 456 | !innerDeepEqual(item, curB, false, memo)) { 457 | return false; 458 | } 459 | return !a.has(altValue) && innerDeepEqual(item, curB, false, memo); 460 | } 461 | 462 | function setEquiv(a, b, strict, memo) { 463 | // This is a lazily initiated Set of entries which have to be compared 464 | // pairwise. 465 | let set = null; 466 | const aValues = arrayFromSet(a); 467 | for (let i = 0; i < aValues.length; i++) { 468 | const val = aValues[i]; 469 | // Note: Checking for the objects first improves the performance for object 470 | // heavy sets but it is a minor slow down for primitives. As they are fast 471 | // to check this improves the worst case scenario instead. 472 | if (typeof val === 'object' && val !== null) { 473 | if (set === null) { 474 | set = new Set(); 475 | } 476 | // If the specified value doesn't exist in the second set its an not null 477 | // object (or non strict only: a not matching primitive) we'll need to go 478 | // hunting for something thats deep-(strict-)equal to it. To make this 479 | // O(n log n) complexity we have to copy these values in a new set first. 480 | set.add(val); 481 | } else if (!b.has(val)) { 482 | if (strict) 483 | return false; 484 | 485 | // Fast path to detect missing string, symbol, undefined and null values. 486 | if (!setMightHaveLoosePrim(a, b, val)) { 487 | return false; 488 | } 489 | 490 | if (set === null) { 491 | set = new Set(); 492 | } 493 | set.add(val); 494 | } 495 | } 496 | 497 | if (set !== null) { 498 | const bValues = arrayFromSet(b); 499 | for (let i = 0; i < bValues.length; i++) { 500 | const val = bValues[i]; 501 | // We have to check if a primitive value is already 502 | // matching and only if it's not, go hunting for it. 503 | if (typeof val === 'object' && val !== null) { 504 | if (!setHasEqualElement(set, val, strict, memo)) 505 | return false; 506 | } else if (!strict && 507 | !a.has(val) && 508 | !setHasEqualElement(set, val, strict, memo)) { 509 | return false; 510 | } 511 | } 512 | return set.size === 0; 513 | } 514 | 515 | return true; 516 | } 517 | 518 | function mapHasEqualEntry(set, map, key1, item1, strict, memo) { 519 | // To be able to handle cases like: 520 | // Map([[{}, 'a'], [{}, 'b']]) vs Map([[{}, 'b'], [{}, 'a']]) 521 | // ... we need to consider *all* matching keys, not just the first we find. 522 | const setValues = arrayFromSet(set); 523 | for (let i = 0; i < setValues.length; i++) { 524 | const key2 = setValues[i]; 525 | if (innerDeepEqual(key1, key2, strict, memo) && 526 | innerDeepEqual(item1, map.get(key2), strict, memo)) { 527 | set.delete(key2); 528 | return true; 529 | } 530 | } 531 | 532 | return false; 533 | } 534 | 535 | function mapEquiv(a, b, strict, memo) { 536 | let set = null; 537 | 538 | const aEntries = arrayFromMap(a); 539 | for (let i = 0; i < aEntries.length; i++) { 540 | const [key, item1] = aEntries[i]; 541 | if (typeof key === 'object' && key !== null) { 542 | if (set === null) { 543 | set = new Set(); 544 | } 545 | set.add(key); 546 | } else { 547 | // By directly retrieving the value we prevent another b.has(key) check in 548 | // almost all possible cases. 549 | const item2 = b.get(key); 550 | if ((item2 === undefined && !b.has(key) || 551 | !innerDeepEqual(item1, item2, strict, memo))) { 552 | if (strict) 553 | return false; 554 | // Fast path to detect missing string, symbol, undefined and null 555 | // keys. 556 | if (!mapMightHaveLoosePrim(a, b, key, item1, memo)) 557 | return false; 558 | if (set === null) { 559 | set = new Set(); 560 | } 561 | set.add(key); 562 | } 563 | } 564 | } 565 | 566 | if (set !== null) { 567 | const bEntries = arrayFromMap(b); 568 | for (let i = 0; i < bEntries.length; i++) { 569 | const [key, item] = bEntries[i]; 570 | if (typeof key === 'object' && key !== null) { 571 | if (!mapHasEqualEntry(set, a, key, item, strict, memo)) 572 | return false; 573 | } else if (!strict && 574 | (!a.has(key) || 575 | !innerDeepEqual(a.get(key), item, false, memo)) && 576 | !mapHasEqualEntry(set, a, key, item, false, memo)) { 577 | return false; 578 | } 579 | } 580 | return set.size === 0; 581 | } 582 | 583 | return true; 584 | } 585 | 586 | function objEquiv(a, b, strict, keys, memos, iterationType) { 587 | // Sets and maps don't have their entries accessible via normal object 588 | // properties. 589 | let i = 0; 590 | 591 | if (iterationType === kIsSet) { 592 | if (!setEquiv(a, b, strict, memos)) { 593 | return false; 594 | } 595 | } else if (iterationType === kIsMap) { 596 | if (!mapEquiv(a, b, strict, memos)) { 597 | return false; 598 | } 599 | } else if (iterationType === kIsArray) { 600 | for (; i < a.length; i++) { 601 | if (hasOwnProperty(a, i)) { 602 | if (!hasOwnProperty(b, i) || 603 | !innerDeepEqual(a[i], b[i], strict, memos)) { 604 | return false; 605 | } 606 | } else if (hasOwnProperty(b, i)) { 607 | return false; 608 | } else { 609 | // Array is sparse. 610 | const keysA = Object.keys(a); 611 | for (; i < keysA.length; i++) { 612 | const key = keysA[i]; 613 | if (!hasOwnProperty(b, key) || 614 | !innerDeepEqual(a[key], b[key], strict, memos)) { 615 | return false; 616 | } 617 | } 618 | if (keysA.length !== Object.keys(b).length) { 619 | return false; 620 | } 621 | return true; 622 | } 623 | } 624 | } 625 | 626 | // The pair must have equivalent values for every corresponding key. 627 | // Possibly expensive deep test: 628 | for (i = 0; i < keys.length; i++) { 629 | const key = keys[i]; 630 | if (!innerDeepEqual(a[key], b[key], strict, memos)) { 631 | return false; 632 | } 633 | } 634 | return true; 635 | } 636 | 637 | function isDeepEqual(val1, val2) { 638 | return innerDeepEqual(val1, val2, kLoose); 639 | } 640 | 641 | function isDeepStrictEqual(val1, val2) { 642 | return innerDeepEqual(val1, val2, kStrict); 643 | } 644 | 645 | module.exports = { 646 | isDeepEqual, 647 | isDeepStrictEqual 648 | }; 649 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assert", 3 | "version": "2.1.0", 4 | "description": "The assert module from Node.js, for the browser.", 5 | "main": "build/assert.js", 6 | "files": [ 7 | "build/assert.js", 8 | "build/internal" 9 | ], 10 | "license": "MIT", 11 | "homepage": "https://github.com/browserify/commonjs-assert", 12 | "repository": "browserify/commonjs-assert", 13 | "scripts": { 14 | "build": "babel assert.js test.js --out-dir build && babel internal --out-dir build/internal && babel test --out-dir build/test", 15 | "prepare": "npm run build", 16 | "dev": "babel assert.js test.js --watch --out-dir build & babel internal --watch --out-dir build/internal & babel test --watch --out-dir build/test", 17 | "lint": "eslint --ext=js,mjs .", 18 | "pretest": "npm run lint", 19 | "test": "npm run tests-only", 20 | "posttest": "aud --production", 21 | "pretests-only": "npm run build", 22 | "tests-only": "npm run test:nobuild", 23 | "test:nobuild": "tape build/test.js", 24 | "test:source": "tape test.js", 25 | "test:browsers": "airtap build/test.js", 26 | "test:browsers:local": "npm run test:browsers -- --local" 27 | }, 28 | "keywords": [ 29 | "assert", 30 | "browser" 31 | ], 32 | "devDependencies": { 33 | "@babel/cli": "^7.22.15", 34 | "@babel/core": "^7.22.15", 35 | "@babel/preset-env": "^7.22.15", 36 | "@ljharb/eslint-config": "^21.1.0", 37 | "airtap": "^2.0.4", 38 | "array-fill": "^1.2.0", 39 | "aud": "^2.0.3", 40 | "core-js": "^3.32.2", 41 | "cross-env": "^5.2.1", 42 | "eslint": "=8.8.0", 43 | "object.entries": "^1.1.7", 44 | "object.getownpropertydescriptors": "^2.1.7", 45 | "tape": "^5.6.6" 46 | }, 47 | "dependencies": { 48 | "call-bind": "^1.0.2", 49 | "is-nan": "^1.3.2", 50 | "object-is": "^1.1.5", 51 | "object.assign": "^4.1.4", 52 | "util": "^0.12.5" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const test = require('tape'); 4 | 5 | // Conditionally load polyfills required for testing 6 | if (typeof Promise === 'undefined') { 7 | console.log('Loading Promise polyfill'); 8 | require('core-js/features/promise'); 9 | } 10 | 11 | const testPaths = [ 12 | ['test-assert-async.js', () => require('./test/parallel/test-assert-async.js')], 13 | ['test-assert-checktag.js', () => require('./test/parallel/test-assert-checktag.js')], 14 | ['test-assert-deep.js', () => require('./test/parallel/test-assert-deep.js')], 15 | ['test-assert-fail-deprecation.js', () => require('./test/parallel/test-assert-fail-deprecation.js')], 16 | ['test-assert-fail.js', () => require('./test/parallel/test-assert-fail.js')], 17 | ['test-assert-if-error.js', () => require('./test/parallel/test-assert-if-error.js')], 18 | ['test-assert-match.js', () => require('./test/parallel/test-assert-match.js')], 19 | ['test-assert-typedarray-deepequal.js', () => require('./test/parallel/test-assert-typedarray-deepequal.js')], 20 | ['test-assert.js', () => require('./test/parallel/test-assert.js')], 21 | ['test-assert-colors.js', () => require('./test/pseudo-tty/test-assert-colors.js')], 22 | ['test-assert-no-color.js', () => require('./test/pseudo-tty/test-assert-no-color.js')], 23 | ['test-assert-position-indicator.js', () => require('./test/pseudo-tty/test-assert-position-indicator.js')], 24 | ]; 25 | 26 | testPaths.forEach(([testName, requireTest]) => { 27 | test(testName, (t) => { 28 | t.plan(2); 29 | let result; 30 | t.doesNotThrow(() => { 31 | result = requireTest(); 32 | }); 33 | Promise.resolve(result) 34 | .then(() => false) 35 | .catch((error) => error) 36 | .then((resolved) => t.error(resolved, 'should not resolve to rejected Promise')); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/common/index.js: -------------------------------------------------------------------------------- 1 | const assert = require('../../assert'); 2 | 3 | const getOwnPropertyDescriptors = require('object.getownpropertydescriptors/polyfill')(); 4 | 5 | const objectEntries = require('object.entries/polyfill')(); 6 | 7 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat 8 | function repeat(str, count) { 9 | count = Math.floor(count); 10 | if (str.length == 0 || count == 0) 11 | return ''; 12 | 13 | var maxCount = str.length * count; 14 | count = Math.floor(Math.log(count) / Math.log(2)); 15 | while (count) { 16 | str += str; 17 | count--; 18 | } 19 | str += str.substring(0, maxCount - str.length); 20 | return str; 21 | } 22 | 23 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes 24 | function includes(str, search, start) { 25 | if (typeof start !== 'number') { 26 | start = 0; 27 | } 28 | 29 | if (start + search.length > str.length) { 30 | return false; 31 | } else { 32 | return str.indexOf(search, start) !== -1; 33 | } 34 | } 35 | 36 | const isBrowser = typeof window !== 'undefined'; 37 | 38 | const bigIntSupported = typeof BigInt !== 'undefined'; 39 | const symbolSupported = typeof Symbol !== 'undefined'; 40 | const symbolToStringTagSupported = symbolSupported && typeof Symbol.toStringTag !== 'undefined'; 41 | 42 | const noop = () => {}; 43 | 44 | const mustCallChecks = []; 45 | 46 | function runCallChecks(exitCode) { 47 | if (exitCode !== 0) return; 48 | 49 | const failed = mustCallChecks.filter(function(context) { 50 | if ('minimum' in context) { 51 | context.messageSegment = `at least ${context.minimum}`; 52 | return context.actual < context.minimum; 53 | } else { 54 | context.messageSegment = `exactly ${context.exact}`; 55 | return context.actual !== context.exact; 56 | } 57 | }); 58 | 59 | failed.forEach(function(context) { 60 | console.log('Mismatched %s function calls. Expected %s, actual %d.', 61 | context.name, 62 | context.messageSegment, 63 | context.actual); 64 | console.log(context.stack.split('\n').slice(2).join('\n')); 65 | }); 66 | 67 | if (failed.length) process.exit(1); 68 | } 69 | 70 | function mustCall(fn, exact) { 71 | return _mustCallInner(fn, exact, 'exact'); 72 | } 73 | 74 | function mustCallAtLeast(fn, minimum) { 75 | return _mustCallInner(fn, minimum, 'minimum'); 76 | } 77 | 78 | function _mustCallInner(fn, criteria = 1, field) { 79 | if (process._exiting) 80 | throw new Error('Cannot use common.mustCall*() in process exit handler'); 81 | if (typeof fn === 'number') { 82 | criteria = fn; 83 | fn = noop; 84 | } else if (fn === undefined) { 85 | fn = noop; 86 | } 87 | 88 | if (typeof criteria !== 'number') 89 | throw new TypeError(`Invalid ${field} value: ${criteria}`); 90 | 91 | const context = { 92 | [field]: criteria, 93 | actual: 0, 94 | stack: (new Error()).stack, 95 | name: fn.name || '' 96 | }; 97 | 98 | // Add the exit listener only once to avoid listener leak warnings 99 | if (mustCallChecks.length === 0) process.on('exit', runCallChecks); 100 | 101 | mustCallChecks.push(context); 102 | 103 | return function() { 104 | context.actual++; 105 | return fn.apply(this, arguments); 106 | }; 107 | } 108 | 109 | function mustNotCall(msg) { 110 | return function mustNotCall() { 111 | assert.fail( 112 | `${msg || 'function should not have been called'}`); 113 | }; 114 | } 115 | 116 | function _expectWarning(name, expected, code) { 117 | if (typeof expected === 'string') { 118 | expected = [[expected, code]]; 119 | } else if (!Array.isArray(expected)) { 120 | expected = objectEntries(expected).map(([a, b]) => [b, a]); 121 | } else if (!(Array.isArray(expected[0]))) { 122 | expected = [[expected[0], expected[1]]]; 123 | } 124 | // Deprecation codes are mandatory, everything else is not. 125 | if (name === 'DeprecationWarning') { 126 | expected.forEach(([_, code]) => assert(code, expected)); 127 | } 128 | return mustCall((warning) => { 129 | const [ message, code ] = expected.shift(); 130 | assert.strictEqual(warning.name, name); 131 | if (typeof message === 'string') { 132 | assert.strictEqual(warning.message, message); 133 | } else { 134 | assert(message.test(warning.message)); 135 | } 136 | assert.strictEqual(warning.code, code); 137 | }, expected.length); 138 | } 139 | 140 | let catchWarning; 141 | 142 | // Accepts a warning name and description or array of descriptions or a map of 143 | // warning names to description(s) ensures a warning is generated for each 144 | // name/description pair. 145 | // The expected messages have to be unique per `expectWarning()` call. 146 | function expectWarning(nameOrMap, expected, code) { 147 | if (catchWarning === undefined) { 148 | catchWarning = {}; 149 | // [browserify] Don't bother actually catching warnings for now as it breaks 150 | // the tests when catchWarning[warning.name] is undefined for 151 | // ExperimentalWarning: queueMicrotask() is experimental. 152 | // process.on('warning', (warning) => catchWarning[warning.name](warning)); 153 | } 154 | if (typeof nameOrMap === 'string') { 155 | catchWarning[nameOrMap] = _expectWarning(nameOrMap, expected, code); 156 | } else { 157 | Object.keys(nameOrMap).forEach((name) => { 158 | catchWarning[name] = _expectWarning(name, nameOrMap[name]); 159 | }); 160 | } 161 | } 162 | 163 | class Comparison { 164 | constructor(obj, keys) { 165 | keys.forEach(key => { 166 | if (key in obj) { 167 | this[key] = obj[key]; 168 | } 169 | }); 170 | } 171 | } 172 | 173 | // Useful for testing expected internal/error objects 174 | function expectsError(fn, settings, exact) { 175 | if (typeof fn !== 'function') { 176 | exact = settings; 177 | settings = fn; 178 | fn = undefined; 179 | } 180 | 181 | function innerFn(error) { 182 | if (arguments.length !== 1) { 183 | // Do not use `assert.strictEqual()` to prevent `util.inspect` from 184 | // always being called. 185 | assert.fail(`Expected one argument, got ${util.inspect(arguments)}`); 186 | } 187 | const descriptor = Object.getOwnPropertyDescriptor(error, 'message'); 188 | // The error message should be non-enumerable 189 | assert.strictEqual(descriptor.enumerable, false); 190 | 191 | let innerSettings = settings; 192 | if ('type' in settings) { 193 | const type = settings.type; 194 | if (type !== Error && !Error.isPrototypeOf(type)) { 195 | throw new TypeError('`settings.type` must inherit from `Error`'); 196 | } 197 | let constructor = error.constructor; 198 | if (constructor.name === 'NodeError' && type.name !== 'NodeError') { 199 | constructor = Object.getPrototypeOf(error.constructor); 200 | } 201 | // Add the `type` to the error to properly compare and visualize it. 202 | if (!('type' in error)) 203 | error.type = constructor; 204 | } 205 | 206 | if ('message' in settings && 207 | typeof settings.message === 'object' && 208 | settings.message.test(error.message)) { 209 | // Make a copy so we are able to modify the settings. 210 | innerSettings = Object.create( 211 | settings, getOwnPropertyDescriptors(settings)); 212 | // Visualize the message as identical in case of other errors. 213 | innerSettings.message = error.message; 214 | } 215 | 216 | const isDeepStrictEqual = (actual, expected) => { 217 | const assert = require('../../assert'); 218 | try { 219 | assert.deepStrictEqual(actual, expected); 220 | return true; 221 | } catch(e) { 222 | return false; 223 | } 224 | }; 225 | 226 | // Check all error properties. 227 | const keys = Object.keys(settings); 228 | keys.forEach(key => { 229 | if (!isDeepStrictEqual(error[key], innerSettings[key])) { 230 | // Create placeholder objects to create a nice output. 231 | const a = new Comparison(error, keys); 232 | const b = new Comparison(innerSettings, keys); 233 | 234 | const tmpLimit = Error.stackTraceLimit; 235 | Error.stackTraceLimit = 0; 236 | const err = new assert.AssertionError({ 237 | actual: a, 238 | expected: b, 239 | operator: 'strictEqual', 240 | stackStartFn: assert.throws 241 | }); 242 | Error.stackTraceLimit = tmpLimit; 243 | 244 | throw new assert.AssertionError({ 245 | actual: error, 246 | expected: settings, 247 | operator: 'common.expectsError', 248 | message: err.message 249 | }); 250 | } 251 | 252 | }); 253 | return true; 254 | } 255 | if (fn) { 256 | assert.throws(fn, innerFn); 257 | return; 258 | } 259 | return mustCall(innerFn, exact); 260 | } 261 | 262 | const crashOnUnhandledRejection = (err) => { throw err; }; 263 | process.on('unhandledRejection', crashOnUnhandledRejection); 264 | 265 | module.exports = { 266 | getOwnPropertyDescriptors, 267 | repeat, 268 | includes, 269 | isBrowser, 270 | bigIntSupported, 271 | symbolToStringTagSupported, 272 | mustNotCall, 273 | mustCall, 274 | expectWarning, 275 | expectsError 276 | }; 277 | -------------------------------------------------------------------------------- /test/parallel/test-assert-async.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/parallel/test-assert-async.js 2 | // https://github.com/nodejs/node/commit/2a51ae424a513ec9a6aa3466baa0cc1d55dd4f3b 3 | 4 | // [browserify] 5 | // Most `err.message` and `err.stack` tests are commented out because they are 6 | // inconsistent between browsers. 7 | // 8 | // `err.operator` tests are commented out because its always `undefined` in IE. 9 | // If we drop IE support we can uncomment these tests. 10 | 11 | 'use strict'; 12 | const common = require('../common'); 13 | const assert = require('../../assert'); 14 | 15 | // Run all tests in parallel and check their outcome at the end. 16 | const promises = []; 17 | 18 | // Thenable object without `catch` method, 19 | // shouldn't be considered as a valid Thenable 20 | const invalidThenable = { 21 | then: (fulfill, reject) => { 22 | fulfill(); 23 | }, 24 | }; 25 | 26 | // Function that returns a Thenable function, 27 | // a function with `catch` and `then` methods attached, 28 | // shouldn't be considered as a valid Thenable. 29 | const invalidThenableFunc = () => { 30 | function f() {} 31 | 32 | f.then = (fulfill, reject) => { 33 | fulfill(); 34 | }; 35 | f.catch = () => {}; 36 | 37 | return f; 38 | }; 39 | 40 | // Test assert.rejects() and assert.doesNotReject() by checking their 41 | // expected output and by verifying that they do not work sync 42 | 43 | // Check `assert.rejects`. 44 | { 45 | const rejectingFn = () => Promise.resolve().then(() => assert.fail()); 46 | const errObj = { 47 | code: 'ERR_ASSERTION', 48 | name: 'AssertionError', 49 | message: 'Failed' 50 | }; 51 | 52 | // `assert.rejects` accepts a function or a promise 53 | // or a thenable as first argument. 54 | promises.push(assert.rejects(rejectingFn, errObj)); 55 | promises.push(assert.rejects(rejectingFn(), errObj)); 56 | 57 | const validRejectingThenable = { 58 | then: (fulfill, reject) => { 59 | reject({ code: 'FAIL' }); 60 | }, 61 | catch: () => {} 62 | }; 63 | promises.push(assert.rejects(validRejectingThenable, { code: 'FAIL' })); 64 | 65 | // `assert.rejects` should not accept thenables that 66 | // use a function as `obj` and that have no `catch` handler. 67 | promises.push(assert.rejects( 68 | assert.rejects(invalidThenable, {}), 69 | { 70 | code: 'ERR_INVALID_ARG_TYPE' 71 | }) 72 | ); 73 | promises.push(assert.rejects( 74 | assert.rejects(invalidThenableFunc, {}), 75 | { 76 | code: 'ERR_INVALID_RETURN_VALUE' 77 | }) 78 | ); 79 | } 80 | 81 | { 82 | const handler = (err) => { 83 | assert(err instanceof assert.AssertionError, 84 | `${err.name} is not instance of AssertionError`); 85 | assert.strictEqual(err.code, 'ERR_ASSERTION'); 86 | // assert.strictEqual(err.message, 87 | // 'Missing expected rejection (mustNotCall).'); 88 | // assert.strictEqual(err.operator, 'rejects'); 89 | assert.ok(!common.includes(err.stack, 'at Function.rejects')); 90 | return true; 91 | }; 92 | 93 | let promise = assert.rejects(() => Promise.resolve({}), common.mustNotCall()); 94 | promises.push(assert.rejects(promise, common.mustCall(handler))); 95 | 96 | promise = assert.rejects(() => {}, common.mustNotCall()); 97 | promises.push(assert.rejects(promise, { 98 | name: 'TypeError', 99 | code: 'ERR_INVALID_RETURN_VALUE', 100 | message: 'Expected instance of Promise to be returned ' + 101 | 'from the "promiseFn" function but got type undefined.' 102 | })); 103 | 104 | promise = assert.rejects(Promise.resolve(), common.mustNotCall()); 105 | promises.push(assert.rejects(promise, common.mustCall(handler))); 106 | } 107 | 108 | { 109 | const THROWN_ERROR = new Error(); 110 | 111 | promises.push(assert.rejects(() => { 112 | throw THROWN_ERROR; 113 | }, {}).catch(common.mustCall((err) => { 114 | assert.strictEqual(err, THROWN_ERROR); 115 | }))); 116 | } 117 | 118 | promises.push(assert.rejects( 119 | assert.rejects('fail', {}), 120 | { 121 | code: 'ERR_INVALID_ARG_TYPE', 122 | message: 'The "promiseFn" argument must be one of type ' + 123 | 'Function or Promise. Received type string' 124 | } 125 | )); 126 | 127 | // Check `assert.doesNotReject`. 128 | { 129 | // `assert.doesNotReject` accepts a function or a promise 130 | // or a thenable as first argument. 131 | const promise = assert.doesNotReject(() => new Map(), common.mustNotCall()); 132 | promises.push(assert.rejects(promise, { 133 | // message: 'Expected instance of Promise to be returned ' + 134 | // 'from the "promiseFn" function but got instance of Map.', 135 | code: 'ERR_INVALID_RETURN_VALUE', 136 | name: 'TypeError' 137 | })); 138 | promises.push(assert.doesNotReject(() => Promise.resolve({}))); 139 | promises.push(assert.doesNotReject(Promise.resolve())); 140 | 141 | // `assert.doesNotReject` should not accept thenables that 142 | // use a function as `obj` and that have no `catch` handler. 143 | const validFulfillingThenable = { 144 | then: (fulfill, reject) => { 145 | fulfill(); 146 | }, 147 | catch: () => {} 148 | }; 149 | promises.push(assert.doesNotReject(validFulfillingThenable)); 150 | promises.push(assert.rejects( 151 | assert.doesNotReject(invalidThenable), 152 | { 153 | code: 'ERR_INVALID_ARG_TYPE' 154 | }) 155 | ); 156 | promises.push(assert.rejects( 157 | assert.doesNotReject(invalidThenableFunc), 158 | { 159 | code: 'ERR_INVALID_RETURN_VALUE' 160 | }) 161 | ); 162 | } 163 | 164 | { 165 | const handler1 = (err) => { 166 | assert(err instanceof assert.AssertionError, 167 | `${err.name} is not instance of AssertionError`); 168 | assert.strictEqual(err.code, 'ERR_ASSERTION'); 169 | assert.strictEqual(err.message, 'Failed'); 170 | return true; 171 | }; 172 | const handler2 = (err) => { 173 | assert(err instanceof assert.AssertionError, 174 | `${err.name} is not instance of AssertionError`); 175 | assert.strictEqual(err.code, 'ERR_ASSERTION'); 176 | // assert.strictEqual(err.message, 177 | // 'Got unwanted rejection.\nActual message: "Failed"'); 178 | // assert.strictEqual(err.operator, 'doesNotReject'); 179 | assert.ok(err.stack); 180 | assert.ok(!common.includes(err.stack, 'at Function.doesNotReject')); 181 | return true; 182 | }; 183 | 184 | const rejectingFn = () => Promise.resolve().then(() => assert.fail()); 185 | 186 | let promise = assert.doesNotReject(rejectingFn, common.mustCall(handler1)); 187 | promises.push(assert.rejects(promise, common.mustCall(handler2))); 188 | 189 | promise = assert.doesNotReject(rejectingFn(), common.mustCall(handler1)); 190 | promises.push(assert.rejects(promise, common.mustCall(handler2))); 191 | 192 | promise = assert.doesNotReject(() => assert.fail(), common.mustNotCall()); 193 | promises.push(assert.rejects(promise, common.mustCall(handler1))); 194 | } 195 | 196 | promises.push(assert.rejects( 197 | assert.doesNotReject(123), 198 | { 199 | code: 'ERR_INVALID_ARG_TYPE', 200 | message: 'The "promiseFn" argument must be one of type ' + 201 | 'Function or Promise. Received type number' 202 | } 203 | )); 204 | 205 | { 206 | const handler = (generated, actual, err) => { 207 | assert.strictEqual(err.generatedMessage, generated); 208 | assert.strictEqual(err.code, 'ERR_ASSERTION'); 209 | assert.strictEqual(err.actual, actual); 210 | // assert.strictEqual(err.operator, 'rejects'); 211 | // assert(/rejects/.test(err.stack)); 212 | return true; 213 | }; 214 | const err = new Error(); 215 | promises.push(assert.rejects( 216 | assert.rejects(Promise.reject(null), { code: 'FOO' }), 217 | handler.bind(null, true, null) 218 | )); 219 | promises.push(assert.rejects( 220 | assert.rejects(Promise.reject(5), { code: 'FOO' }, 'AAAAA'), 221 | handler.bind(null, false, 5) 222 | )); 223 | promises.push(assert.rejects( 224 | assert.rejects(Promise.reject(err), { code: 'FOO' }, 'AAAAA'), 225 | handler.bind(null, false, err) 226 | )); 227 | } 228 | 229 | // Make sure all async code gets properly executed. 230 | module.exports = Promise.all(promises).then(common.mustCall()); 231 | -------------------------------------------------------------------------------- /test/parallel/test-assert-checktag.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/parallel/test-assert-checktag.js 2 | // https://github.com/nodejs/node/commit/7493db21b667ed746d39c9b54357eac4287232e3 3 | 4 | // [browserify] 5 | // Most `err.message` tests are commented out because they are 6 | // inconsistent between browsers. 7 | 8 | 'use strict'; 9 | const {isBrowser} = require('../common'); 10 | const assert = require('../../assert'); 11 | 12 | const isNode14 = process.versions.node.indexOf('.') > 1 && process.versions.node.slice(0, 2) >= 14; 13 | const isNode15 = isNode14 && process.versions.node.slice(0, 2) >= 15; 14 | 15 | // Disable colored output to prevent color codes from breaking assertion 16 | // message comparisons. This should only be an issue when process.stdout 17 | // is a TTY. 18 | if (process.stdout && process.stdout.isTTY) 19 | process.env.NODE_DISABLE_COLORS = '1'; 20 | 21 | // Turn off no-restricted-properties because we are testing deepEqual! 22 | 23 | 24 | // See https://github.com/nodejs/node/issues/10258 25 | { 26 | const date = new Date('2016'); 27 | function FakeDate() {} 28 | FakeDate.prototype = Date.prototype; 29 | const fake = new FakeDate(); 30 | 31 | assert.notDeepEqual(date, fake); 32 | assert.notDeepEqual(fake, date); 33 | 34 | // For deepStrictEqual we check the runtime type, 35 | // then reveal the fakeness of the fake date 36 | assert.throws( 37 | () => assert.deepStrictEqual(date, fake), 38 | // { 39 | // message: 'Expected values to be strictly deep-equal:\n' + 40 | // '+ actual - expected\n\n+ 2016-01-01T00:00:00.000Z\n- Date {}' 41 | // } 42 | ); 43 | assert.throws( 44 | () => assert.deepStrictEqual(fake, date), 45 | // { 46 | // message: 'Expected values to be strictly deep-equal:\n' + 47 | // '+ actual - expected\n\n+ Date {}\n- 2016-01-01T00:00:00.000Z' 48 | // } 49 | ); 50 | } 51 | 52 | if (!isBrowser && isNode14) { // At the moment global has its own type tag 53 | const fakeGlobal = {}; 54 | Object.setPrototypeOf(fakeGlobal, Object.getPrototypeOf(global)); 55 | Object.keys(global).forEach(prop => { 56 | fakeGlobal[prop] = global[prop]; 57 | }); 58 | assert.notDeepEqual(fakeGlobal, global); 59 | // Message will be truncated anyway, don't validate 60 | assert.throws(() => assert.deepStrictEqual(fakeGlobal, global), 61 | assert.AssertionError); 62 | } 63 | 64 | 65 | if (!isBrowser && isNode15) { // At the moment process has its own type tag 66 | const fakeProcess = {}; 67 | Object.setPrototypeOf(fakeProcess, Object.getPrototypeOf(process)); 68 | Object.keys(process).forEach(prop => { 69 | fakeProcess[prop] = process[prop]; 70 | }); 71 | assert.notDeepEqual(fakeProcess, process); 72 | // Message will be truncated anyway, don't validate 73 | assert.throws(() => assert.deepStrictEqual(fakeProcess, process), 74 | assert.AssertionError); 75 | } 76 | /* eslint-enable */ 77 | -------------------------------------------------------------------------------- /test/parallel/test-assert-deep.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/parallel/test-assert-deep.js 2 | // https://github.com/nodejs/node/commit/1ed3c54ecbd72a33693e5954f86bcc9fd9b1cc09 3 | 4 | // [browserify] 5 | // Most `err.message` tests are commented out because they are 6 | // inconsistent between browsers. 7 | 8 | 'use strict'; 9 | 10 | const common = require('../common'); 11 | const assert = require('../../assert'); 12 | const util = require('util/'); 13 | const { AssertionError } = assert; 14 | const defaultMsgStart = 'Expected values to be strictly deep-equal:\n'; 15 | const defaultMsgStartFull = `${defaultMsgStart}+ actual - expected`; 16 | 17 | const objectEntries = require('object.entries'); 18 | 19 | const arrayFromSet = set => { 20 | const array = []; 21 | set.forEach(value => array.push(value)); 22 | 23 | return array; 24 | }; 25 | 26 | // Disable colored output to prevent color codes from breaking assertion 27 | // message comparisons. This should only be an issue when process.stdout 28 | // is a TTY. 29 | if (process.stdout && process.stdout.isTTY) 30 | process.env.NODE_DISABLE_COLORS = '1'; 31 | 32 | // Allows us to create Sets/Maps from arrays in IE 33 | const createSet = (values = []) => { 34 | const set = new Set(); 35 | values.forEach(value => set.add(value)); 36 | 37 | return set; 38 | }; 39 | 40 | const createMap = (values = []) => { 41 | const map = new Map(); 42 | values.forEach(([key, value]) => map.set(key, value)); 43 | 44 | return map; 45 | }; 46 | 47 | // Template tag function turning an error message into a RegExp 48 | // for assert.throws() 49 | function re(literals, ...values) { 50 | let result = 'Expected values to be loosely deep-equal:\n\n'; 51 | values.forEach((value, i) => { 52 | const str = util.inspect(value, { 53 | compact: false, 54 | depth: 1000, 55 | customInspect: false, 56 | maxArrayLength: Infinity, 57 | breakLength: Infinity, 58 | sorted: true, 59 | getters: true 60 | }); 61 | // Need to escape special characters. 62 | result += str; 63 | result += literals[i + 1]; 64 | }); 65 | return { 66 | code: 'ERR_ASSERTION', 67 | message: result 68 | }; 69 | } 70 | 71 | // The following deepEqual tests might seem very weird. 72 | // They just describe what it is now. 73 | // That is why we discourage using deepEqual in our own tests. 74 | 75 | // Turn off no-restricted-properties because we are testing deepEqual! 76 | /* eslint-disable no-restricted-properties */ 77 | 78 | const arr = new Uint8Array([120, 121, 122, 10]); 79 | const buf = Buffer.from(arr); 80 | // They have different [[Prototype]] 81 | assert.throws( 82 | () => assert.deepStrictEqual(arr, buf), 83 | // { 84 | // code: 'ERR_ASSERTION', 85 | // message: `${defaultMsgStartFull} ... Lines skipped\n\n` + 86 | // '+ Uint8Array [\n' + 87 | // '- Buffer [Uint8Array] [\n 120,\n...\n 10\n ]' 88 | // } 89 | ); 90 | assert.deepEqual(arr, buf); 91 | 92 | { 93 | const buf2 = Buffer.from(arr); 94 | buf2.prop = 1; 95 | 96 | assert.throws( 97 | () => assert.deepStrictEqual(buf2, buf), 98 | { 99 | code: 'ERR_ASSERTION', 100 | // message: `${defaultMsgStartFull} ... Lines skipped\n\n` + 101 | // ' Buffer [Uint8Array] [\n' + 102 | // ' 120,\n' + 103 | // '...\n' + 104 | // ' 10,\n' + 105 | // '+ prop: 1\n' + 106 | // ' ]' 107 | } 108 | ); 109 | assert.notDeepEqual(buf2, buf); 110 | } 111 | 112 | { 113 | const arr2 = new Uint8Array([120, 121, 122, 10]); 114 | arr2.prop = 5; 115 | assert.throws( 116 | () => assert.deepStrictEqual(arr, arr2), 117 | { 118 | code: 'ERR_ASSERTION', 119 | // message: `${defaultMsgStartFull} ... Lines skipped\n\n` + 120 | // ' Uint8Array [\n' + 121 | // ' 120,\n' + 122 | // '...\n' + 123 | // ' 10,\n' + 124 | // '- prop: 5\n' + 125 | // ' ]' 126 | } 127 | ); 128 | assert.notDeepEqual(arr, arr2); 129 | } 130 | 131 | const date = new Date('2016'); 132 | 133 | class MyDate extends Date { 134 | constructor(...args) { 135 | super(...args); 136 | this[0] = '1'; 137 | } 138 | } 139 | 140 | const date2 = new MyDate('2016'); 141 | 142 | // deepEqual returns true as long as the time are the same, 143 | // but deepStrictEqual checks own properties 144 | assert.notDeepEqual(date, date2); 145 | assert.notDeepEqual(date2, date); 146 | assert.throws( 147 | () => assert.deepStrictEqual(date, date2), 148 | { 149 | code: 'ERR_ASSERTION', 150 | // message: `${defaultMsgStartFull}\n\n` + 151 | // '+ 2016-01-01T00:00:00.000Z\n- MyDate 2016-01-01T00:00:00.000Z' + 152 | // " {\n- '0': '1'\n- }" 153 | } 154 | ); 155 | assert.throws( 156 | () => assert.deepStrictEqual(date2, date), 157 | { 158 | code: 'ERR_ASSERTION', 159 | // message: `${defaultMsgStartFull}\n\n` + 160 | // '+ MyDate 2016-01-01T00:00:00.000Z {\n' + 161 | // "+ '0': '1'\n+ }\n- 2016-01-01T00:00:00.000Z" 162 | } 163 | ); 164 | 165 | class MyRegExp extends RegExp { 166 | constructor(...args) { 167 | super(...args); 168 | this[0] = '1'; 169 | } 170 | } 171 | 172 | const re1 = new RegExp('test'); 173 | const re2 = new MyRegExp('test'); 174 | 175 | // deepEqual returns true as long as the regexp-specific properties 176 | // are the same, but deepStrictEqual checks all properties 177 | assert.notDeepEqual(re1, re2); 178 | assert.throws( 179 | () => assert.deepStrictEqual(re1, re2), 180 | { 181 | code: 'ERR_ASSERTION', 182 | // message: `${defaultMsgStartFull}\n\n` + 183 | // "+ /test/\n- MyRegExp /test/ {\n- '0': '1'\n- }" 184 | } 185 | ); 186 | 187 | // For these weird cases, deepEqual should pass (at least for now), 188 | // but deepStrictEqual should throw. 189 | { 190 | const similar = createSet([ 191 | { 0: 1 }, // Object 192 | new String('1'), // Object 193 | [1], // Array 194 | date2, // Date with this[0] = '1' 195 | re2, // RegExp with this[0] = '1' 196 | new Int8Array([1]), // Int8Array 197 | new Int16Array([1]), // Int16Array 198 | new Uint16Array([1]), // Uint16Array 199 | new Int32Array([1]), // Int32Array 200 | new Uint32Array([1]), // Uint32Array 201 | Buffer.from([1]), // Uint8Array 202 | (function() { return arguments; })(1) 203 | ]); 204 | 205 | arrayFromSet(similar).forEach(a => { 206 | arrayFromSet(similar).forEach(b => { 207 | if (a !== b) { 208 | assert.notDeepEqual(a, b); 209 | assert.throws( 210 | () => assert.deepStrictEqual(a, b), 211 | { code: 'ERR_ASSERTION' } 212 | ); 213 | } 214 | }); 215 | }); 216 | } 217 | 218 | function assertDeepAndStrictEqual(a, b) { 219 | assert.deepEqual(a, b); 220 | assert.deepStrictEqual(a, b); 221 | 222 | assert.deepEqual(b, a); 223 | assert.deepStrictEqual(b, a); 224 | } 225 | 226 | function assertNotDeepOrStrict(a, b, err) { 227 | assert.throws( 228 | () => assert.deepEqual(a, b), 229 | err || re`${a}\n\nshould equal\n\n${b}` 230 | ); 231 | assert.throws( 232 | () => assert.deepStrictEqual(a, b), 233 | err || { code: 'ERR_ASSERTION' } 234 | ); 235 | 236 | assert.throws( 237 | () => assert.deepEqual(b, a), 238 | err || re`${b}\n\nshould equal\n\n${a}` 239 | ); 240 | assert.throws( 241 | () => assert.deepStrictEqual(b, a), 242 | err || { code: 'ERR_ASSERTION' } 243 | ); 244 | } 245 | 246 | function assertOnlyDeepEqual(a, b, err) { 247 | assert.deepEqual(a, b); 248 | assert.throws( 249 | () => assert.deepStrictEqual(a, b), 250 | err || { code: 'ERR_ASSERTION' } 251 | ); 252 | 253 | assert.deepEqual(b, a); 254 | assert.throws( 255 | () => assert.deepStrictEqual(b, a), 256 | err || { code: 'ERR_ASSERTION' } 257 | ); 258 | } 259 | 260 | // es6 Maps and Sets 261 | assertDeepAndStrictEqual(createSet(), createSet()); 262 | assertDeepAndStrictEqual(createMap(), createMap()); 263 | 264 | assertDeepAndStrictEqual(createSet([1, 2, 3]), createSet([1, 2, 3])); 265 | assertNotDeepOrStrict(createSet([1, 2, 3]), createSet([1, 2, 3, 4])); 266 | assertNotDeepOrStrict(createSet([1, 2, 3, 4]), createSet([1, 2, 3])); 267 | assertDeepAndStrictEqual(createSet(['1', '2', '3']), createSet(['1', '2', '3'])); 268 | assertDeepAndStrictEqual(createSet([[1, 2], [3, 4]]), createSet([[3, 4], [1, 2]])); 269 | assertNotDeepOrStrict(createSet([{ a: 0 }]), createSet([{ a: 1 }])); 270 | if (common.symbolSupported) { 271 | assertNotDeepOrStrict(createSet([Symbol()]), createSet([Symbol()])); 272 | } 273 | 274 | { 275 | const a = [ 1, 2 ]; 276 | const b = [ 3, 4 ]; 277 | const c = [ 1, 2 ]; 278 | const d = [ 3, 4 ]; 279 | 280 | assertDeepAndStrictEqual( 281 | { a: a, b: b, s: createSet([a, b]) }, 282 | { a: c, b: d, s: createSet([d, c]) } 283 | ); 284 | } 285 | 286 | assertDeepAndStrictEqual(createMap([[1, 1], [2, 2]]), createMap([[1, 1], [2, 2]])); 287 | assertDeepAndStrictEqual(createMap([[1, 1], [2, 2]]), createMap([[2, 2], [1, 1]])); 288 | assertNotDeepOrStrict(createMap([[1, 1], [2, 2]]), createMap([[1, 2], [2, 1]])); 289 | assertNotDeepOrStrict( 290 | createMap([[[1], 1], [{}, 2]]), 291 | createMap([[[1], 2], [{}, 1]]) 292 | ); 293 | 294 | assertNotDeepOrStrict(createSet([1]), [1]); 295 | assertNotDeepOrStrict(createSet(), []); 296 | assertNotDeepOrStrict(createSet(), {}); 297 | 298 | assertNotDeepOrStrict(createMap([['a', 1]]), { a: 1 }); 299 | assertNotDeepOrStrict(createMap(), []); 300 | assertNotDeepOrStrict(createMap(), {}); 301 | 302 | assertOnlyDeepEqual(createSet(['1']), createSet([1])); 303 | 304 | assertOnlyDeepEqual(createMap([['1', 'a']]), createMap([[1, 'a']])); 305 | assertOnlyDeepEqual(createMap([['a', '1']]), createMap([['a', 1]])); 306 | assertNotDeepOrStrict(createMap([['a', '1']]), createMap([['a', 2]])); 307 | 308 | assertDeepAndStrictEqual(createSet([{}]), createSet([{}])); 309 | 310 | // Ref: https://github.com/nodejs/node/issues/13347 311 | assertNotDeepOrStrict( 312 | createSet([{ a: 1 }, { a: 1 }]), 313 | createSet([{ a: 1 }, { a: 2 }]) 314 | ); 315 | assertNotDeepOrStrict( 316 | createSet([{ a: 1 }, { a: 1 }, { a: 2 }]), 317 | createSet([{ a: 1 }, { a: 2 }, { a: 2 }]) 318 | ); 319 | assertNotDeepOrStrict( 320 | createMap([[{ x: 1 }, 5], [{ x: 1 }, 5]]), 321 | createMap([[{ x: 1 }, 5], [{ x: 2 }, 5]]) 322 | ); 323 | 324 | assertNotDeepOrStrict(createSet([3, '3']), createSet([3, 4])); 325 | assertNotDeepOrStrict(createMap([[3, 0], ['3', 0]]), createMap([[3, 0], [4, 0]])); 326 | 327 | assertNotDeepOrStrict( 328 | createSet([{ a: 1 }, { a: 1 }, { a: 2 }]), 329 | createSet([{ a: 1 }, { a: 2 }, { a: 2 }]) 330 | ); 331 | 332 | // Mixed primitive and object keys 333 | assertDeepAndStrictEqual( 334 | createMap([[1, 'a'], [{}, 'a']]), 335 | createMap([[1, 'a'], [{}, 'a']]) 336 | ); 337 | assertDeepAndStrictEqual( 338 | createSet([1, 'a', [{}, 'a']]), 339 | createSet([1, 'a', [{}, 'a']]) 340 | ); 341 | 342 | // This is an awful case, where a map contains multiple equivalent keys: 343 | assertOnlyDeepEqual( 344 | createMap([[1, 'a'], ['1', 'b']]), 345 | createMap([['1', 'a'], [true, 'b']]) 346 | ); 347 | assertNotDeepOrStrict( 348 | createSet(['a']), 349 | createSet(['b']) 350 | ); 351 | assertDeepAndStrictEqual( 352 | createMap([[{}, 'a'], [{}, 'b']]), 353 | createMap([[{}, 'b'], [{}, 'a']]) 354 | ); 355 | assertOnlyDeepEqual( 356 | createMap([[true, 'a'], ['1', 'b'], [1, 'a']]), 357 | createMap([['1', 'a'], [1, 'b'], [true, 'a']]) 358 | ); 359 | assertNotDeepOrStrict( 360 | createMap([[true, 'a'], ['1', 'b'], [1, 'c']]), 361 | createMap([['1', 'a'], [1, 'b'], [true, 'a']]) 362 | ); 363 | 364 | // Similar object keys 365 | assertNotDeepOrStrict( 366 | createSet([{}, {}]), 367 | createSet([{}, 1]) 368 | ); 369 | assertNotDeepOrStrict( 370 | createSet([[{}, 1], [{}, 1]]), 371 | createSet([[{}, 1], [1, 1]]) 372 | ); 373 | assertNotDeepOrStrict( 374 | createMap([[{}, 1], [{}, 1]]), 375 | createMap([[{}, 1], [1, 1]]) 376 | ); 377 | assertOnlyDeepEqual( 378 | createMap([[{}, 1], [true, 1]]), 379 | createMap([[{}, 1], [1, 1]]) 380 | ); 381 | 382 | // Similar primitive key / values 383 | assertNotDeepOrStrict( 384 | createSet([1, true, false]), 385 | createSet(['1', 0, '0']) 386 | ); 387 | assertNotDeepOrStrict( 388 | createMap([[1, 5], [true, 5], [false, 5]]), 389 | createMap([['1', 5], [0, 5], ['0', 5]]) 390 | ); 391 | 392 | // Undefined value in Map 393 | assertDeepAndStrictEqual( 394 | createMap([[1, undefined]]), 395 | createMap([[1, undefined]]) 396 | ); 397 | assertOnlyDeepEqual( 398 | createMap([[1, null], ['', '0']]), 399 | createMap([['1', undefined], [false, 0]]) 400 | ); 401 | assertNotDeepOrStrict( 402 | createMap([[1, undefined]]), 403 | createMap([[2, undefined]]) 404 | ); 405 | 406 | // null as key 407 | assertDeepAndStrictEqual( 408 | createMap([[null, 3]]), 409 | createMap([[null, 3]]) 410 | ); 411 | 412 | if (common.bigIntSupported) { 413 | assertOnlyDeepEqual( 414 | createMap([[undefined, null], eval("['+000', 2n]")]), 415 | createMap([[null, undefined], [false, '2']]), 416 | ); 417 | 418 | assertOnlyDeepEqual( 419 | createSet(eval("[null, '', 1n, 5, 2n, false]")), 420 | createSet(eval("[undefined, 0, 5n, true, '2', '-000']")) 421 | ); 422 | } 423 | 424 | assertNotDeepOrStrict( 425 | createSet(['']), 426 | createSet(['0']) 427 | ); 428 | assertOnlyDeepEqual( 429 | createMap([[1, {}]]), 430 | createMap([[true, {}]]) 431 | ); 432 | assertOnlyDeepEqual( 433 | createMap([[undefined, true]]), 434 | createMap([[null, true]]) 435 | ); 436 | assertNotDeepOrStrict( 437 | createMap([[undefined, true]]), 438 | createMap([[true, true]]) 439 | ); 440 | 441 | // GH-6416. Make sure circular refs don't throw. 442 | { 443 | const b = {}; 444 | b.b = b; 445 | const c = {}; 446 | c.b = c; 447 | 448 | assertDeepAndStrictEqual(b, c); 449 | 450 | const d = {}; 451 | d.a = 1; 452 | d.b = d; 453 | const e = {}; 454 | e.a = 1; 455 | e.b = {}; 456 | 457 | assertNotDeepOrStrict(d, e); 458 | } 459 | 460 | // GH-14441. Circular structures should be consistent 461 | { 462 | const a = {}; 463 | const b = {}; 464 | a.a = a; 465 | b.a = {}; 466 | b.a.a = a; 467 | assertDeepAndStrictEqual(a, b); 468 | } 469 | 470 | { 471 | const a = createSet(); 472 | const b = createSet(); 473 | const c = createSet(); 474 | a.add(a); 475 | b.add(b); 476 | c.add(a); 477 | assertDeepAndStrictEqual(b, c); 478 | } 479 | 480 | // https://github.com/nodejs/node-v0.x-archive/pull/7178 481 | // Ensure reflexivity of deepEqual with `arguments` objects. 482 | { 483 | const args = (function() { return arguments; })(); 484 | assertNotDeepOrStrict([], args); 485 | } 486 | 487 | // More checking that arguments objects are handled correctly 488 | { 489 | // eslint-disable-next-line func-style 490 | const returnArguments = function() { return arguments; }; 491 | 492 | const someArgs = returnArguments('a'); 493 | const sameArgs = returnArguments('a'); 494 | const diffArgs = returnArguments('b'); 495 | 496 | assertNotDeepOrStrict(someArgs, ['a']); 497 | assertNotDeepOrStrict(someArgs, { '0': 'a' }); 498 | assertNotDeepOrStrict(someArgs, diffArgs); 499 | assertDeepAndStrictEqual(someArgs, sameArgs); 500 | } 501 | 502 | { 503 | const values = [ 504 | 123, 505 | Infinity, 506 | 0, 507 | null, 508 | undefined, 509 | false, 510 | true, 511 | {}, 512 | [], 513 | () => {}, 514 | ]; 515 | assertDeepAndStrictEqual(createSet(values), createSet(values)); 516 | assertDeepAndStrictEqual(createSet(values), createSet(values.reverse())); 517 | 518 | const mapValues = values.map((v) => [v, { a: 5 }]); 519 | assertDeepAndStrictEqual(createMap(mapValues), createMap(mapValues)); 520 | assertDeepAndStrictEqual(createMap(mapValues), createMap(mapValues.reverse())); 521 | } 522 | 523 | { 524 | const s1 = createSet(); 525 | const s2 = createSet(); 526 | s1.add(1); 527 | s1.add(2); 528 | s2.add(2); 529 | s2.add(1); 530 | assertDeepAndStrictEqual(s1, s2); 531 | } 532 | 533 | { 534 | const m1 = createMap(); 535 | const m2 = createMap(); 536 | const obj = { a: 5, b: 6 }; 537 | m1.set(1, obj); 538 | m1.set(2, 'hi'); 539 | m1.set(3, [1, 2, 3]); 540 | 541 | m2.set(2, 'hi'); // different order 542 | m2.set(1, obj); 543 | m2.set(3, [1, 2, 3]); // Deep equal, but not reference equal. 544 | 545 | assertDeepAndStrictEqual(m1, m2); 546 | } 547 | 548 | { 549 | const m1 = createMap(); 550 | const m2 = createMap(); 551 | 552 | // m1 contains itself. 553 | m1.set(1, m1); 554 | m2.set(1, createMap()); 555 | 556 | assertNotDeepOrStrict(m1, m2); 557 | } 558 | 559 | { 560 | const map1 = createMap([[1, 1]]); 561 | const map2 = createMap([[1, '1']]); 562 | assert.deepEqual(map1, map2); 563 | assert.throws( 564 | () => assert.deepStrictEqual(map1, map2), 565 | { 566 | code: 'ERR_ASSERTION', 567 | // message: `${defaultMsgStartFull}\n\n` + 568 | // " Map {\n+ 1 => 1\n- 1 => '1'\n }" 569 | } 570 | ); 571 | } 572 | 573 | { 574 | // Two equivalent sets / maps with different key/values applied shouldn't be 575 | // the same. This is a terrible idea to do in practice, but deepEqual should 576 | // still check for it. 577 | const s1 = createSet(); 578 | const s2 = createSet(); 579 | s1.x = 5; 580 | assertNotDeepOrStrict(s1, s2); 581 | 582 | const m1 = createMap(); 583 | const m2 = createMap(); 584 | m1.x = 5; 585 | assertNotDeepOrStrict(m1, m2); 586 | } 587 | 588 | { 589 | // Circular references. 590 | const s1 = createSet(); 591 | s1.add(s1); 592 | const s2 = createSet(); 593 | s2.add(s2); 594 | assertDeepAndStrictEqual(s1, s2); 595 | 596 | const m1 = createMap(); 597 | m1.set(2, m1); 598 | const m2 = createMap(); 599 | m2.set(2, m2); 600 | assertDeepAndStrictEqual(m1, m2); 601 | 602 | const m3 = createMap(); 603 | m3.set(m3, 2); 604 | const m4 = createMap(); 605 | m4.set(m4, 2); 606 | assertDeepAndStrictEqual(m3, m4); 607 | } 608 | 609 | // Handle sparse arrays. 610 | { 611 | assertDeepAndStrictEqual([1, , , 3], [1, , , 3]); 612 | assertNotDeepOrStrict([1, , , 3], [1, , , 3, , , ]); 613 | const a = new Array(3); 614 | const b = new Array(3); 615 | a[2] = true; 616 | b[1] = true; 617 | assertNotDeepOrStrict(a, b); 618 | b[2] = true; 619 | assertNotDeepOrStrict(a, b); 620 | a[0] = true; 621 | assertNotDeepOrStrict(a, b); 622 | } 623 | 624 | // Handle different error messages 625 | { 626 | const err1 = new Error('foo1'); 627 | assertNotDeepOrStrict(err1, new Error('foo2'), assert.AssertionError); 628 | assertNotDeepOrStrict(err1, new TypeError('foo1'), assert.AssertionError); 629 | // [browserify] Objects will not be strictly equal in some browsers due to 630 | // different `line`/`column` properties 631 | // assertDeepAndStrictEqual(err1, new Error('foo1')); 632 | assertNotDeepOrStrict(err1, {}, AssertionError); 633 | } 634 | 635 | // Handle NaN 636 | assert.notDeepEqual(NaN, NaN); 637 | assert.deepStrictEqual(NaN, NaN); 638 | assert.deepStrictEqual({ a: NaN }, { a: NaN }); 639 | assert.deepStrictEqual([ 1, 2, NaN, 4 ], [ 1, 2, NaN, 4 ]); 640 | 641 | // Handle boxed primitives 642 | if (common.symbolSupported) { 643 | const boxedString = new String('test'); 644 | const boxedSymbol = Object(Symbol()); 645 | assertNotDeepOrStrict(new Boolean(true), Object(false)); 646 | assertNotDeepOrStrict(Object(true), new Number(1)); 647 | assertNotDeepOrStrict(new Number(2), new Number(1)); 648 | assertNotDeepOrStrict(boxedSymbol, Object(Symbol())); 649 | assertNotDeepOrStrict(boxedSymbol, {}); 650 | assertDeepAndStrictEqual(boxedSymbol, boxedSymbol); 651 | assertDeepAndStrictEqual(Object(true), Object(true)); 652 | assertDeepAndStrictEqual(Object(2), Object(2)); 653 | assertDeepAndStrictEqual(boxedString, Object('test')); 654 | boxedString.slow = true; 655 | assertNotDeepOrStrict(boxedString, Object('test')); 656 | boxedSymbol.slow = true; 657 | assertNotDeepOrStrict(boxedSymbol, {}); 658 | } 659 | 660 | // Minus zero 661 | assertOnlyDeepEqual(0, -0); 662 | assertDeepAndStrictEqual(-0, -0); 663 | 664 | // Handle symbols (enumerable only) 665 | if (common.symbolSupported) { 666 | const symbol1 = Symbol(); 667 | const obj1 = { [symbol1]: 1 }; 668 | const obj2 = { [symbol1]: 1 }; 669 | const obj3 = { [Symbol()]: 1 }; 670 | // Add a non enumerable symbol as well. It is going to be ignored! 671 | Object.defineProperty(obj2, Symbol(), { value: 1 }); 672 | assertOnlyDeepEqual(obj1, obj3); 673 | assertDeepAndStrictEqual(obj1, obj2); 674 | obj2[Symbol()] = true; 675 | assertOnlyDeepEqual(obj1, obj2); 676 | // TypedArrays have a fast path. Test for this as well. 677 | const a = new Uint8Array(4); 678 | const b = new Uint8Array(4); 679 | a[symbol1] = true; 680 | b[symbol1] = false; 681 | assertNotDeepOrStrict(a, b); 682 | b[symbol1] = true; 683 | assertDeepAndStrictEqual(a, b); 684 | // The same as TypedArrays is valid for boxed primitives 685 | const boxedStringA = new String('test'); 686 | const boxedStringB = new String('test'); 687 | boxedStringA[symbol1] = true; 688 | assertOnlyDeepEqual(boxedStringA, boxedStringB); 689 | boxedStringA[symbol1] = true; 690 | assertDeepAndStrictEqual(a, b); 691 | } 692 | 693 | assert.deepEqual(new Date(2000, 3, 14), new Date(2000, 3, 14)); 694 | 695 | assert.throws(() => { assert.deepEqual(new Date(), new Date(2000, 3, 14)); }, 696 | AssertionError, 697 | 'deepEqual(new Date(), new Date(2000, 3, 14))'); 698 | 699 | assert.throws( 700 | () => { assert.notDeepEqual(new Date(2000, 3, 14), new Date(2000, 3, 14)); }, 701 | AssertionError, 702 | 'notDeepEqual(new Date(2000, 3, 14), new Date(2000, 3, 14))' 703 | ); 704 | 705 | assert.throws( 706 | () => { assert.notDeepEqual(common.repeat('a', 1024), common.repeat('a', 1024)); }, 707 | AssertionError, 708 | 'notDeepEqual("a".repeat(1024), "a".repeat(1024))' 709 | ); 710 | 711 | assert.notDeepEqual(new Date(), new Date(2000, 3, 14)); 712 | 713 | assertDeepAndStrictEqual(/a/, /a/); 714 | assertDeepAndStrictEqual(/a/g, /a/g); 715 | assertDeepAndStrictEqual(/a/i, /a/i); 716 | assertDeepAndStrictEqual(/a/m, /a/m); 717 | assertDeepAndStrictEqual(/a/igm, /a/igm); 718 | assertNotDeepOrStrict(/ab/, /a/); 719 | assertNotDeepOrStrict(/a/g, /a/); 720 | assertNotDeepOrStrict(/a/i, /a/); 721 | assertNotDeepOrStrict(/a/m, /a/); 722 | assertNotDeepOrStrict(/a/igm, /a/im); 723 | 724 | { 725 | const re1 = /a/g; 726 | re1.lastIndex = 3; 727 | assert.deepEqual(re1, /a/g); 728 | } 729 | 730 | assert.deepEqual(4, '4'); 731 | assert.deepEqual(true, 1); 732 | assert.throws(() => assert.deepEqual(4, '5'), 733 | AssertionError, 734 | 'deepEqual( 4, \'5\')'); 735 | 736 | // Having the same number of owned properties && the same set of keys. 737 | assert.deepEqual({ a: 4 }, { a: 4 }); 738 | assert.deepEqual({ a: 4, b: '2' }, { a: 4, b: '2' }); 739 | assert.deepEqual([4], ['4']); 740 | assert.throws( 741 | () => assert.deepEqual({ a: 4 }, { a: 4, b: true }), AssertionError); 742 | assert.notDeepEqual(['a'], { 0: 'a' }); 743 | assert.deepEqual({ a: 4, b: '1' }, { b: '1', a: 4 }); 744 | const a1 = [1, 2, 3]; 745 | const a2 = [1, 2, 3]; 746 | a1.a = 'test'; 747 | a1.b = true; 748 | a2.b = true; 749 | a2.a = 'test'; 750 | assert.throws(() => assert.deepEqual(Object.keys(a1), Object.keys(a2)), 751 | AssertionError); 752 | assert.deepEqual(a1, a2); 753 | 754 | // Having an identical prototype property. 755 | const nbRoot = { 756 | toString() { return `${this.first} ${this.last}`; } 757 | }; 758 | 759 | function nameBuilder(first, last) { 760 | this.first = first; 761 | this.last = last; 762 | return this; 763 | } 764 | nameBuilder.prototype = nbRoot; 765 | 766 | function nameBuilder2(first, last) { 767 | this.first = first; 768 | this.last = last; 769 | return this; 770 | } 771 | nameBuilder2.prototype = nbRoot; 772 | 773 | const nb1 = new nameBuilder('Ryan', 'Dahl'); 774 | let nb2 = new nameBuilder2('Ryan', 'Dahl'); 775 | 776 | assert.deepEqual(nb1, nb2); 777 | 778 | nameBuilder2.prototype = Object; 779 | nb2 = new nameBuilder2('Ryan', 'Dahl'); 780 | assert.deepEqual(nb1, nb2); 781 | 782 | // Primitives 783 | assertNotDeepOrStrict(null, {}); 784 | assertNotDeepOrStrict(undefined, {}); 785 | assertNotDeepOrStrict('a', ['a']); 786 | assertNotDeepOrStrict('a', { 0: 'a' }); 787 | assertNotDeepOrStrict(1, {}); 788 | assertNotDeepOrStrict(true, {}); 789 | if (common.symbolSupported) { 790 | assertNotDeepOrStrict(Symbol(), {}); 791 | assertNotDeepOrStrict(Symbol(), Symbol()); 792 | } 793 | 794 | assertOnlyDeepEqual(4, '4'); 795 | assertOnlyDeepEqual(true, 1); 796 | 797 | if (common.symbolSupported) { 798 | const s = Symbol(); 799 | assertDeepAndStrictEqual(s, s); 800 | } 801 | 802 | // Primitive wrappers and object. 803 | assertNotDeepOrStrict(new String('a'), ['a']); 804 | assertNotDeepOrStrict(new String('a'), { 0: 'a' }); 805 | assertNotDeepOrStrict(new Number(1), {}); 806 | assertNotDeepOrStrict(new Boolean(true), {}); 807 | 808 | // Same number of keys but different key names. 809 | assertNotDeepOrStrict({ a: 1 }, { b: 1 }); 810 | 811 | assert.deepStrictEqual(new Date(2000, 3, 14), new Date(2000, 3, 14)); 812 | 813 | assert.throws( 814 | () => assert.deepStrictEqual(new Date(), new Date(2000, 3, 14)), 815 | AssertionError, 816 | 'deepStrictEqual(new Date(), new Date(2000, 3, 14))' 817 | ); 818 | 819 | assert.throws( 820 | () => assert.notDeepStrictEqual(new Date(2000, 3, 14), new Date(2000, 3, 14)), 821 | { 822 | name: 'AssertionError', 823 | // message: 'Expected "actual" not to be strictly deep-equal to: ' + 824 | // util.inspect(new Date(2000, 3, 14)) 825 | } 826 | ); 827 | 828 | assert.notDeepStrictEqual(new Date(), new Date(2000, 3, 14)); 829 | 830 | assert.throws( 831 | () => assert.deepStrictEqual(/ab/, /a/), 832 | { 833 | code: 'ERR_ASSERTION', 834 | name: 'AssertionError', 835 | // message: `${defaultMsgStartFull}\n\n+ /ab/\n- /a/` 836 | }); 837 | assert.throws( 838 | () => assert.deepStrictEqual(/a/g, /a/), 839 | { 840 | code: 'ERR_ASSERTION', 841 | name: 'AssertionError', 842 | // message: `${defaultMsgStartFull}\n\n+ /a/g\n- /a/` 843 | }); 844 | assert.throws( 845 | () => assert.deepStrictEqual(/a/i, /a/), 846 | { 847 | code: 'ERR_ASSERTION', 848 | name: 'AssertionError', 849 | // message: `${defaultMsgStartFull}\n\n+ /a/i\n- /a/` 850 | }); 851 | assert.throws( 852 | () => assert.deepStrictEqual(/a/m, /a/), 853 | { 854 | code: 'ERR_ASSERTION', 855 | name: 'AssertionError', 856 | // message: `${defaultMsgStartFull}\n\n+ /a/m\n- /a/` 857 | }); 858 | assert.throws( 859 | () => assert.deepStrictEqual(/a/igm, /a/im), 860 | { 861 | code: 'ERR_ASSERTION', 862 | name: 'AssertionError', 863 | // message: `${defaultMsgStartFull}\n\n+ /a/gim\n- /a/im\n ^` 864 | }); 865 | 866 | { 867 | const re1 = /a/; 868 | re1.lastIndex = 3; 869 | assert.deepStrictEqual(re1, /a/); 870 | } 871 | 872 | assert.throws( 873 | () => assert.deepStrictEqual(4, '4'), 874 | // { message: `${defaultMsgStart}\n4 !== '4'\n` } 875 | ); 876 | 877 | assert.throws( 878 | () => assert.deepStrictEqual(true, 1), 879 | // { message: `${defaultMsgStart}\ntrue !== 1\n` } 880 | ); 881 | 882 | // Having the same number of owned properties && the same set of keys. 883 | assert.deepStrictEqual({ a: 4 }, { a: 4 }); 884 | assert.deepStrictEqual({ a: 4, b: '2' }, { a: 4, b: '2' }); 885 | assert.throws(() => assert.deepStrictEqual([4], ['4']), 886 | { 887 | code: 'ERR_ASSERTION', 888 | name: 'AssertionError', 889 | // message: `${defaultMsgStartFull}\n\n [\n+ 4\n- '4'\n ]` 890 | }); 891 | assert.throws( 892 | () => assert.deepStrictEqual({ a: 4 }, { a: 4, b: true }), 893 | { 894 | code: 'ERR_ASSERTION', 895 | name: 'AssertionError', 896 | // message: `${defaultMsgStartFull}\n\n ` + 897 | // '{\n a: 4,\n- b: true\n }' 898 | }); 899 | assert.throws( 900 | () => assert.deepStrictEqual(['a'], { 0: 'a' }), 901 | { 902 | code: 'ERR_ASSERTION', 903 | name: 'AssertionError', 904 | // message: `${defaultMsgStartFull}\n\n` + 905 | // "+ [\n+ 'a'\n+ ]\n- {\n- '0': 'a'\n- }" 906 | }); 907 | 908 | /* eslint-enable */ 909 | 910 | assert.deepStrictEqual({ a: 4, b: '1' }, { b: '1', a: 4 }); 911 | 912 | assert.throws( 913 | () => assert.deepStrictEqual([0, 1, 2, 'a', 'b'], [0, 1, 2, 'b', 'a']), 914 | AssertionError); 915 | 916 | assert.deepStrictEqual(a1, a2); 917 | 918 | // Prototype check. 919 | function Constructor1(first, last) { 920 | this.first = first; 921 | this.last = last; 922 | } 923 | 924 | function Constructor2(first, last) { 925 | this.first = first; 926 | this.last = last; 927 | } 928 | 929 | const obj1 = new Constructor1('Ryan', 'Dahl'); 930 | let obj2 = new Constructor2('Ryan', 'Dahl'); 931 | 932 | assert.throws(() => assert.deepStrictEqual(obj1, obj2), AssertionError); 933 | 934 | Constructor2.prototype = Constructor1.prototype; 935 | obj2 = new Constructor2('Ryan', 'Dahl'); 936 | 937 | assert.deepStrictEqual(obj1, obj2); 938 | 939 | // Check extra properties on errors. 940 | { 941 | const a = new TypeError('foo'); 942 | const b = new TypeError('foo'); 943 | a.foo = 'bar'; 944 | b.foo = 'baz'; 945 | 946 | assert.throws( 947 | () => assert.deepStrictEqual(a, b), 948 | // { 949 | // message: `${defaultMsgStartFull}\n\n` + 950 | // ' [TypeError: foo] {\n+ foo: \'bar\'\n- foo: \'baz\'\n }' 951 | // } 952 | ); 953 | } 954 | 955 | // Check proxies. 956 | { 957 | // TODO(BridgeAR): Check if it would not be better to detect proxies instead 958 | // of just using the proxy value. 959 | if (typeof Proxy !== 'undefined') { 960 | const arrProxy = new Proxy([1, 2], {}); 961 | assert.deepStrictEqual(arrProxy, [1, 2]); 962 | const tmp = util.inspect.defaultOptions; 963 | util.inspect.defaultOptions = { showProxy: true }; 964 | assert.throws( 965 | () => assert.deepStrictEqual(arrProxy, [1, 2, 3]), 966 | // { message: `${defaultMsgStartFull}\n\n` + 967 | // ' [\n 1,\n 2,\n- 3\n ]' } 968 | ); 969 | util.inspect.defaultOptions = tmp; 970 | } 971 | 972 | // [browserify] Safari fails this test due to the proxy. Chrome and Firefox pass. 973 | // const invalidTrap = new Proxy([1, 2, 3], { 974 | // ownKeys() { return []; } 975 | // }); 976 | // assert.throws( 977 | // () => assert.deepStrictEqual(invalidTrap, [1, 2, 3]), 978 | // { 979 | // name: 'TypeError', 980 | // // message: "'ownKeys' on proxy: trap result did not include 'length'" 981 | // } 982 | // ); 983 | } 984 | 985 | // Strict equal with identical objects that are not identical 986 | // by reference and longer than 30 elements 987 | // E.g., assert.deepStrictEqual({ a: Symbol() }, { a: Symbol() }) 988 | if (common.symbolSupported) { 989 | const a = {}; 990 | const b = {}; 991 | for (let i = 0; i < 35; i++) { 992 | a[`symbol${i}`] = Symbol(); 993 | b[`symbol${i}`] = Symbol(); 994 | } 995 | 996 | assert.throws( 997 | () => assert.deepStrictEqual(a, b), 998 | { 999 | code: 'ERR_ASSERTION', 1000 | name: 'AssertionError', 1001 | // message: /\.\.\./g 1002 | } 1003 | ); 1004 | } 1005 | 1006 | // Basic valueOf check. 1007 | { 1008 | const a = new String(1); 1009 | a.valueOf = undefined; 1010 | assertNotDeepOrStrict(a, new String(1)); 1011 | } 1012 | 1013 | // Basic array out of bounds check. 1014 | { 1015 | const arr = [1, 2, 3]; 1016 | arr[2 ** 32] = true; 1017 | assertNotDeepOrStrict(arr, [1, 2, 3]); 1018 | } 1019 | 1020 | assert.throws( 1021 | () => assert.deepStrictEqual([1, 2, 3], [1, 2]), 1022 | { 1023 | code: 'ERR_ASSERTION', 1024 | name: 'AssertionError', 1025 | // message: `${defaultMsgStartFull}\n\n` + 1026 | // ' [\n' + 1027 | // ' 1,\n' + 1028 | // ' 2,\n' + 1029 | // '+ 3\n' + 1030 | // ' ]' 1031 | } 1032 | ); 1033 | 1034 | // Verify that manipulating the `getTime()` function has no impact on the time 1035 | // verification. 1036 | { 1037 | const a = new Date('2000'); 1038 | const b = new Date('2000'); 1039 | Object.defineProperty(a, 'getTime', { 1040 | value: () => 5 1041 | }); 1042 | assert.deepStrictEqual(a, b); 1043 | } 1044 | 1045 | // Verify that extra keys will be tested for when using fake arrays. 1046 | if (common.symbolToStringTagSupported) { 1047 | const a = { 1048 | 0: 1, 1049 | 1: 1, 1050 | 2: 'broken' 1051 | }; 1052 | Object.setPrototypeOf(a, Object.getPrototypeOf([])); 1053 | Object.defineProperty(a, Symbol.toStringTag, { 1054 | value: 'Array', 1055 | }); 1056 | Object.defineProperty(a, 'length', { 1057 | value: 2 1058 | }); 1059 | assert.notDeepStrictEqual(a, [1, 1]); 1060 | } 1061 | 1062 | // Verify that changed tags will still check for the error message. 1063 | if (common.symbolToStringTagSupported) { 1064 | const err = new Error('foo'); 1065 | err[Symbol.toStringTag] = 'Foobar'; 1066 | const err2 = new Error('bar'); 1067 | err2[Symbol.toStringTag] = 'Foobar'; 1068 | assertNotDeepOrStrict(err, err2, AssertionError); 1069 | } 1070 | 1071 | // Check for non-native errors. 1072 | if (common.symbolToStringTagSupported) { 1073 | const source = new Error('abc'); 1074 | const err = Object.create( 1075 | Object.getPrototypeOf(source), common.getOwnPropertyDescriptors(source)); 1076 | Object.defineProperty(err, 'message', { value: 'foo' }); 1077 | const err2 = Object.create( 1078 | Object.getPrototypeOf(source), common.getOwnPropertyDescriptors(source)); 1079 | Object.defineProperty(err2, 'message', { value: 'bar' }); 1080 | err[Symbol.toStringTag] = 'Foo'; 1081 | err2[Symbol.toStringTag] = 'Foo'; 1082 | assert.notDeepStrictEqual(err, err2); 1083 | } 1084 | 1085 | // Verify that `valueOf` is not called for boxed primitives. 1086 | { 1087 | const a = new Number(5); 1088 | const b = new Number(5); 1089 | Object.defineProperty(a, 'valueOf', { 1090 | value: () => { throw new Error('failed'); } 1091 | }); 1092 | Object.defineProperty(b, 'valueOf', { 1093 | value: () => { throw new Error('failed'); } 1094 | }); 1095 | assertDeepAndStrictEqual(a, b); 1096 | } 1097 | 1098 | // Check getters. 1099 | { 1100 | const a = { 1101 | get a() { return 5; } 1102 | }; 1103 | const b = { 1104 | get a() { return 6; } 1105 | }; 1106 | assert.throws( 1107 | () => assert.deepStrictEqual(a, b), 1108 | { 1109 | code: 'ERR_ASSERTION', 1110 | name: 'AssertionError', 1111 | // message: /a: \[Getter: 5]\n- a: \[Getter: 6]\n / 1112 | } 1113 | ); 1114 | 1115 | // The descriptor is not compared. 1116 | assertDeepAndStrictEqual(a, { a: 5 }); 1117 | } 1118 | -------------------------------------------------------------------------------- /test/parallel/test-assert-fail-deprecation.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/parallel/test-assert-fail-deprecation.js 2 | // https://github.com/nodejs/node/commit/1ed3c54ecbd72a33693e5954f86bcc9fd9b1cc09 3 | 4 | 'use strict'; 5 | 6 | const common = require('../common'); 7 | const assert = require('../../assert'); 8 | 9 | common.expectWarning( 10 | 'DeprecationWarning', 11 | 'assert.fail() with more than one argument is deprecated. ' + 12 | 'Please use assert.strictEqual() instead or only pass a message.', 13 | 'DEP0094' 14 | ); 15 | 16 | // Two args only, operator defaults to '!=' 17 | assert.throws(() => { 18 | assert.fail('first', 'second'); 19 | }, { 20 | code: 'ERR_ASSERTION', 21 | name: 'AssertionError', 22 | message: '\'first\' != \'second\'', 23 | operator: '!=', 24 | actual: 'first', 25 | expected: 'second', 26 | generatedMessage: true 27 | }); 28 | 29 | // Three args 30 | assert.throws(() => { 31 | assert.fail('ignored', 'ignored', 'another custom message'); 32 | }, { 33 | code: 'ERR_ASSERTION', 34 | name: 'AssertionError', 35 | message: 'another custom message', 36 | operator: 'fail', 37 | actual: 'ignored', 38 | expected: 'ignored', 39 | generatedMessage: false 40 | }); 41 | 42 | // Three args with custom Error 43 | assert.throws(() => { 44 | assert.fail(typeof 1, 'object', new TypeError('another custom message')); 45 | }, { 46 | name: 'TypeError', 47 | message: 'another custom message' 48 | }); 49 | 50 | // No third arg (but a fourth arg) 51 | assert.throws(() => { 52 | assert.fail('first', 'second', undefined, 'operator'); 53 | }, { 54 | code: 'ERR_ASSERTION', 55 | name: 'AssertionError', 56 | message: '\'first\' operator \'second\'', 57 | operator: 'operator', 58 | actual: 'first', 59 | expected: 'second' 60 | }); 61 | 62 | // [broserify] Don't worry if some browsers have less accurate stacks 63 | // // The stackFrameFunction should exclude the foo frame 64 | // assert.throws( 65 | // function foo() { assert.fail('first', 'second', 'message', '!==', foo); }, 66 | // (err) => !/^\s*at\sfoo\b/m.test(err.stack) 67 | // ); 68 | -------------------------------------------------------------------------------- /test/parallel/test-assert-fail.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/parallel/test-assert-fail.js 2 | // https://github.com/nodejs/node/commit/1ed3c54ecbd72a33693e5954f86bcc9fd9b1cc09 3 | 4 | 'use strict'; 5 | 6 | require('../common'); 7 | const assert = require('../../assert'); 8 | 9 | // No args 10 | assert.throws( 11 | () => { assert.fail(); }, 12 | { 13 | code: 'ERR_ASSERTION', 14 | name: 'AssertionError', 15 | message: 'Failed', 16 | operator: 'fail', 17 | actual: undefined, 18 | expected: undefined, 19 | generatedMessage: true 20 | } 21 | ); 22 | 23 | // One arg = message 24 | assert.throws(() => { 25 | assert.fail('custom message'); 26 | }, { 27 | code: 'ERR_ASSERTION', 28 | name: 'AssertionError', 29 | message: 'custom message', 30 | operator: 'fail', 31 | actual: undefined, 32 | expected: undefined, 33 | generatedMessage: false 34 | }); 35 | 36 | // One arg = Error 37 | assert.throws(() => { 38 | assert.fail(new TypeError('custom message')); 39 | }, { 40 | name: 'TypeError', 41 | message: 'custom message' 42 | }); 43 | -------------------------------------------------------------------------------- /test/parallel/test-assert-if-error.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/parallel/test-assert-if-error.js 2 | // https://github.com/nodejs/node/commit/644fdd60d4be49ffa66d0bda1702c4459f607635 3 | 4 | // [browserify] 5 | // Most `err.message` and `err.stack` tests are commented out because they are 6 | // inconsistent between browsers. 7 | 8 | 'use strict'; 9 | 10 | require('../common'); 11 | const assert = require('../../assert').strict; 12 | /* eslint-disable no-restricted-properties */ 13 | 14 | // Test that assert.ifError has the correct stack trace of both stacks. 15 | 16 | let err; 17 | // Create some random error frames. 18 | (function a() { 19 | (function b() { 20 | (function c() { 21 | err = new Error('test error'); 22 | })(); 23 | })(); 24 | })(); 25 | 26 | const msg = err.message; 27 | const stack = err.stack; 28 | 29 | (function x() { 30 | (function y() { 31 | (function z() { 32 | let threw = false; 33 | try { 34 | assert.ifError(err); 35 | } catch (e) { 36 | assert.equal(e.message, 'ifError got unwanted exception: test error'); 37 | assert.equal(err.message, msg); 38 | assert.equal(e.actual, err); 39 | assert.equal(e.actual.stack, stack); 40 | assert.equal(e.expected, null); 41 | assert.equal(e.operator, 'ifError'); 42 | threw = true; 43 | } 44 | assert(threw); 45 | })(); 46 | })(); 47 | })(); 48 | 49 | // assert.throws( 50 | // () => assert.ifError(new TypeError()), 51 | // { 52 | // message: 'ifError got unwanted exception: TypeError' 53 | // } 54 | // ); 55 | 56 | assert.throws( 57 | () => assert.ifError({ stack: false }), 58 | { 59 | message: 'ifError got unwanted exception: { stack: false }' 60 | } 61 | ); 62 | 63 | assert.throws( 64 | () => assert.ifError({ constructor: null, message: '' }), 65 | { 66 | message: 'ifError got unwanted exception: ' 67 | } 68 | ); 69 | 70 | assert.throws( 71 | () => { assert.ifError(false); }, 72 | { 73 | message: 'ifError got unwanted exception: false' 74 | } 75 | ); 76 | 77 | // Should not throw. 78 | assert.ifError(null); 79 | assert.ifError(); 80 | assert.ifError(undefined); 81 | 82 | // https://github.com/nodejs/node-v0.x-archive/issues/2893 83 | { 84 | let threw = false; 85 | try { 86 | // eslint-disable-next-line no-restricted-syntax 87 | assert.throws(() => { 88 | assert.ifError(null); 89 | }); 90 | } catch (e) { 91 | threw = true; 92 | assert.strictEqual(e.message, 'Missing expected exception.'); 93 | // assert(!e.stack.includes('throws'), e.stack); 94 | } 95 | assert(threw); 96 | } 97 | -------------------------------------------------------------------------------- /test/parallel/test-assert-match.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/parallel/test-assert.js 2 | // https://github.com/nodejs/node/commit/2a871df3dfb8ea663ef5e1f8f62701ec51384ecb 3 | 4 | 'use strict'; 5 | 6 | require('../common'); 7 | const assert = require('../../assert'); 8 | 9 | // Multiple assert.match() tests. 10 | { 11 | assert.throws( 12 | () => assert.match(/abc/, 'string'), 13 | { 14 | code: 'ERR_INVALID_ARG_TYPE', 15 | message: 'The "regexp" argument must be of type RegExp. Received type string' 16 | } 17 | ); 18 | assert.throws( 19 | () => assert.match('string', /abc/), 20 | { 21 | actual: 'string', 22 | expected: /abc/, 23 | operator: 'match', 24 | message: 'The input did not match the regular expression /abc/. ' + 25 | "Input:\n\n'string'\n", 26 | generatedMessage: true 27 | } 28 | ); 29 | assert.throws( 30 | () => assert.match('string', /abc/, 'foobar'), 31 | { 32 | actual: 'string', 33 | expected: /abc/, 34 | operator: 'match', 35 | message: 'foobar', 36 | generatedMessage: false 37 | } 38 | ); 39 | const errorMessage = new RangeError('foobar'); 40 | assert.throws( 41 | () => assert.match('string', /abc/, errorMessage), 42 | errorMessage 43 | ); 44 | assert.throws( 45 | () => assert.match({ abc: 123 }, /abc/), 46 | { 47 | actual: { abc: 123 }, 48 | expected: /abc/, 49 | operator: 'match', 50 | message: 'The "string" argument must be of type string. ' + 51 | 'Received type object ({ abc: 123 })', 52 | generatedMessage: true 53 | } 54 | ); 55 | assert.match('I will pass', /pass$/); 56 | } 57 | 58 | // Multiple assert.doesNotMatch() tests. 59 | { 60 | assert.throws( 61 | () => assert.doesNotMatch(/abc/, 'string'), 62 | { 63 | code: 'ERR_INVALID_ARG_TYPE', 64 | message: 'The "regexp" argument must be of type RegExp. Received type string' 65 | } 66 | ); 67 | assert.throws( 68 | () => assert.doesNotMatch('string', /string/), 69 | { 70 | actual: 'string', 71 | expected: /string/, 72 | operator: 'doesNotMatch', 73 | message: 'The input was expected to not match the regular expression ' + 74 | "/string/. Input:\n\n'string'\n", 75 | generatedMessage: true 76 | } 77 | ); 78 | assert.throws( 79 | () => assert.doesNotMatch('string', /string/, 'foobar'), 80 | { 81 | actual: 'string', 82 | expected: /string/, 83 | operator: 'doesNotMatch', 84 | message: 'foobar', 85 | generatedMessage: false 86 | } 87 | ); 88 | const errorMessage = new RangeError('foobar'); 89 | assert.throws( 90 | () => assert.doesNotMatch('string', /string/, errorMessage), 91 | errorMessage 92 | ); 93 | assert.throws( 94 | () => assert.doesNotMatch({ abc: 123 }, /abc/), 95 | { 96 | actual: { abc: 123 }, 97 | expected: /abc/, 98 | operator: 'doesNotMatch', 99 | message: 'The "string" argument must be of type string. ' + 100 | 'Received type object ({ abc: 123 })', 101 | generatedMessage: true 102 | } 103 | ); 104 | assert.doesNotMatch('I will pass', /different$/); 105 | } 106 | -------------------------------------------------------------------------------- /test/parallel/test-assert-typedarray-deepequal.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/parallel/test-assert-typedarray-deepequal.js 2 | // https://github.com/nodejs/node/commit/7493db21b667ed746d39c9b54357eac4287232e3 3 | 4 | 'use strict'; 5 | 6 | require('../common'); 7 | const assert = require('../../assert'); 8 | 9 | function makeBlock(f) { 10 | const args = Array.prototype.slice.call(arguments, 1); 11 | return function() { 12 | return f.apply(this, args); 13 | }; 14 | } 15 | 16 | const equalArrayPairs = [ 17 | [() => new Uint8Array(1e5), () => new Uint8Array(1e5)], 18 | [() => new Uint16Array(1e5), () => new Uint16Array(1e5)], 19 | [() => new Uint32Array(1e5), () => new Uint32Array(1e5)], 20 | [() => new Uint8ClampedArray(1e5), () => new Uint8ClampedArray(1e5)], 21 | [() => new Int8Array(1e5), () => new Int8Array(1e5)], 22 | [() => new Int16Array(1e5), () => new Int16Array(1e5)], 23 | [() => new Int32Array(1e5), () => new Int32Array(1e5)], 24 | [() => new Float32Array(1e5), () => new Float32Array(1e5)], 25 | [() => new Float64Array(1e5), () => new Float64Array(1e5)], 26 | [() => new Float32Array([+0.0]), () => new Float32Array([+0.0])], 27 | [() => new Uint8Array([1, 2, 3, 4]).subarray(1), () => new Uint8Array([2, 3, 4])], 28 | [() => new Uint16Array([1, 2, 3, 4]).subarray(1), () => new Uint16Array([2, 3, 4])], 29 | [() => new Uint32Array([1, 2, 3, 4]).subarray(1, 3), () => new Uint32Array([2, 3])], 30 | [() => new ArrayBuffer(3), () => new ArrayBuffer(3)], 31 | [() => new SharedArrayBuffer(3), () => new SharedArrayBuffer(3)] 32 | ]; 33 | 34 | const looseEqualArrayPairs = [ 35 | [() => new Float32Array([+0.0]), () => new Float32Array([-0.0])], 36 | [() => new Float64Array([+0.0]), () => new Float64Array([-0.0])] 37 | ]; 38 | 39 | const notEqualArrayPairs = [ 40 | [() => new ArrayBuffer(3), () => new SharedArrayBuffer(3)], 41 | [() => new Int16Array(256), () => new Uint16Array(256)], 42 | [() => new Int16Array([256]), () => new Uint16Array([256])], 43 | [() => new Float64Array([+0.0]), () => new Float32Array([-0.0])], 44 | [() => new Uint8Array(2), () => new Uint8Array(3)], 45 | [() => new Uint8Array([1, 2, 3]), () => new Uint8Array([4, 5, 6])], 46 | [() => new Uint8ClampedArray([300, 2, 3]), () => new Uint8Array([300, 2, 3])], 47 | [() => new Uint16Array([2]), () => new Uint16Array([3])], 48 | [() => new Uint16Array([0]), () => new Uint16Array([256])], 49 | [() => new Int16Array([0]), () => new Uint16Array([256])], 50 | [() => new Int16Array([-256]), () => new Uint16Array([0xff00])], // same bits 51 | [() => new Int32Array([-256]), () => new Uint32Array([0xffffff00])], // ditto 52 | [() => new Float32Array([0.1]), () => new Float32Array([0.0])], 53 | [() => new Float32Array([0.1]), () => new Float32Array([0.1, 0.2])], 54 | [() => new Float64Array([0.1]), () => new Float64Array([0.0])], 55 | [() => new Uint8Array([1, 2, 3]).buffer, () => new Uint8Array([4, 5, 6]).buffer], 56 | [ 57 | () => new Uint8Array(new SharedArrayBuffer(3)).fill(1).buffer, 58 | () => new Uint8Array(new SharedArrayBuffer(3)).fill(2).buffer 59 | ], 60 | [() => new ArrayBuffer(2), () => new ArrayBuffer(3)], 61 | [() => new SharedArrayBuffer(2), () => new SharedArrayBuffer(3)], 62 | [() => new ArrayBuffer(2), () => new SharedArrayBuffer(3)], 63 | [ 64 | () => new Uint8Array(new ArrayBuffer(3)).fill(1).buffer, 65 | () => new Uint8Array(new SharedArrayBuffer(3)).fill(2).buffer 66 | ] 67 | ]; 68 | 69 | console.log('equalArrayPairs'); 70 | equalArrayPairs.forEach((arrayPair) => { 71 | let array0; 72 | let array1; 73 | try { 74 | array0 = arrayPair[0](); 75 | array1 = arrayPair[1](); 76 | } catch(e) { 77 | console.log('Skipping unsupported typed array:', e.message); 78 | return; 79 | } 80 | // eslint-disable-next-line no-restricted-properties 81 | assert.deepEqual(array0, array1); 82 | assert.deepStrictEqual(array0, array1); 83 | }); 84 | 85 | console.log('looseEqualArrayPairs'); 86 | looseEqualArrayPairs.forEach((arrayPair) => { 87 | let array0; 88 | let array1; 89 | try { 90 | array0 = arrayPair[0](); 91 | array1 = arrayPair[1](); 92 | } catch(e) { 93 | console.log('Skipping unsupported typed array:', e.message); 94 | return; 95 | } 96 | // eslint-disable-next-line no-restricted-properties 97 | assert.deepEqual(array0, array1); 98 | assert.throws( 99 | makeBlock(assert.deepStrictEqual, array0, array1), 100 | assert.AssertionError 101 | ); 102 | }); 103 | 104 | console.log('notEqualArrayPairs'); 105 | notEqualArrayPairs.forEach((arrayPair) => { 106 | let array0; 107 | let array1; 108 | try { 109 | array0 = arrayPair[0](); 110 | array1 = arrayPair[1](); 111 | } catch(e) { 112 | console.log('Skipping unsupported typed array:', e.message); 113 | return; 114 | } 115 | assert.throws( 116 | // eslint-disable-next-line no-restricted-properties 117 | makeBlock(assert.deepEqual, array0, array1), 118 | assert.AssertionError 119 | ); 120 | assert.throws( 121 | makeBlock(assert.deepStrictEqual, array0, array1), 122 | assert.AssertionError 123 | ); 124 | }); 125 | -------------------------------------------------------------------------------- /test/parallel/test-assert.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/parallel/test-assert.js 2 | // https://github.com/nodejs/node/commit/1ed3c54ecbd72a33693e5954f86bcc9fd9b1cc09 3 | 4 | // Flags: --expose-internals 5 | // Copyright Joyent, Inc. and other Node contributors. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, subject to the 13 | // following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 21 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | // [browserify] 27 | // Most `err.message` and `err.stack` tests are commented out because they are 28 | // inconsistent between browsers. 29 | // 30 | // `err.operator` tests are commented out because its always `undefined` in IE. 31 | // If we drop IE support we can uncomment these tests. 32 | 33 | 'use strict'; 34 | 35 | const arrayFill = require('array-fill'); 36 | 37 | const common = require('../common'); 38 | const assert = require('../../assert'); 39 | const { inspect } = require('util/'); 40 | // [browserify] 41 | // const { internalBinding } = require('internal/test/binding'); 42 | const a = assert; 43 | 44 | // Disable colored output to prevent color codes from breaking assertion 45 | // message comparisons. This should only be an issue when process.stdout 46 | // is a TTY. 47 | if (process.stdout && process.stdout.isTTY) 48 | process.env.NODE_DISABLE_COLORS = '1'; 49 | 50 | const strictEqualMessageStart = 'Expected values to be strictly equal:\n'; 51 | const start = 'Expected values to be strictly deep-equal:'; 52 | const actExp = '+ actual - expected'; 53 | 54 | assert.ok(a.AssertionError.prototype instanceof Error, 55 | 'a.AssertionError instanceof Error'); 56 | 57 | assert.throws(() => a(false), a.AssertionError, 'ok(false)'); 58 | assert.throws(() => a.ok(false), a.AssertionError, 'ok(false)'); 59 | 60 | a(true); 61 | a('test', 'ok(\'test\')'); 62 | a.ok(true); 63 | a.ok('test'); 64 | 65 | assert.throws(() => a.equal(true, false), 66 | a.AssertionError, 'equal(true, false)'); 67 | 68 | a.equal(null, null); 69 | a.equal(undefined, undefined); 70 | a.equal(null, undefined); 71 | a.equal(true, true); 72 | a.equal(2, '2'); 73 | a.notEqual(true, false); 74 | 75 | assert.throws(() => a.notEqual(true, true), 76 | a.AssertionError, 'notEqual(true, true)'); 77 | 78 | assert.throws(() => a.strictEqual(2, '2'), 79 | a.AssertionError, 'strictEqual(2, \'2\')'); 80 | 81 | /* eslint-disable no-restricted-syntax */ 82 | assert.throws(() => a.strictEqual(null, undefined), 83 | a.AssertionError, 'strictEqual(null, undefined)'); 84 | 85 | assert.throws( 86 | () => a.notStrictEqual(2, 2), 87 | { 88 | // message: 'Expected "actual" to be strictly unequal to: 2', 89 | name: 'AssertionError' 90 | } 91 | ); 92 | 93 | assert.throws( 94 | () => a.notStrictEqual(common.repeat('a ', 30), common.repeat('a ', 30)), 95 | { 96 | // message: 'Expected "actual" to be strictly unequal to: ' + 97 | // `'${'a '.repeat(30)}'`, 98 | name: 'AssertionError' 99 | } 100 | ); 101 | 102 | a.notStrictEqual(2, '2'); 103 | 104 | // Testing the throwing. 105 | function thrower(errorConstructor) { 106 | throw new errorConstructor({}); 107 | } 108 | 109 | // The basic calls work. 110 | assert.throws(() => thrower(a.AssertionError), a.AssertionError, 'message'); 111 | assert.throws(() => thrower(a.AssertionError), a.AssertionError); 112 | assert.throws(() => thrower(a.AssertionError)); 113 | 114 | // If not passing an error, catch all. 115 | assert.throws(() => thrower(TypeError)); 116 | 117 | // When passing a type, only catch errors of the appropriate type. 118 | { 119 | let threw = false; 120 | try { 121 | a.throws(() => thrower(TypeError), a.AssertionError); 122 | } catch (e) { 123 | threw = true; 124 | assert.ok(e instanceof TypeError, 'type'); 125 | } 126 | assert.ok(threw, 'a.throws with an explicit error is eating extra errors'); 127 | } 128 | 129 | // doesNotThrow should pass through all errors. 130 | { 131 | let threw = false; 132 | try { 133 | a.doesNotThrow(() => thrower(TypeError), a.AssertionError); 134 | } catch (e) { 135 | threw = true; 136 | assert.ok(e instanceof TypeError); 137 | } 138 | assert(threw, 'a.doesNotThrow with an explicit error is eating extra errors'); 139 | } 140 | 141 | // Key difference is that throwing our correct error makes an assertion error. 142 | { 143 | let threw = false; 144 | try { 145 | a.doesNotThrow(() => thrower(TypeError), TypeError); 146 | } catch (e) { 147 | threw = true; 148 | assert.ok(e instanceof a.AssertionError); 149 | assert.ok(!common.includes(e.stack, 'at Function.doesNotThrow')); 150 | } 151 | assert.ok(threw, 'a.doesNotThrow is not catching type matching errors'); 152 | } 153 | 154 | assert.throws( 155 | () => a.doesNotThrow(() => thrower(Error), 'user message'), 156 | { 157 | name: 'AssertionError', 158 | code: 'ERR_ASSERTION', 159 | // operator: 'doesNotThrow', 160 | // message: 'Got unwanted exception: user message\n' + 161 | // 'Actual message: "[object Object]"' 162 | } 163 | ); 164 | 165 | assert.throws( 166 | () => a.doesNotThrow(() => thrower(Error)), 167 | { 168 | code: 'ERR_ASSERTION', 169 | // message: 'Got unwanted exception.\nActual message: "[object Object]"' 170 | } 171 | ); 172 | 173 | // Make sure that validating using constructor really works. 174 | { 175 | let threw = false; 176 | try { 177 | assert.throws( 178 | () => { 179 | throw ({}); // eslint-disable-line no-throw-literal 180 | }, 181 | Array 182 | ); 183 | } catch(e) { 184 | threw = true; 185 | } 186 | assert.ok(threw, 'wrong constructor validation'); 187 | } 188 | 189 | // Use a RegExp to validate the error message. 190 | a.throws(() => thrower(TypeError), /\[object Object\]/); 191 | 192 | // Use a fn to validate the error object. 193 | a.throws(() => thrower(TypeError), (err) => { 194 | if ((err instanceof TypeError) && /\[object Object\]/.test(err)) { 195 | return true; 196 | } 197 | }); 198 | 199 | // https://github.com/nodejs/node/issues/3188 200 | { 201 | let threw = false; 202 | let AnotherErrorType; 203 | try { 204 | const ES6Error = class extends Error {}; 205 | AnotherErrorType = class extends Error {}; 206 | 207 | assert.throws(() => { throw new AnotherErrorType('foo'); }, ES6Error); 208 | } catch (e) { 209 | threw = true; 210 | assert(e instanceof AnotherErrorType, 211 | `expected AnotherErrorType, received ${e}`); 212 | } 213 | 214 | assert.ok(threw); 215 | } 216 | 217 | // Check messages from assert.throws(). 218 | { 219 | const noop = () => {}; 220 | assert.throws( 221 | () => { a.throws((noop)); }, 222 | { 223 | code: 'ERR_ASSERTION', 224 | // message: 'Missing expected exception.', 225 | // operator: 'throws', 226 | actual: undefined, 227 | expected: undefined 228 | }); 229 | 230 | assert.throws( 231 | () => { a.throws(noop, TypeError); }, 232 | { 233 | code: 'ERR_ASSERTION', 234 | // message: 'Missing expected exception (TypeError).', 235 | actual: undefined, 236 | expected: TypeError 237 | }); 238 | 239 | assert.throws( 240 | () => { a.throws(noop, 'fhqwhgads'); }, 241 | { 242 | code: 'ERR_ASSERTION', 243 | // message: 'Missing expected exception: fhqwhgads', 244 | actual: undefined, 245 | expected: undefined 246 | }); 247 | 248 | assert.throws( 249 | () => { a.throws(noop, TypeError, 'fhqwhgads'); }, 250 | { 251 | code: 'ERR_ASSERTION', 252 | // message: 'Missing expected exception (TypeError): fhqwhgads', 253 | actual: undefined, 254 | expected: TypeError 255 | }); 256 | 257 | let threw = false; 258 | try { 259 | a.throws(noop); 260 | } catch (e) { 261 | threw = true; 262 | assert.ok(e instanceof a.AssertionError); 263 | assert.ok(!common.includes(e.stack, 'at Function.throws')); 264 | } 265 | assert.ok(threw); 266 | } 267 | 268 | const circular = { y: 1 }; 269 | circular.x = circular; 270 | 271 | function testAssertionMessage(actual, expected, msg) { 272 | assert.throws( 273 | () => assert.strictEqual(actual, ''), 274 | // { 275 | // // generatedMessage: true, 276 | // // message: msg || strictEqualMessageStart + 277 | // // `+ actual - expected\n\n+ ${expected}\n- ''` 278 | // } 279 | ); 280 | } 281 | 282 | function testShortAssertionMessage(actual, expected) { 283 | testAssertionMessage(actual, expected, strictEqualMessageStart + 284 | `\n${inspect(actual)} !== ''\n`); 285 | } 286 | 287 | testShortAssertionMessage(null, 'null'); 288 | testShortAssertionMessage(true, 'true'); 289 | testShortAssertionMessage(false, 'false'); 290 | testShortAssertionMessage(100, '100'); 291 | testShortAssertionMessage(NaN, 'NaN'); 292 | testShortAssertionMessage(Infinity, 'Infinity'); 293 | testShortAssertionMessage('a', '"a"'); 294 | testShortAssertionMessage('foo', '\'foo\''); 295 | testShortAssertionMessage(0, '0'); 296 | if (common.symbolSupported) { 297 | testShortAssertionMessage(Symbol(), 'Symbol()'); 298 | } 299 | testAssertionMessage([], '[]'); 300 | testAssertionMessage(/a/, '/a/'); 301 | testAssertionMessage(/abc/gim, '/abc/gim'); 302 | testAssertionMessage({}, '{}'); 303 | testAssertionMessage(undefined, 'undefined'); 304 | testAssertionMessage(-Infinity, '-Infinity'); 305 | testAssertionMessage([1, 2, 3], '[\n+ 1,\n+ 2,\n+ 3\n+ ]'); 306 | testAssertionMessage(function f() {}, '[Function: f]'); 307 | testAssertionMessage(function() {}, '[Function]'); 308 | testAssertionMessage(circular, '{\n+ x: [Circular],\n+ y: 1\n+ }'); 309 | testAssertionMessage({ a: undefined, b: null }, 310 | '{\n+ a: undefined,\n+ b: null\n+ }'); 311 | testAssertionMessage({ a: NaN, b: Infinity, c: -Infinity }, 312 | '{\n+ a: NaN,\n+ b: Infinity,\n+ c: -Infinity\n+ }'); 313 | 314 | // https://github.com/nodejs/node-v0.x-archive/issues/5292 315 | try { 316 | assert.strictEqual(1, 2); 317 | } catch (e) { 318 | assert.strictEqual( 319 | e.message, 320 | `${strictEqualMessageStart}\n1 !== 2\n` 321 | ); 322 | assert.ok(e.generatedMessage, 'Message not marked as generated'); 323 | } 324 | 325 | try { 326 | assert.strictEqual(1, 2, 'oh no'); 327 | } catch (e) { 328 | assert.strictEqual(e.message, 'oh no'); 329 | // Message should not be marked as generated. 330 | assert.strictEqual(e.generatedMessage, false); 331 | } 332 | 333 | { 334 | let threw = false; 335 | const rangeError = new RangeError('my range'); 336 | 337 | // Verify custom errors. 338 | try { 339 | assert.strictEqual(1, 2, rangeError); 340 | } catch (e) { 341 | assert.strictEqual(e, rangeError); 342 | threw = true; 343 | assert.ok(e instanceof RangeError, 'Incorrect error type thrown'); 344 | } 345 | assert.ok(threw); 346 | threw = false; 347 | 348 | // Verify AssertionError is the result from doesNotThrow with custom Error. 349 | try { 350 | a.doesNotThrow(() => { 351 | throw new TypeError('wrong type'); 352 | }, TypeError, rangeError); 353 | } catch (e) { 354 | threw = true; 355 | assert.ok(common.includes(e.message, rangeError.message)); 356 | assert.ok(e instanceof assert.AssertionError); 357 | // [browserify] 358 | // This fails because `doesNotThrow` appears in the stack trace. 359 | // I'm not quite sure why that's an issue if the error message is set 360 | // and the above tests pass so commenting out for now. 361 | // assert.ok(!common.includes(e.stack, 'doesNotThrow'), e.stack); 362 | } 363 | assert.ok(threw); 364 | } 365 | 366 | { 367 | // Verify that throws() and doesNotThrow() throw on non-functions. 368 | const testBlockTypeError = (method, fn) => { 369 | common.expectsError( 370 | () => method(fn), 371 | { 372 | code: 'ERR_INVALID_ARG_TYPE', 373 | // type: TypeError, 374 | // message: 'The "fn" argument must be of type Function. Received ' + 375 | // `type ${typeof fn}` 376 | } 377 | ); 378 | }; 379 | 380 | testBlockTypeError(assert.throws, 'string'); 381 | testBlockTypeError(assert.doesNotThrow, 'string'); 382 | testBlockTypeError(assert.throws, 1); 383 | testBlockTypeError(assert.doesNotThrow, 1); 384 | testBlockTypeError(assert.throws, true); 385 | testBlockTypeError(assert.doesNotThrow, true); 386 | testBlockTypeError(assert.throws, false); 387 | testBlockTypeError(assert.doesNotThrow, false); 388 | testBlockTypeError(assert.throws, []); 389 | testBlockTypeError(assert.doesNotThrow, []); 390 | testBlockTypeError(assert.throws, {}); 391 | testBlockTypeError(assert.doesNotThrow, {}); 392 | testBlockTypeError(assert.throws, /foo/); 393 | testBlockTypeError(assert.doesNotThrow, /foo/); 394 | testBlockTypeError(assert.throws, null); 395 | testBlockTypeError(assert.doesNotThrow, null); 396 | testBlockTypeError(assert.throws, undefined); 397 | testBlockTypeError(assert.doesNotThrow, undefined); 398 | } 399 | 400 | // https://github.com/nodejs/node/issues/3275 401 | // eslint-disable-next-line no-throw-literal 402 | assert.throws(() => { throw 'error'; }, (err) => err === 'error'); 403 | assert.throws(() => { throw new Error(); }, (err) => err instanceof Error); 404 | 405 | // Long values should be truncated for display. 406 | assert.throws(() => { 407 | assert.strictEqual(common.repeat('A', 1000), ''); 408 | }, { 409 | code: 'ERR_ASSERTION', 410 | // message: `${strictEqualMessageStart}+ actual - expected\n\n` + 411 | // `+ '${'A'.repeat(1000)}'\n- ''` 412 | }); 413 | 414 | if (common.symbolSupported) { 415 | // Bad args to AssertionError constructor should throw TypeError. 416 | const args = [1, true, false, '', null, Infinity, Symbol('test'), undefined]; 417 | args.forEach((input) => { 418 | assert.throws( 419 | () => new assert.AssertionError(input), 420 | { 421 | code: 'ERR_INVALID_ARG_TYPE', 422 | name: 'TypeError', 423 | // message: 'The "options" argument must be of type Object. ' + 424 | // `Received type ${typeof input}` 425 | }); 426 | }); 427 | } 428 | 429 | assert.throws( 430 | () => assert.strictEqual(new Error('foo'), new Error('foobar')), 431 | { 432 | code: 'ERR_ASSERTION', 433 | name: 'AssertionError', 434 | // message: 'Expected "actual" to be reference-equal to "expected":\n' + 435 | // '+ actual - expected\n\n' + 436 | // '+ [Error: foo]\n- [Error: foobar]' 437 | } 438 | ); 439 | 440 | // Test strict assert. 441 | { 442 | const a = require('../../assert'); 443 | const assert = require('../../assert').strict; 444 | /* eslint-disable no-restricted-properties */ 445 | assert.throws(() => assert.equal(1, true), assert.AssertionError); 446 | assert.notEqual(0, false); 447 | assert.throws(() => assert.deepEqual(1, true), assert.AssertionError); 448 | assert.notDeepEqual(0, false); 449 | assert.equal(assert.strict, assert.strict.strict); 450 | assert.equal(assert.equal, assert.strictEqual); 451 | assert.equal(assert.deepEqual, assert.deepStrictEqual); 452 | assert.equal(assert.notEqual, assert.notStrictEqual); 453 | assert.equal(assert.notDeepEqual, assert.notDeepStrictEqual); 454 | assert.equal(Object.keys(assert).length, Object.keys(a).length); 455 | assert(7); 456 | assert.throws( 457 | () => assert(...[]), 458 | { 459 | // message: 'No value argument passed to `assert.ok()`', 460 | name: 'AssertionError', 461 | // generatedMessage: true 462 | } 463 | ); 464 | assert.throws( 465 | () => a(), 466 | { 467 | // message: 'No value argument passed to `assert.ok()`', 468 | name: 'AssertionError' 469 | } 470 | ); 471 | 472 | // Test setting the limit to zero and that assert.strict works properly. 473 | const tmpLimit = Error.stackTraceLimit; 474 | Error.stackTraceLimit = 0; 475 | common.expectsError( 476 | () => { 477 | assert.ok( 478 | typeof 123 === 'string' 479 | ); 480 | }, 481 | { 482 | code: 'ERR_ASSERTION', 483 | // type: assert.AssertionError, 484 | // message: 'The expression evaluated to a falsy value:\n\n ' + 485 | // "assert.ok(\n typeof 123 === 'string'\n )\n" 486 | } 487 | ); 488 | Error.stackTraceLimit = tmpLimit; 489 | 490 | // Test error diffs. 491 | let message = [ 492 | start, 493 | `${actExp} ... Lines skipped`, 494 | '', 495 | ' [', 496 | ' [', 497 | '...', 498 | ' 2,', 499 | '+ 3', 500 | "- '3'", 501 | ' ]', 502 | '...', 503 | ' 5', 504 | ' ]'].join('\n'); 505 | assert.throws( 506 | () => assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]), 507 | // { message } 508 | ); 509 | 510 | message = [ 511 | start, 512 | `${actExp} ... Lines skipped`, 513 | '', 514 | ' [', 515 | ' 1,', 516 | '...', 517 | ' 0,', 518 | '- 1,', 519 | ' 1,', 520 | '...', 521 | ' 1', 522 | ' ]' 523 | ].join('\n'); 524 | assert.throws( 525 | () => assert.deepEqual( 526 | [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1], 527 | [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]), 528 | // { message } 529 | ); 530 | 531 | message = [ 532 | start, 533 | `${actExp} ... Lines skipped`, 534 | '', 535 | ' [', 536 | ' 1,', 537 | '...', 538 | ' 0,', 539 | '+ 1,', 540 | ' 1,', 541 | '...', 542 | ' 1', 543 | ' ]' 544 | ].join('\n'); 545 | assert.throws( 546 | () => assert.deepEqual( 547 | [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1], 548 | [1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1]), 549 | // { message } 550 | ); 551 | 552 | message = [ 553 | start, 554 | actExp, 555 | '', 556 | ' [', 557 | ' 1,', 558 | '+ 2,', 559 | '- 1,', 560 | ' 1,', 561 | ' 1,', 562 | ' 0,', 563 | '+ 1,', 564 | ' 1', 565 | ' ]' 566 | ].join('\n'); 567 | assert.throws( 568 | () => assert.deepEqual( 569 | [1, 2, 1, 1, 0, 1, 1], 570 | [1, 1, 1, 1, 0, 1]), 571 | // { message } 572 | ); 573 | 574 | message = [ 575 | start, 576 | actExp, 577 | '', 578 | '+ [', 579 | '+ 1,', 580 | '+ 2,', 581 | '+ 1', 582 | '+ ]', 583 | '- undefined', 584 | ].join('\n'); 585 | assert.throws( 586 | () => assert.deepEqual([1, 2, 1], undefined), 587 | // { message } 588 | ); 589 | 590 | message = [ 591 | start, 592 | actExp, 593 | '', 594 | ' [', 595 | '+ 1,', 596 | ' 2,', 597 | ' 1', 598 | ' ]' 599 | ].join('\n'); 600 | assert.throws( 601 | () => assert.deepEqual([1, 2, 1], [2, 1]), 602 | // { message } 603 | ); 604 | 605 | message = `${start}\n` + 606 | `${actExp} ... Lines skipped\n` + 607 | '\n' + 608 | ' [\n' + 609 | common.repeat('+ 1,\n', 10) + 610 | '...\n' + 611 | common.repeat('- 2,\n', 10) + 612 | '...'; 613 | assert.throws( 614 | () => assert.deepEqual(arrayFill(Array(12), 1), arrayFill(Array(12), 2)), 615 | // { message } 616 | ); 617 | 618 | const obj1 = {}; 619 | const obj2 = { loop: 'forever' }; 620 | obj2[inspect.custom] = () => '{}'; 621 | // No infinite loop and no custom inspect. 622 | assert.throws( 623 | () => assert.deepEqual(obj1, obj2), 624 | // { 625 | // message: `${start}\n` + 626 | // `${actExp}\n` + 627 | // '\n' + 628 | // '+ {}\n' + 629 | // '- {\n' + 630 | // '- [Symbol(nodejs.util.inspect.custom)]: [Function],\n' + 631 | // "- loop: 'forever'\n" + 632 | // '- }' 633 | // } 634 | ); 635 | 636 | // notDeepEqual tests 637 | assert.throws( 638 | () => assert.notDeepEqual([1], [1]), 639 | // { 640 | // message: 'Expected "actual" not to be strictly deep-equal to:\n\n' + 641 | // '[\n 1\n]\n' 642 | // } 643 | ); 644 | 645 | message = 'Expected "actual" not to be strictly deep-equal to:' + 646 | `\n\n[${common.repeat('\n 1,', 25)}\n...\n`; 647 | const data = arrayFill(Array(31), 1); 648 | assert.throws( 649 | () => assert.notDeepEqual(data, data), 650 | // { message } 651 | ); 652 | /* eslint-enable no-restricted-properties */ 653 | } 654 | 655 | common.expectsError( 656 | () => assert.ok(null), 657 | { 658 | code: 'ERR_ASSERTION', 659 | // type: assert.AssertionError, 660 | // generatedMessage: true, 661 | // message: 'The expression evaluated to a falsy value:\n\n ' + 662 | // 'assert.ok(null)\n' 663 | } 664 | ); 665 | common.expectsError( 666 | () => assert(typeof 123 === 'string'), 667 | { 668 | code: 'ERR_ASSERTION', 669 | // type: assert.AssertionError, 670 | // generatedMessage: true, 671 | // message: 'The expression evaluated to a falsy value:\n\n ' + 672 | // "assert(typeof 123 === 'string')\n" 673 | } 674 | ); 675 | 676 | if (common.symbolSupported) { 677 | common.expectsError( 678 | () => assert(false, Symbol('foo')), 679 | { 680 | code: 'ERR_ASSERTION', 681 | // type: assert.AssertionError, 682 | generatedMessage: false, 683 | // message: 'Symbol(foo)' 684 | } 685 | ); 686 | } 687 | 688 | // [browserify] 689 | // This test uses internal Node.js functionality so we have to skip it. 690 | // { 691 | // // Test caching. 692 | // const fs = internalBinding('fs'); 693 | // const tmp = fs.close; 694 | // fs.close = common.mustCall(tmp, 1); 695 | // function throwErr() { 696 | // assert( 697 | // (Buffer.from('test') instanceof Error) 698 | // ); 699 | // } 700 | // common.expectsError( 701 | // () => throwErr(), 702 | // { 703 | // code: 'ERR_ASSERTION', 704 | // type: assert.AssertionError, 705 | // message: 'The expression evaluated to a falsy value:\n\n ' + 706 | // "assert(\n (Buffer.from('test') instanceof Error)\n )\n" 707 | // } 708 | // ); 709 | // common.expectsError( 710 | // () => throwErr(), 711 | // { 712 | // code: 'ERR_ASSERTION', 713 | // type: assert.AssertionError, 714 | // message: 'The expression evaluated to a falsy value:\n\n ' + 715 | // "assert(\n (Buffer.from('test') instanceof Error)\n )\n" 716 | // } 717 | // ); 718 | // fs.close = tmp; 719 | // } 720 | 721 | common.expectsError( 722 | () => { 723 | a( 724 | (() => 'string')() 725 | // eslint-disable-next-line operator-linebreak 726 | === 727 | 123 instanceof 728 | Buffer 729 | ); 730 | }, 731 | { 732 | code: 'ERR_ASSERTION', 733 | // type: assert.AssertionError, 734 | // message: 'The expression evaluated to a falsy value:\n\n' + 735 | // ' a(\n' + 736 | // ' (() => \'string\')()\n' + 737 | // ' // eslint-disable-next-line operator-linebreak\n' + 738 | // ' ===\n' + 739 | // ' 123 instanceof\n' + 740 | // ' Buffer\n' + 741 | // ' )\n' 742 | } 743 | ); 744 | 745 | common.expectsError( 746 | () => { 747 | a( 748 | (() => 'string')() 749 | // eslint-disable-next-line operator-linebreak 750 | === 751 | 123 instanceof 752 | Buffer 753 | ); 754 | }, 755 | { 756 | code: 'ERR_ASSERTION', 757 | // type: assert.AssertionError, 758 | // message: 'The expression evaluated to a falsy value:\n\n' + 759 | // ' a(\n' + 760 | // ' (() => \'string\')()\n' + 761 | // ' // eslint-disable-next-line operator-linebreak\n' + 762 | // ' ===\n' + 763 | // ' 123 instanceof\n' + 764 | // ' Buffer\n' + 765 | // ' )\n' 766 | } 767 | ); 768 | 769 | /* eslint-disable indent */ 770 | common.expectsError(() => { 771 | a(( 772 | () => 'string')() === 773 | 123 instanceof 774 | Buffer 775 | ); 776 | }, { 777 | code: 'ERR_ASSERTION', 778 | // type: assert.AssertionError, 779 | // message: 'The expression evaluated to a falsy value:\n\n' + 780 | // ' a((\n' + 781 | // ' () => \'string\')() ===\n' + 782 | // ' 123 instanceof\n' + 783 | // ' Buffer\n' + 784 | // ' )\n' 785 | } 786 | ); 787 | /* eslint-enable indent */ 788 | 789 | common.expectsError( 790 | () => { 791 | assert(true); assert(null, undefined); 792 | }, 793 | { 794 | code: 'ERR_ASSERTION', 795 | // type: assert.AssertionError, 796 | // message: 'The expression evaluated to a falsy value:\n\n ' + 797 | // 'assert(null, undefined)\n' 798 | } 799 | ); 800 | 801 | common.expectsError( 802 | () => { 803 | assert 804 | .ok(null, undefined); 805 | }, 806 | { 807 | code: 'ERR_ASSERTION', 808 | // type: assert.AssertionError, 809 | // message: 'The expression evaluated to a falsy value:\n\n ' + 810 | // 'ok(null, undefined)\n' 811 | } 812 | ); 813 | 814 | common.expectsError( 815 | // eslint-disable-next-line dot-notation, quotes 816 | () => assert['ok']["apply"](null, [0]), 817 | { 818 | code: 'ERR_ASSERTION', 819 | // type: assert.AssertionError, 820 | // message: 'The expression evaluated to a falsy value:\n\n ' + 821 | // 'assert[\'ok\']["apply"](null, [0])\n' 822 | } 823 | ); 824 | 825 | common.expectsError( 826 | () => { 827 | const wrapper = (fn, value) => fn(value); 828 | wrapper(assert, false); 829 | }, 830 | { 831 | code: 'ERR_ASSERTION', 832 | // type: assert.AssertionError, 833 | // message: 'The expression evaluated to a falsy value:\n\n fn(value)\n' 834 | } 835 | ); 836 | 837 | common.expectsError( 838 | () => assert.ok.call(null, 0), 839 | { 840 | code: 'ERR_ASSERTION', 841 | // type: assert.AssertionError, 842 | // message: 'The expression evaluated to a falsy value:\n\n ' + 843 | // 'assert.ok.call(null, 0)\n', 844 | // generatedMessage: true 845 | } 846 | ); 847 | 848 | common.expectsError( 849 | () => assert.ok.call(null, 0, 'test'), 850 | { 851 | code: 'ERR_ASSERTION', 852 | // type: assert.AssertionError, 853 | // message: 'test', 854 | generatedMessage: false 855 | } 856 | ); 857 | 858 | // Works in eval. 859 | common.expectsError( 860 | () => new Function('assert', 'assert(1 === 2);')(assert), 861 | { 862 | code: 'ERR_ASSERTION', 863 | // type: assert.AssertionError, 864 | // message: 'false == true' 865 | } 866 | ); 867 | 868 | common.expectsError( 869 | () => assert.throws(() => {}, 'Error message', 'message'), 870 | { 871 | code: 'ERR_INVALID_ARG_TYPE', 872 | // type: TypeError, 873 | // message: 'The "error" argument must be one of type Object, Error, ' + 874 | // 'Function, or RegExp. Received type string' 875 | } 876 | ); 877 | 878 | if (common.symbolSupported) { 879 | [ 880 | 1, 881 | false, 882 | Symbol() 883 | ].forEach((input) => { 884 | assert.throws( 885 | () => assert.throws(() => {}, input), 886 | { 887 | code: 'ERR_INVALID_ARG_TYPE', 888 | // message: 'The "error" argument must be one of type Object, Error, ' + 889 | // `Function, or RegExp. Received type ${typeof input}` 890 | } 891 | ); 892 | }); 893 | } 894 | 895 | { 896 | 897 | assert.throws( 898 | () => { 899 | assert.ok((() => Boolean('' === false))()); 900 | }, 901 | // { 902 | // message: 'The expression evaluated to a falsy value:\n\n' + 903 | // " assert.ok((() => Boolean('\\u0001' === false))())\n" 904 | // } 905 | ); 906 | 907 | const errFn = () => { 908 | const err = new TypeError('Wrong value'); 909 | err.code = 404; 910 | throw err; 911 | }; 912 | const errObj = { 913 | name: 'TypeError', 914 | // message: 'Wrong value' 915 | }; 916 | assert.throws(errFn, errObj); 917 | 918 | errObj.code = 404; 919 | assert.throws(errFn, errObj); 920 | 921 | // Fail in case a expected property is undefined and not existent on the 922 | // error. 923 | errObj.foo = undefined; 924 | assert.throws( 925 | () => assert.throws(errFn, errObj), 926 | { 927 | code: 'ERR_ASSERTION', 928 | name: 'AssertionError', 929 | // message: `${start}\n${actExp}\n\n` + 930 | // ' Comparison {\n' + 931 | // ' code: 404,\n' + 932 | // '- foo: undefined,\n' + 933 | // " message: 'Wrong value',\n" + 934 | // " name: 'TypeError'\n" + 935 | // ' }' 936 | } 937 | ); 938 | 939 | // Show multiple wrong properties at the same time. 940 | errObj.code = '404'; 941 | assert.throws( 942 | () => assert.throws(errFn, errObj), 943 | { 944 | code: 'ERR_ASSERTION', 945 | name: 'AssertionError', 946 | // message: `${start}\n${actExp}\n\n` + 947 | // ' Comparison {\n' + 948 | // '+ code: 404,\n' + 949 | // "- code: '404',\n" + 950 | // '- foo: undefined,\n' + 951 | // " message: 'Wrong value',\n" + 952 | // " name: 'TypeError'\n" + 953 | // ' }' 954 | } 955 | ); 956 | 957 | common.expectsError( 958 | () => assert.throws(() => { throw new Error(); }, { foo: 'bar' }, 'foobar'), 959 | { 960 | // type: assert.AssertionError, 961 | code: 'ERR_ASSERTION', 962 | // message: 'foobar' 963 | } 964 | ); 965 | 966 | common.expectsError( 967 | () => a.doesNotThrow(() => { throw new Error(); }, { foo: 'bar' }), 968 | { 969 | // type: TypeError, 970 | code: 'ERR_INVALID_ARG_TYPE', 971 | // message: 'The "expected" argument must be one of type Function or ' + 972 | // 'RegExp. Received type object' 973 | } 974 | ); 975 | 976 | // [browserify] Error objects will not be strictly equal in some browsers due 977 | // to different `line`/`column` properties. 978 | // assert.throws(() => { throw new Error('e'); }, new Error('e')); 979 | assert.throws( 980 | () => assert.throws(() => { throw new TypeError('e'); }, new Error('e')), 981 | { 982 | name: 'AssertionError', 983 | code: 'ERR_ASSERTION', 984 | // message: `${start}\n${actExp}\n\n` + 985 | // ' Comparison {\n' + 986 | // " message: 'e',\n" + 987 | // "+ name: 'TypeError'\n" + 988 | // "- name: 'Error'\n" + 989 | // ' }' 990 | } 991 | ); 992 | assert.throws( 993 | () => assert.throws(() => { throw new Error('foo'); }, new Error('')), 994 | { 995 | name: 'AssertionError', 996 | code: 'ERR_ASSERTION', 997 | // generatedMessage: true, 998 | // message: `${start}\n${actExp}\n\n` + 999 | // ' Comparison {\n' + 1000 | // "+ message: 'foo',\n" + 1001 | // "- message: '',\n" + 1002 | // " name: 'Error'\n" + 1003 | // ' }' 1004 | } 1005 | ); 1006 | 1007 | // eslint-disable-next-line no-throw-literal 1008 | assert.throws(() => { throw undefined; }, /undefined/); 1009 | assert.throws( 1010 | // eslint-disable-next-line no-throw-literal 1011 | () => a.doesNotThrow(() => { throw undefined; }), 1012 | { 1013 | name: 'AssertionError', 1014 | code: 'ERR_ASSERTION', 1015 | // message: 'Got unwanted exception.\nActual message: "undefined"' 1016 | } 1017 | ); 1018 | } 1019 | 1020 | assert.throws( 1021 | () => assert.throws(() => { throw new Error(); }, {}), 1022 | { 1023 | // message: "The argument 'error' may not be an empty object. Received {}", 1024 | code: 'ERR_INVALID_ARG_VALUE' 1025 | } 1026 | ); 1027 | 1028 | assert.throws( 1029 | () => a.throws( 1030 | // eslint-disable-next-line no-throw-literal 1031 | () => { throw 'foo'; }, 1032 | 'foo' 1033 | ), 1034 | { 1035 | code: 'ERR_AMBIGUOUS_ARGUMENT', 1036 | // message: 'The "error/message" argument is ambiguous. ' + 1037 | // 'The error "foo" is identical to the message.' 1038 | } 1039 | ); 1040 | 1041 | assert.throws( 1042 | () => a.throws( 1043 | () => { throw new TypeError('foo'); }, 1044 | 'foo' 1045 | ), 1046 | { 1047 | code: 'ERR_AMBIGUOUS_ARGUMENT', 1048 | // message: 'The "error/message" argument is ambiguous. ' + 1049 | // 'The error message "foo" is identical to the message.' 1050 | } 1051 | ); 1052 | /* eslint-enable no-restricted-syntax */ 1053 | 1054 | // Should not throw. 1055 | // eslint-disable-next-line no-restricted-syntax, no-throw-literal 1056 | assert.throws(() => { throw null; }, 'foo'); 1057 | 1058 | assert.throws( 1059 | () => assert.strictEqual([], []), 1060 | // { 1061 | // message: 'Values identical but not reference-equal:\n\n[]\n' 1062 | // } 1063 | ); 1064 | 1065 | { 1066 | const args = (function() { return arguments; })('a'); 1067 | assert.throws( 1068 | () => assert.strictEqual(args, { 0: 'a' }), 1069 | // { 1070 | // message: 'Expected "actual" to be reference-equal to "expected":\n' + 1071 | // '+ actual - expected\n\n' + 1072 | // "+ [Arguments] {\n- {\n '0': 'a'\n }" 1073 | // } 1074 | ); 1075 | } 1076 | 1077 | assert.throws( 1078 | () => { throw new TypeError('foobar'); }, 1079 | { 1080 | // message: /foo/, 1081 | name: /^TypeError$/ 1082 | } 1083 | ); 1084 | 1085 | assert.throws( 1086 | () => assert.throws( 1087 | () => { throw new TypeError('foobar'); }, 1088 | { 1089 | message: /fooa/, 1090 | name: /^TypeError$/ 1091 | } 1092 | ), 1093 | // { 1094 | // message: `${start}\n${actExp}\n\n` + 1095 | // ' Comparison {\n' + 1096 | // "+ message: 'foobar',\n" + 1097 | // '- message: /fooa/,\n' + 1098 | // " name: 'TypeError'\n" + 1099 | // ' }' 1100 | // } 1101 | ); 1102 | 1103 | { 1104 | let actual = null; 1105 | const expected = { message: 'foo' }; 1106 | assert.throws( 1107 | () => assert.throws( 1108 | () => { throw actual; }, 1109 | expected 1110 | ), 1111 | { 1112 | // operator: 'throws', 1113 | actual, 1114 | expected, 1115 | // generatedMessage: true, 1116 | // message: `${start}\n${actExp}\n\n` + 1117 | // '+ null\n' + 1118 | // '- {\n' + 1119 | // "- message: 'foo'\n" + 1120 | // '- }' 1121 | } 1122 | ); 1123 | 1124 | actual = 'foobar'; 1125 | const message = 'message'; 1126 | assert.throws( 1127 | () => assert.throws( 1128 | () => { throw actual; }, 1129 | { message: 'foobar' }, 1130 | message 1131 | ), 1132 | { 1133 | actual, 1134 | message, 1135 | // operator: 'throws', 1136 | generatedMessage: false 1137 | } 1138 | ); 1139 | } 1140 | 1141 | // Indicate where the strings diverge. 1142 | assert.throws( 1143 | () => assert.strictEqual('test test', 'test foobar'), 1144 | { 1145 | code: 'ERR_ASSERTION', 1146 | name: 'AssertionError', 1147 | // message: strictEqualMessageStart + 1148 | // '+ actual - expected\n\n' + 1149 | // "+ 'test test'\n" + 1150 | // "- 'test foobar'\n" + 1151 | // ' ^' 1152 | } 1153 | ); 1154 | 1155 | // Check for reference-equal objects in `notStrictEqual()` 1156 | assert.throws( 1157 | () => { 1158 | const obj = {}; 1159 | assert.notStrictEqual(obj, obj); 1160 | }, 1161 | { 1162 | code: 'ERR_ASSERTION', 1163 | name: 'AssertionError', 1164 | // message: 'Expected "actual" not to be reference-equal to "expected": {}' 1165 | } 1166 | ); 1167 | 1168 | assert.throws( 1169 | () => { 1170 | const obj = { a: true }; 1171 | assert.notStrictEqual(obj, obj); 1172 | }, 1173 | { 1174 | code: 'ERR_ASSERTION', 1175 | name: 'AssertionError', 1176 | // message: 'Expected "actual" not to be reference-equal to "expected":\n\n' + 1177 | // '{\n a: true\n}\n' 1178 | } 1179 | ); 1180 | 1181 | { 1182 | let threw = false; 1183 | try { 1184 | assert.deepStrictEqual(arrayFill(Array(100), 1), 'foobar'); 1185 | } catch (err) { 1186 | threw = true; 1187 | // assert(/actual: \[Array],\n expected: 'foobar',/.test(inspect(err))); 1188 | } 1189 | assert(threw); 1190 | } 1191 | 1192 | assert.throws( 1193 | () => a.equal(1), 1194 | { code: 'ERR_MISSING_ARGS' } 1195 | ); 1196 | 1197 | assert.throws( 1198 | () => a.deepEqual(/a/), 1199 | { code: 'ERR_MISSING_ARGS' } 1200 | ); 1201 | 1202 | assert.throws( 1203 | () => a.notEqual(null), 1204 | { code: 'ERR_MISSING_ARGS' } 1205 | ); 1206 | 1207 | assert.throws( 1208 | () => a.notDeepEqual('test'), 1209 | { code: 'ERR_MISSING_ARGS' } 1210 | ); 1211 | 1212 | assert.throws( 1213 | () => a.strictEqual({}), 1214 | { code: 'ERR_MISSING_ARGS' } 1215 | ); 1216 | 1217 | if (common.symbolSupported) { 1218 | assert.throws( 1219 | () => a.deepStrictEqual(Symbol()), 1220 | { code: 'ERR_MISSING_ARGS' } 1221 | ); 1222 | } 1223 | 1224 | if (common.bigIntSupported) { 1225 | assert.throws( 1226 | () => a.notStrictEqual(eval('5n')), 1227 | { code: 'ERR_MISSING_ARGS' } 1228 | ); 1229 | } 1230 | 1231 | assert.throws( 1232 | () => a.notDeepStrictEqual(undefined), 1233 | { code: 'ERR_MISSING_ARGS' } 1234 | ); 1235 | 1236 | assert.throws( 1237 | () => a.strictEqual(), 1238 | { code: 'ERR_MISSING_ARGS' } 1239 | ); 1240 | 1241 | assert.throws( 1242 | () => a.deepStrictEqual(), 1243 | { code: 'ERR_MISSING_ARGS' } 1244 | ); 1245 | -------------------------------------------------------------------------------- /test/pseudo-tty/test-assert-colors.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/pseudo-tty/test-assert-colors.js 2 | // https://github.com/nodejs/node/commit/7f2d2cdc0cb45b8c97abf152b5cce6ec43aaaf79 3 | 4 | 'use strict'; 5 | require('../common'); 6 | const assert = require('../../assert').strict; 7 | 8 | try { 9 | // Activate colors even if the tty does not support colors. 10 | process.env.COLORTERM = '1'; 11 | // Make sure TERM is not set to e.g., 'dumb' and NODE_DISABLE_COLORS is not 12 | // active. 13 | process.env.TERM = 'FOOBAR'; 14 | delete process.env.NODE_DISABLE_COLORS; 15 | assert.deepStrictEqual([1, 2, 2, 2], [2, 2, 2, 2]); 16 | } catch (err) { 17 | const expected = 'Expected values to be strictly deep-equal:\n' + 18 | '\u001b[32m+ actual\u001b[39m \u001b[31m- expected\u001b[39m' + 19 | ' \u001b[34m...\u001b[39m Lines skipped\n\n' + 20 | ' [\n' + 21 | '\u001b[32m+\u001b[39m 1,\n' + 22 | '\u001b[31m-\u001b[39m 2,\n' + 23 | ' 2,\n' + 24 | '\u001b[34m...\u001b[39m\n' + 25 | ' 2\n' + 26 | ' ]'; 27 | // [browserify] 28 | // Don't test the exact message, it's inconstent between browsers. 29 | // assert.strictEqual(err.message, expected); 30 | if(process.stderr && process.stderr.getColorDepth) { 31 | assert(err.message.indexOf('[32m') > -1); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/pseudo-tty/test-assert-no-color.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/pseudo-tty/test-assert-no-color.js 2 | // https://github.com/nodejs/node/commit/a2c2c78e097c4e1036eb24abd620a52c709a9467 3 | 4 | 'use strict'; 5 | require('../common'); 6 | const assert = require('../../assert').strict; 7 | 8 | process.env.NODE_DISABLE_COLORS = true; 9 | 10 | try { 11 | assert.deepStrictEqual({}, { foo: 'bar' }); 12 | } catch (error) { 13 | const expected = 14 | 'Expected values to be strictly deep-equal:\n' + 15 | '+ actual - expected\n' + 16 | '\n' + 17 | '+ {}\n' + 18 | '- {\n' + 19 | '- foo: \'bar\'\n' + 20 | '- }'; 21 | // [browserify] 22 | // Don't test the exact message, it's inconstent between browsers. 23 | // assert.strictEqual(error.message, expected); 24 | assert(error.message.indexOf('[32m') === -1); 25 | } 26 | -------------------------------------------------------------------------------- /test/pseudo-tty/test-assert-position-indicator.js: -------------------------------------------------------------------------------- 1 | // Currently in sync with Node.js test/pseudo-tty/test-assert-position-indicator.js 2 | // https://github.com/nodejs/node/commit/40a8a7391664e7a5d8a264a1d85d059f9c05063b 3 | 4 | 'use strict'; 5 | const common = require('../common'); 6 | const assert = require('../../assert'); 7 | 8 | process.env.NODE_DISABLE_COLORS = true; 9 | if (!process.stderr) { 10 | process.stderr = {}; 11 | } 12 | process.stderr.isTTY = true; 13 | process.stderr.columns = 20; 14 | 15 | // Confirm that there is no position indicator. 16 | assert.throws( 17 | () => { assert.deepStrictEqual('a'.repeat(30), 'a'.repeat(31)); }, 18 | (err) => !common.includes(err.message, '^') 19 | ); 20 | 21 | // Confirm that there is a position indicator. 22 | assert.throws( 23 | () => { assert.deepStrictEqual('aaa', 'aaaa'); }, 24 | (err) => common.includes(err.message, '^') 25 | ); 26 | --------------------------------------------------------------------------------