diff --git a/.gitignore b/.gitignore index 9f563c04..d0a3f627 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ test/auth/* /chai.js /chai.cjs +/lib diff --git a/eslint.config.js b/eslint.config.js index 22339fce..10fc53fc 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,4 +1,5 @@ import jsdoc from "eslint-plugin-jsdoc"; +import {configs as tseslintConfigs} from 'typescript-eslint'; import eslintjs from "@eslint/js"; const {configs: eslintConfigs} = eslintjs; @@ -6,6 +7,7 @@ const {configs: eslintConfigs} = eslintjs; export default [ jsdoc.configs["flat/recommended"], eslintConfigs["recommended"], + ...tseslintConfigs.recommended, { languageOptions: { // if we ever use more globals than this, pull in the `globals` package @@ -17,10 +19,16 @@ export default [ "jsdoc/require-param-description": "off", "jsdoc/require-returns-description": "off", "jsdoc/tag-lines": ["error", "any", { startLines: 1 }], - "no-unused-vars": ["error", { - argsIgnorePattern: "^_", - caughtErrorsIgnorePattern: "^_" - }] + + // temporary until we do a cleanup + "no-var": "off", + "@typescript-eslint/no-unsafe-function-type": "off", + "prefer-rest-params": "off", + "@typescript-eslint/no-unused-expressions": "off", + "@typescript-eslint/no-unused-vars": ["error", { + "argsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_" + }], }, }, ]; diff --git a/index.js b/index.js deleted file mode 100644 index ea487486..00000000 --- a/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from './lib/chai.js'; diff --git a/lib/chai/assertion.js b/lib/chai/assertion.js deleted file mode 100644 index 2969d3e9..00000000 --- a/lib/chai/assertion.js +++ /dev/null @@ -1,180 +0,0 @@ -/*! - * chai - * http://chaijs.com - * Copyright(c) 2011-2014 Jake Luer <jake@alogicalparadox.com> - * MIT Licensed - */ - -import {config} from './config.js'; -import {AssertionError} from 'assertion-error'; -import * as util from './utils/index.js'; - -/** - * Assertion Constructor - * - * Creates object for chaining. - * - * `Assertion` objects contain metadata in the form of flags. Three flags can - * be assigned during instantiation by passing arguments to this constructor: - * - * - `object`: This flag contains the target of the assertion. For example, in - * the assertion `expect(numKittens).to.equal(7);`, the `object` flag will - * contain `numKittens` so that the `equal` assertion can reference it when - * needed. - * - * - `message`: This flag contains an optional custom error message to be - * prepended to the error message that's generated by the assertion when it - * fails. - * - * - `ssfi`: This flag stands for "start stack function indicator". It - * contains a function reference that serves as the starting point for - * removing frames from the stack trace of the error that's created by the - * assertion when it fails. The goal is to provide a cleaner stack trace to - * end users by removing Chai's internal functions. Note that it only works - * in environments that support `Error.captureStackTrace`, and only when - * `Chai.config.includeStack` hasn't been set to `false`. - * - * - `lockSsfi`: This flag controls whether or not the given `ssfi` flag - * should retain its current value, even as assertions are chained off of - * this object. This is usually set to `true` when creating a new assertion - * from within another assertion. It's also temporarily set to `true` before - * an overwritten assertion gets called by the overwriting assertion. - * - * - `eql`: This flag contains the deepEqual function to be used by the assertion. - * - * @param {unknown} obj target of the assertion - * @param {string} msg (optional) custom error message - * @param {Function} ssfi (optional) starting point for removing stack frames - * @param {boolean} lockSsfi (optional) whether or not the ssfi flag is locked - * @returns {unknown} - * @private - */ -export function Assertion(obj, msg, ssfi, lockSsfi) { - util.flag(this, 'ssfi', ssfi || Assertion); - util.flag(this, 'lockSsfi', lockSsfi); - util.flag(this, 'object', obj); - util.flag(this, 'message', msg); - util.flag(this, 'eql', config.deepEqual || util.eql); - - return util.proxify(this); -} - -Object.defineProperty(Assertion, 'includeStack', { - get: function () { - console.warn( - 'Assertion.includeStack is deprecated, use chai.config.includeStack instead.' - ); - return config.includeStack; - }, - set: function (value) { - console.warn( - 'Assertion.includeStack is deprecated, use chai.config.includeStack instead.' - ); - config.includeStack = value; - } -}); - -Object.defineProperty(Assertion, 'showDiff', { - get: function () { - console.warn( - 'Assertion.showDiff is deprecated, use chai.config.showDiff instead.' - ); - return config.showDiff; - }, - set: function (value) { - console.warn( - 'Assertion.showDiff is deprecated, use chai.config.showDiff instead.' - ); - config.showDiff = value; - } -}); - -Assertion.addProperty = function (name, fn) { - util.addProperty(this.prototype, name, fn); -}; - -Assertion.addMethod = function (name, fn) { - util.addMethod(this.prototype, name, fn); -}; - -Assertion.addChainableMethod = function (name, fn, chainingBehavior) { - util.addChainableMethod(this.prototype, name, fn, chainingBehavior); -}; - -Assertion.overwriteProperty = function (name, fn) { - util.overwriteProperty(this.prototype, name, fn); -}; - -Assertion.overwriteMethod = function (name, fn) { - util.overwriteMethod(this.prototype, name, fn); -}; - -Assertion.overwriteChainableMethod = function (name, fn, chainingBehavior) { - util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior); -}; - -/** - * ### .assert(expression, message, negateMessage, expected, actual, showDiff) - * - * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. - * - * @name assert - * @param {unknown} expression to be tested - * @param {string | Function} message or function that returns message to display if expression fails - * @param {string | Function} negatedMessage or function that returns negatedMessage to display if negated expression fails - * @param {unknown} expected value (remember to check for negation) - * @param {unknown} actual (optional) will default to `this.obj` - * @param {boolean} showDiff (optional) when set to `true`, assert will display a diff in addition to the message if expression fails - * @private - */ - -Assertion.prototype.assert = function ( - expr, - msg, - negateMsg, - expected, - _actual, - showDiff -) { - var ok = util.test(this, arguments); - if (false !== showDiff) showDiff = true; - if (undefined === expected && undefined === _actual) showDiff = false; - if (true !== config.showDiff) showDiff = false; - - if (!ok) { - msg = util.getMessage(this, arguments); - var actual = util.getActual(this, arguments); - var assertionErrorObjectProperties = { - actual: actual, - expected: expected, - showDiff: showDiff - }; - - var operator = util.getOperator(this, arguments); - if (operator) { - assertionErrorObjectProperties.operator = operator; - } - - throw new AssertionError( - msg, - assertionErrorObjectProperties, - config.includeStack ? this.assert : util.flag(this, 'ssfi') - ); - } -}; - -/** - * ### ._obj - * - * Quick reference to stored `actual` value for plugin developers. - * - * @private - */ -Object.defineProperty(Assertion.prototype, '_obj', { - get: function () { - return util.flag(this, 'object'); - }, - set: function (val) { - util.flag(this, 'object', val); - } -}); diff --git a/lib/chai/utils/flag.js b/lib/chai/utils/flag.js deleted file mode 100644 index 89434b71..00000000 --- a/lib/chai/utils/flag.js +++ /dev/null @@ -1,33 +0,0 @@ -/*! - * Chai - flag utility - * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> - * MIT Licensed - */ - -/** - * ### .flag(object, key, [value]) - * - * Get or set a flag value on an object. If a - * value is provided it will be set, else it will - * return the currently set value or `undefined` if - * the value is not set. - * - * utils.flag(this, 'foo', 'bar'); // setter - * utils.flag(this, 'foo'); // getter, returns `bar` - * - * @param {object} obj object constructed Assertion - * @param {string} key - * @param {unknown} value (optional) - * @namespace Utils - * @name flag - * @returns {unknown | undefined} - * @private - */ -export function flag(obj, key, value) { - var flags = obj.__flags || (obj.__flags = Object.create(null)); - if (arguments.length === 3) { - flags[key] = value; - } else { - return flags[key]; - } -} diff --git a/package-lock.json b/package-lock.json index f44d3140..71f58804 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "0.0.0-development", "license": "MIT", "dependencies": { + "@types/check-error": "^1.0.3", + "@types/deep-eql": "^4.0.2", + "@types/pathval": "^1.1.2", "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", @@ -25,7 +28,9 @@ "eslint": "^8.56.0", "eslint-plugin-jsdoc": "^48.0.4", "mocha": "^10.2.0", - "prettier": "^3.4.2" + "prettier": "^3.4.2", + "typescript": "^5.3.3", + "typescript-eslint": "^8.20.0" }, "engines": { "node": ">=12" @@ -550,9 +555,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", + "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", "dev": true, "license": "MIT", "engines": { @@ -560,12 +565,14 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -578,6 +585,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -588,6 +596,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -609,10 +618,12 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", @@ -1132,6 +1143,12 @@ "@types/node": "*" } }, + "node_modules/@types/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-xq2qg/lG9b/73i4id0YJPILVK/5W2yZV/LkMzY5BKQ27n0GJqY6HFtaLyK2ff0qnxa03W7AerOhhnAM68DUAZw==", + "license": "MIT" + }, "node_modules/@types/co-body": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/@types/co-body/-/co-body-6.1.3.tgz", @@ -1187,6 +1204,12 @@ "integrity": "sha512-jBqiORIzKDOToaF63Fm//haOCHuwQuLa2202RK4MozpA6lh93eCBc+/8+wZn5OzjJt3ySdc+74SXWXB55Ewtyw==", "dev": true }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -1305,6 +1328,12 @@ "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", "dev": true }, + "node_modules/@types/pathval": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/pathval/-/pathval-1.1.2.tgz", + "integrity": "sha512-Lecyi0eLLv4ERUAXtmqvYfoy+VxtLwYqitXfAwCJk8IY0t0OWQu2ptJNEFTr6v8qUFhq41on7UFfdvSV+wB8RQ==", + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.9.11", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", @@ -1364,6 +1393,215 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", + "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/type-utils": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", + "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", + "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", + "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", + "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", + "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", + "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", + "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.20.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -2884,16 +3122,18 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -3002,9 +3242,9 @@ } }, "node_modules/eslint/node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { @@ -3822,10 +4062,11 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -6273,6 +6514,19 @@ "node": ">=12" } }, + "node_modules/ts-api-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/tslib": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", @@ -6326,6 +6580,43 @@ "node": ">= 0.6" } }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.20.0.tgz", + "integrity": "sha512-Kxz2QRFsgbWj6Xcftlw3Dd154b3cEPFqQC+qMZrMypSijPd4UanKKvoKDrJ4o8AIfZFKAF+7sMaEIR8mTElozA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.20.0", + "@typescript-eslint/parser": "8.20.0", + "@typescript-eslint/utils": "8.20.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, "node_modules/typical": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", diff --git a/package.json b/package.json index 4af256d8..bb371dd3 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,11 @@ "name": "chai", "type": "module", "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.", + "files": [ + "lib", + "/chai.js", + "/register-*" + ], "keywords": [ "test", "assertion", @@ -30,21 +35,25 @@ "scripts": { "prebuild": "npm run clean", "build": "npm run build:esm", - "build:esm": "esbuild --bundle --format=esm --keep-names --outfile=chai.js index.js", - "format": "prettier --write lib", + "build:esm": "esbuild --bundle --format=esm --keep-names --outfile=chai.js src/chai.ts", + "format": "prettier --write src", "pretest": "npm run lint && npm run build", "test": "npm run test-node && npm run test-chrome", "test-node": "mocha --require ./test/bootstrap/index.js --reporter dot test/*.js", "test-chrome": "web-test-runner --playwright", - "lint": "npm run lint:js && npm run lint:format", - "lint:js": "eslint lib/", - "lint:format": "prettier --check lib", - "clean": "rm -f chai.js coverage" + "lint": "npm run lint:js && npm run lint:format && npm run lint:types", + "lint:js": "eslint src", + "lint:format": "prettier --check src", + "lint:types": "tsc --noEmit", + "clean": "rm -rf chai.js coverage lib" }, "engines": { "node": ">=12" }, "dependencies": { + "@types/check-error": "^1.0.3", + "@types/deep-eql": "^4.0.2", + "@types/pathval": "^1.1.2", "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", @@ -61,6 +70,8 @@ "eslint": "^8.56.0", "eslint-plugin-jsdoc": "^48.0.4", "mocha": "^10.2.0", + "typescript": "^5.3.3", + "typescript-eslint": "^8.20.0", "prettier": "^3.4.2" } } diff --git a/register-assert.js b/register-assert.js index f593717e..6390068f 100644 --- a/register-assert.js +++ b/register-assert.js @@ -1,3 +1,3 @@ -import {assert} from './index.js'; +import {assert} from './chai.js'; globalThis.assert = assert; diff --git a/register-expect.js b/register-expect.js index 2807b89b..90a87ff3 100644 --- a/register-expect.js +++ b/register-expect.js @@ -1,3 +1,3 @@ -import {expect} from './index.js'; +import {expect} from './chai.js'; globalThis.expect = expect; diff --git a/register-should.js b/register-should.js index 1339ee4c..d4f48d67 100644 --- a/register-should.js +++ b/register-should.js @@ -1,3 +1,3 @@ -import {should} from './index.js'; +import {should} from './chai.js'; globalThis.should = should(); diff --git a/lib/chai.js b/src/chai.ts similarity index 88% rename from lib/chai.js rename to src/chai.ts index 19186896..c6a1154d 100644 --- a/lib/chai.js +++ b/src/chai.ts @@ -13,7 +13,9 @@ import {Assertion} from './chai/assertion.js'; import * as should from './chai/interface/should.js'; import {assert} from './chai/interface/assert.js'; -const used = []; +type UseFn = (exports: Record<PropertyKey, unknown>, u: typeof util) => void; + +const used: Array<UseFn> = []; // Assertion Error export {AssertionError}; @@ -27,7 +29,7 @@ export {AssertionError}; * @returns {this} for chaining * @public */ -export function use(fn) { +export function use(fn: UseFn): Record<string, unknown> { const exports = { use, AssertionError, diff --git a/src/chai/assertion.ts b/src/chai/assertion.ts new file mode 100644 index 00000000..4a13781f --- /dev/null +++ b/src/chai/assertion.ts @@ -0,0 +1,250 @@ +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer <jake@alogicalparadox.com> + * MIT Licensed + */ + +import {config} from './config.js'; +import {AssertionError} from 'assertion-error'; +import * as util from './utils/index.js'; +import {ChainableBehavior} from './utils/chainableBehavior.js'; + +export interface AssertionFlags<T> { + ssfi: Function; + lockSsfi?: boolean; + object: T; + message?: string | null; + eql: (a: unknown, b: unknown) => boolean; + deltaBehavior?: string; + nested?: boolean; + deep?: boolean; + [key: PropertyKey]: unknown; +} + +type MethodNames<T> = { + [k in keyof T]: T[k] extends (...args: never) => void ? k : never; +}[keyof T]; + +const getDefaultValue = <T>(assertion: Assertion<T>): Assertion<T> => { + var newAssertion = Assertion.create<T>(); + util.transferFlags(assertion, newAssertion); + return newAssertion; +}; + +/*! + * Assertion Constructor + * + * Creates object for chaining. + * + * `Assertion` objects contain metadata in the form of flags. Three flags can + * be assigned during instantiation by passing arguments to this constructor: + * + * - `object`: This flag contains the target of the assertion. For example, in + * the assertion `expect(numKittens).to.equal(7);`, the `object` flag will + * contain `numKittens` so that the `equal` assertion can reference it when + * needed. + * + * - `message`: This flag contains an optional custom error message to be + * prepended to the error message that's generated by the assertion when it + * fails. + * + * - `ssfi`: This flag stands for "start stack function indicator". It + * contains a function reference that serves as the starting point for + * removing frames from the stack trace of the error that's created by the + * assertion when it fails. The goal is to provide a cleaner stack trace to + * end users by removing Chai's internal functions. Note that it only works + * in environments that support `Error.captureStackTrace`, and only when + * `Chai.config.includeStack` hasn't been set to `false`. + * + * - `lockSsfi`: This flag controls whether or not the given `ssfi` flag + * should retain its current value, even as assertions are chained off of + * this object. This is usually set to `true` when creating a new assertion + * from within another assertion. It's also temporarily set to `true` before + * an overwritten assertion gets called by the overwriting assertion. + * + * - `eql`: This flag contains the deepEqual function to be used by the assertion. + * + * @param {unknown} obj target of the assertion + * @param {string} msg (optional) custom error message + * @param {Function} ssfi (optional) starting point for removing stack frames + * @param {boolean} lockSsfi (optional) whether or not the ssfi flag is locked + * @private + */ +export class Assertion< + T, + TFlags extends AssertionFlags<T> = AssertionFlags<T> +> { + declare public __flags: TFlags; + public __methods: Record<string, ChainableBehavior> = {}; + + public constructor( + obj?: unknown, + msg?: string | null, + ssfi?: Function, + lockSsfi?: boolean + ) { + util.flag(this, 'ssfi', ssfi || Assertion); + util.flag(this, 'lockSsfi', lockSsfi); + util.flag(this, 'object', obj); + util.flag(this, 'message', msg); + util.flag(this, 'eql', config.deepEqual ?? util.eql); + } + + public static create<TObj>( + obj?: TObj, + msg?: string | null, + ssfi?: Function, + lockSsfi?: boolean + ): Assertion<TObj> { + return util.proxify(new Assertion<TObj>(obj, msg, ssfi, lockSsfi)); + } + + public static get includeStack(): boolean { + console.warn( + 'Assertion.includeStack is deprecated, use chai.config.includeStack instead.' + ); + return config.includeStack; + } + + public static set includeStack(value: boolean) { + console.warn( + 'Assertion.includeStack is deprecated, use chai.config.includeStack instead.' + ); + config.includeStack = value; + } + + public static get showDiff(): boolean { + console.warn( + 'Assertion.showDiff is deprecated, use chai.config.showDiff instead.' + ); + return config.showDiff; + } + + public static set showDiff(value: boolean) { + console.warn( + 'Assertion.showDiff is deprecated, use chai.config.showDiff instead.' + ); + config.showDiff = value; + } + + public static addProperty( + name: string, + fn?: (this: Assertion<unknown>) => unknown + ): void { + util.addProperty(this.prototype, name, fn, getDefaultValue); + } + + public static addMethod<TKey extends PropertyKey>( + name: TKey, + fn: TKey extends MethodNames<Assertion<unknown>> + ? ( + this: Assertion<unknown>, + ...args: Parameters<Assertion<unknown>[TKey]> + ) => ReturnType<Assertion<unknown>[TKey]> | void + : (this: Assertion<unknown>, ...args: never) => unknown + ): void { + util.addMethod(this.prototype, name, fn, getDefaultValue); + } + + public static addChainableMethod<T extends unknown[]>( + name: string, + fn: (this: Assertion<unknown>, ...args: T) => unknown, + chainingBehavior?: () => void + ): void { + util.addChainableMethod( + this.prototype, + name, + fn, + chainingBehavior, + getDefaultValue + ); + } + + public static overwriteProperty(name: string, fn: Function): void { + util.overwriteProperty(this.prototype, name, fn, getDefaultValue); + } + + public static overwriteMethod(name: string, fn: Function): void { + util.overwriteMethod(this.prototype, name, fn, getDefaultValue); + } + + public static overwriteChainableMethod( + name: string, + fn: Function, + chainingBehavior: Function + ): void { + util.overwriteChainableMethod( + this.prototype, + name, + fn, + chainingBehavior, + getDefaultValue + ); + } + + /** + * ### .assert(expression, message, negateMessage, expected, actual, showDiff) + * + * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. + * + * @name assert + * @param {unknown} _expr to be tested + * @param {string | Function} msg or function that returns message to display if expression fails + * @param {string | Function} _negateMsg or function that returns negatedMessage to display if negated expression fails + * @param {unknown} expected value (remember to check for negation) + * @param {unknown} _actual (optional) will default to `this.obj` + * @param {boolean} showDiff (optional) when set to `true`, assert will display a diff in addition to the message if expression fails + * @private + */ + public assert( + _expr: unknown, + msg?: string | (() => string), + _negateMsg?: string | (() => string), + expected?: unknown, + _actual?: unknown, + showDiff?: boolean + ): void { + var ok = util.test(this, arguments); + if (false !== showDiff) showDiff = true; + if (undefined === expected && undefined === _actual) showDiff = false; + if (true !== config.showDiff) showDiff = false; + + if (!ok) { + msg = util.getMessage(this, arguments); + var actual = util.getActual(this, arguments); + var assertionErrorObjectProperties: Record<string, unknown> = { + actual: actual, + expected: expected, + showDiff: showDiff, + operator: undefined + }; + + var operator = util.getOperator(this, arguments); + if (operator) { + assertionErrorObjectProperties.operator = operator; + } + + throw new AssertionError( + msg, + assertionErrorObjectProperties, + config.includeStack ? this.assert : util.flag(this, 'ssfi') + ); + } + } + + /*! + * ### ._obj + * + * Quick reference to stored `actual` value for plugin developers. + * + * @private + */ + public get _obj(): unknown { + return util.flag(this, 'object'); + } + + public set _obj(val: unknown) { + util.flag(this, 'object', val); + } +} diff --git a/lib/chai/config.js b/src/chai/config.ts similarity index 100% rename from lib/chai/config.js rename to src/chai/config.ts diff --git a/lib/chai/core/assertions.js b/src/chai/core/assertions.ts similarity index 82% rename from lib/chai/core/assertions.js rename to src/chai/core/assertions.ts index 80a1ce7e..74bf4a4c 100644 --- a/lib/chai/core/assertions.js +++ b/src/chai/core/assertions.ts @@ -5,12 +5,495 @@ * MIT Licensed */ -import {Assertion} from '../assertion.js'; +import {Assertion, AssertionFlags} from '../assertion.js'; import {AssertionError} from 'assertion-error'; import * as _ from '../utils/index.js'; +import { + OnlyIf, + Constructor, + LengthLike, + CollectionLike +} from '../utils/types.js'; const {flag} = _; +type ChainedMethod< + T, + TFlags extends AssertionFlags<T>, + TParams extends unknown[], + TReturn = Assertion<T> +> = Assertion<T, TFlags> & { + (...args: TParams): TReturn; +}; + +declare module '../assertion.js' { + interface Assertion<T, TFlags extends AssertionFlags<T> = AssertionFlags<T>> { + Arguments: Assertion<T, TFlags>; + NaN: Assertion<T, TFlags>; + all: Assertion<T, TFlags>; + also: Assertion<T, TFlags>; + and: Assertion<T, TFlags>; + any: Assertion<T, TFlags>; + arguments: Assertion<T, TFlags>; + at: Assertion<T, TFlags>; + be: Assertion<T, TFlags>; + been: Assertion<T, TFlags>; + but: Assertion<T, TFlags>; + callable: Assertion<T, TFlags>; + deep: Assertion<T, TFlags & {deep: true}>; + does: Assertion<T, TFlags>; + empty: Assertion<T, TFlags>; + exist: Assertion<T, TFlags>; + exists: Assertion<T, TFlags>; + extensible: Assertion<T, TFlags>; + false: Assertion<T, TFlags>; + finite: Assertion<T, TFlags>; + frozen: Assertion<T, TFlags>; + has: Assertion<T, TFlags>; + have: Assertion<T, TFlags>; + is: Assertion<T, TFlags>; + iterable: Assertion<T, TFlags>; + itself: Assertion<T, TFlags>; + nested: Assertion<T, TFlags & {nested: true}>; + not: Assertion<T, TFlags>; + null: Assertion<T, TFlags>; + numeric: Assertion<T, TFlags>; + of: Assertion<T, TFlags>; + ok: Assertion<T, TFlags>; + ordered: Assertion<T, TFlags>; + own: Assertion<T, TFlags>; + same: Assertion<T, TFlags>; + sealed: Assertion<T, TFlags>; + still: Assertion<T, TFlags>; + that: Assertion<T, TFlags>; + to: Assertion<T, TFlags>; + true: Assertion<T, TFlags>; + undefined: Assertion<T, TFlags>; + which: Assertion<T, TFlags>; + with: Assertion<T, TFlags>; + + a: ChainedMethod<T, TFlags, [type: string, msg?: string]>; + an: ChainedMethod<T, TFlags, [type: string, msg?: string]>; + + above: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + gt: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + greaterThan: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + + approximately: OnlyIf< + T, + number, + (expected: number, delta: number, msg?: string) => Assertion<T, TFlags> + >; + closeTo: OnlyIf< + T, + number, + (expected: number, delta: number, msg?: string) => Assertion<T, TFlags> + >; + + below: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + lt: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + lessThan: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + + by: OnlyIf< + TFlags, + {deltaBehavior: string}, + (delta: number, msg?: string) => Assertion<T, TFlags> + >; + + change: { + <TSubject>( + subject: TSubject, + prop: keyof TSubject, + msg?: string + ): Assertion<T, TFlags & {deltaBehavior: string}>; + (subject: Function): Assertion<T, TFlags & {deltaBehavior: string}>; + }; + changes: { + <TSubject>( + subject: TSubject, + prop: keyof TSubject, + msg?: string + ): Assertion<T, TFlags & {deltaBehavior: string}>; + (subject: Function): Assertion<T, TFlags & {deltaBehavior: string}>; + }; + + contain: OnlyIf< + T, + CollectionLike<never> | string | object, + ChainedMethod<T, TFlags, [val: unknown, msg?: string]> + >; + contains: OnlyIf< + T, + CollectionLike<never> | string | object, + ChainedMethod<T, TFlags, [val: unknown, msg?: string]> + >; + include: OnlyIf< + T, + CollectionLike<never> | string | object, + ChainedMethod<T, TFlags, [val: unknown, msg?: string]> + >; + includes: OnlyIf< + T, + CollectionLike<never> | string | object, + ChainedMethod<T, TFlags, [val: unknown, msg?: string]> + >; + + decrease: OnlyIf< + T, + Function, + { + (subject: Function): Assertion<T, TFlags & {deltaBehavior: string}>; + <TSubject>( + subject: TSubject, + prop: keyof TSubject, + msg?: string + ): Assertion<T, TFlags & {deltaBehavior: string}>; + } + >; + decreases: OnlyIf< + T, + Function, + { + (subject: Function): Assertion<T, TFlags & {deltaBehavior: string}>; + <TSubject>( + subject: TSubject, + prop: keyof TSubject, + msg?: string + ): Assertion<T, TFlags & {deltaBehavior: string}>; + } + >; + + eq: (val: T, msg?: string) => Assertion<T, TFlags>; + equal: (val: T, msg?: string) => Assertion<T, TFlags>; + equals: (val: T, msg?: string) => Assertion<T, TFlags>; + + eql: (val: T, msg?: string) => Assertion<T, TFlags>; + eqls: (val: T, msg?: string) => Assertion<T, TFlags>; + + greaterThanOrEqual: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + least: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + gte: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + + haveOwnProperty: OnlyIf< + T, + object, + { + <TKey extends keyof T>(name: TKey): Assertion<T[TKey]>; + (name: PropertyKey): Assertion<unknown>; + <TKey extends keyof T>( + name: TKey, + val: T[TKey], + msg?: string + ): Assertion<T[TKey]>; + } + >; + ownProperty: OnlyIf< + T, + object, + { + <TKey extends keyof T>(name: TKey): Assertion<T[TKey]>; + (name: PropertyKey): Assertion<unknown>; + <TKey extends keyof T>( + name: TKey, + val: T[TKey], + msg?: string + ): Assertion<T[TKey]>; + } + >; + + haveOwnPropertyDescriptor: ( + name: PropertyKey, + descriptor: PropertyDescriptor, + msg?: string + ) => Assertion<PropertyDescriptor>; + ownPropertyDescriptor: ( + name: PropertyKey, + descriptor: PropertyDescriptor, + msg?: string + ) => Assertion<PropertyDescriptor>; + + increase: OnlyIf< + T, + Function, + { + (subject: Function): Assertion<T, TFlags & {deltaBehavior: string}>; + <TSubject>( + subject: TSubject, + prop: keyof TSubject, + msg?: string + ): Assertion<T, TFlags & {deltaBehavior: string}>; + } + >; + increases: OnlyIf< + T, + Function, + { + (subject: Function): Assertion<T, TFlags & {deltaBehavior: string}>; + <TSubject>( + subject: TSubject, + prop: keyof TSubject, + msg?: string + ): Assertion<T, TFlags & {deltaBehavior: string}>; + } + >; + + instanceOf: ( + ctor: Constructor<unknown>, + msg?: string + ) => Assertion<T, TFlags>; + instanceof: ( + ctor: Constructor<unknown>, + msg?: string + ) => Assertion<T, TFlags>; + + keys: T extends Map<unknown, unknown> | Set<unknown> + ? { + (keys: unknown[]): Assertion<T, TFlags>; + (...keys: unknown[]): Assertion<T, TFlags>; + } + : T extends object + ? { + (keys: Record<PropertyKey, unknown>): Assertion<T, TFlags>; + (keys: PropertyKey[]): Assertion<T, TFlags>; + (...keys: PropertyKey[]): Assertion<T, TFlags>; + } + : never; + key: T extends Map<unknown, unknown> | Set<unknown> + ? { + (keys: unknown[]): Assertion<T, TFlags>; + (...keys: unknown[]): Assertion<T, TFlags>; + } + : T extends object + ? { + (keys: PropertyKey[]): Assertion<T, TFlags>; + (...keys: PropertyKey[]): Assertion<T, TFlags>; + } + : never; + + length: OnlyIf< + T, + LengthLike, + ChainedMethod< + T, + AssertionFlags<T> & {doLength: true}, + [n?: number, msg?: string], + Assertion<T, AssertionFlags<T> & {doLength: true}> + > + >; + lengthOf: OnlyIf< + T, + LengthLike, + ChainedMethod< + T, + AssertionFlags<T> & {doLength: true}, + [n?: number, msg?: string], + Assertion<T, AssertionFlags<T> & {doLength: true}> + > + >; + + lessThanOrEqual: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + lte: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + most: OnlyIf< + T, + Date | number, + (val: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (val: number, msg?: string) => Assertion<T, TFlags> + > + >; + + match: OnlyIf< + T, + string, + (re: RegExp, msg?: string) => Assertion<T, TFlags> + >; + matches: OnlyIf< + T, + string, + (re: RegExp, msg?: string) => Assertion<T, TFlags> + >; + + members: OnlyIf< + T, + unknown[], + (subset: T, msg?: string) => Assertion<T, TFlags> + >; + + oneOf: OnlyIf< + TFlags, + {deep: true}, + (list: unknown[], msg?: string) => Assertion<T, TFlags>, + OnlyIf< + T, + string | unknown[], + (list: unknown[], msg?: string) => Assertion<T, TFlags> + > + >; + + property: OnlyIf< + T, + object, + OnlyIf< + TFlags, + {nested: true}, + ( + name: string, + val?: unknown, + msg?: string + ) => Assertion<unknown, TFlags>, + { + <TKey extends keyof T>(name: TKey): Assertion<T[TKey]>; + (name: PropertyKey): Assertion<unknown>; + <TKey extends keyof T>( + name: TKey, + val: T[TKey], + msg?: string + ): Assertion<T[TKey]>; + } + > + >; + + respondTo: (method: string, msg?: string) => Assertion<T, TFlags>; + respondsTo: (method: string, msg?: string) => Assertion<T, TFlags>; + + satifies: (matcher: Function, msg?: string) => Assertion<T, TFlags>; + satisfy: (matcher: Function, msg?: string) => Assertion<T, TFlags>; + + string: (str: string, msg?: string) => Assertion<T, TFlags>; + + Throw: ((errMsgMatcher: string | RegExp) => Assertion<T, TFlags>) & + (( + errorLike: Error | Constructor<Error>, + errMsgMatcher: string | RegExp, + msg?: string + ) => Assertion<T, TFlags>); + throws: ((errMsgMatcher: string | RegExp) => Assertion<T, TFlags>) & + (( + errorLike: Error | Constructor<Error>, + errMsgMatcher: string | RegExp, + msg?: string + ) => Assertion<T, TFlags>); + throw: ((errMsgMatcher: string | RegExp) => Assertion<T, TFlags>) & + (( + errorLike: Error | Constructor<Error>, + errMsgMatcher: string | RegExp, + msg?: string + ) => Assertion<T, TFlags>); + + within: OnlyIf< + T, + Date | number, + (start: T, finish: T, msg?: string) => Assertion<T, TFlags>, + OnlyIf< + TFlags, + {doLength: true}, + (start: number, finish: number, msg?: string) => Assertion<T, TFlags> + > + >; + } +} + /** * ### Language Chains * @@ -253,7 +736,7 @@ Assertion.addProperty('all', function () { flag(this, 'any', false); }); -const functionTypes = { +const functionTypes: Record<string, string[]> = { function: [ 'function', 'asyncfunction', @@ -324,7 +807,7 @@ const functionTypes = { * @namespace BDD * @public */ -function an(type, msg) { +function an(this: Assertion<unknown>, type: string, msg?: string) { if (msg) flag(this, 'message', msg); type = type.toLowerCase(); var obj = flag(this, 'object'), @@ -356,14 +839,14 @@ Assertion.addChainableMethod('a', an); * @param {unknown} b * @returns {boolean} */ -function SameValueZero(a, b) { +function SameValueZero(a: unknown, b: unknown): boolean { return (_.isNaN(a) && _.isNaN(b)) || a === b; } /** - * + * Flags that this assertion should operate using the contains logic */ -function includeChainingBehavior() { +function includeChainingBehavior(this: Assertion<unknown>): void { flag(this, 'contains', true); } @@ -513,7 +996,7 @@ function includeChainingBehavior() { * @namespace BDD * @public */ -function include(val, msg) { +function include<T>(this: Assertion<unknown>, val: T, msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), @@ -531,7 +1014,7 @@ function include(val, msg) { switch (objType) { case 'string': - included = obj.indexOf(val) !== -1; + included = (obj as string).indexOf(val as string) !== -1; break; case 'weakset': @@ -543,32 +1026,32 @@ function include(val, msg) { ); } - included = obj.has(val); + included = (obj as WeakSet<object>).has(val as object); break; case 'map': - obj.forEach(function (item) { + (obj as Map<unknown, unknown>).forEach(function (item) { included = included || isEql(item, val); }); break; case 'set': if (isDeep) { - obj.forEach(function (item) { + (obj as Set<unknown>).forEach(function (item) { included = included || isEql(item, val); }); } else { - included = obj.has(val); + included = (obj as Set<unknown>).has(val); } break; case 'array': if (isDeep) { - included = obj.some(function (item) { + included = (obj as Array<unknown>).some(function (item) { return isEql(item, val); }); } else { - included = obj.indexOf(val) !== -1; + included = (obj as Array<unknown>).indexOf(val) !== -1; } break; @@ -593,24 +1076,34 @@ function include(val, msg) { ); } - var props = Object.keys(val), - firstErr = null, + var props = Object.keys(val as object), + firstErr: unknown = null, numErrs = 0; - props.forEach(function (prop) { - var propAssertion = new Assertion(obj); + props.forEach(function (this: Assertion<unknown>, prop) { + var propAssertion = Assertion.create( + obj as Record<PropertyKey, unknown> + ); _.transferFlags(this, propAssertion, true); flag(propAssertion, 'lockSsfi', true); if (!negate || props.length === 1) { - propAssertion.property(prop, val[prop]); + propAssertion.property( + prop, + (val as Record<PropertyKey, unknown>)[prop] + ); return; } try { - propAssertion.property(prop, val[prop]); + propAssertion.property( + prop, + (val as Record<PropertyKey, unknown>)[prop] + ); } catch (err) { - if (!_.checkError.compatibleConstructor(err, AssertionError)) { + if ( + !_.checkError.compatibleConstructor(err as Error, AssertionError) + ) { throw err; } if (firstErr === null) firstErr = err; @@ -923,7 +1416,7 @@ Assertion.addProperty('NaN', function () { * @namespace BDD * @public */ -function assertExist() { +function assertExist(this: Assertion<unknown>) { var val = flag(this, 'object'); this.assert( val !== null && val !== undefined, @@ -994,11 +1487,11 @@ Assertion.addProperty('empty', function () { switch (_.type(val).toLowerCase()) { case 'array': case 'string': - itemsCount = val.length; + itemsCount = (val as Array<unknown> | string).length; break; case 'map': case 'set': - itemsCount = val.size; + itemsCount = (val as Map<unknown, unknown> | Set<unknown>).size; break; case 'weakmap': case 'weakset': @@ -1008,7 +1501,8 @@ Assertion.addProperty('empty', function () { ssfi ); case 'function': - var msg = flagMsg + '.empty was passed a function ' + _.getName(val); + var msg = + flagMsg + '.empty was passed a function ' + _.getName(val as Function); throw new AssertionError(msg.trim(), undefined, ssfi); default: if (val !== Object(val)) { @@ -1018,7 +1512,7 @@ Assertion.addProperty('empty', function () { ssfi ); } - itemsCount = Object.keys(val).length; + itemsCount = Object.keys(val as object).length; } this.assert( @@ -1057,7 +1551,7 @@ Assertion.addProperty('empty', function () { * @namespace BDD * @public */ -function checkArguments() { +function checkArguments(this: Assertion<unknown>) { var obj = flag(this, 'object'), type = _.type(obj); this.assert( @@ -1114,7 +1608,7 @@ Assertion.addProperty('Arguments', checkArguments); * @namespace BDD * @public */ -function assertEqual(val, msg) { +function assertEqual(this: Assertion<unknown>, val: unknown, msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'); if (flag(this, 'deep')) { @@ -1178,7 +1672,7 @@ Assertion.addMethod('eq', assertEqual); * @namespace BDD * @public */ -function assertEql(obj, msg) { +function assertEql(this: Assertion<unknown>, obj: unknown, msg?: string) { if (msg) flag(this, 'message', msg); var eql = flag(this, 'eql'); this.assert( @@ -1236,7 +1730,7 @@ Assertion.addMethod('eqls', assertEql); * @namespace BDD * @public */ -function assertAbove(n, msg) { +function assertAbove(this: Assertion<unknown>, n: Date | number, msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), doLength = flag(this, 'doLength'), @@ -1247,7 +1741,9 @@ function assertAbove(n, msg) { nType = _.type(n).toLowerCase(); if (doLength && objType !== 'map' && objType !== 'set') { - new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + Assertion.create(obj as object, flagMsg, ssfi, true).to.have.property( + 'length' + ); } if (!doLength && objType === 'date' && nType !== 'date') { @@ -1276,12 +1772,12 @@ function assertAbove(n, msg) { itemsCount; if (objType === 'map' || objType === 'set') { descriptor = 'size'; - itemsCount = obj.size; + itemsCount = (obj as Set<unknown> | Map<unknown, unknown>).size; } else { - itemsCount = obj.length; + itemsCount = (obj as Array<unknown>).length; } this.assert( - itemsCount > n, + itemsCount > (n as number), 'expected #{this} to have a ' + descriptor + ' above #{exp} but got #{act}', @@ -1291,7 +1787,7 @@ function assertAbove(n, msg) { ); } else { this.assert( - obj > n, + (obj as number) > (n as number), 'expected #{this} to be above #{exp}', 'expected #{this} to be at most #{exp}', n @@ -1346,7 +1842,7 @@ Assertion.addMethod('greaterThan', assertAbove); * @namespace BDD * @public */ -function assertLeast(n, msg) { +function assertLeast(this: Assertion<unknown>, n: unknown, msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), doLength = flag(this, 'doLength'), @@ -1355,11 +1851,13 @@ function assertLeast(n, msg) { ssfi = flag(this, 'ssfi'), objType = _.type(obj).toLowerCase(), nType = _.type(n).toLowerCase(), - errorMessage, + errorMessage = 'unknown error', shouldThrow = true; if (doLength && objType !== 'map' && objType !== 'set') { - new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + Assertion.create(obj as object, flagMsg, ssfi, true).to.have.property( + 'length' + ); } if (!doLength && objType === 'date' && nType !== 'date') { @@ -1383,12 +1881,12 @@ function assertLeast(n, msg) { itemsCount; if (objType === 'map' || objType === 'set') { descriptor = 'size'; - itemsCount = obj.size; + itemsCount = (obj as Set<unknown> | Map<unknown, unknown>).size; } else { - itemsCount = obj.length; + itemsCount = (obj as Array<unknown>).length; } this.assert( - itemsCount >= n, + itemsCount >= (n as number), 'expected #{this} to have a ' + descriptor + ' at least #{exp} but got #{act}', @@ -1398,7 +1896,7 @@ function assertLeast(n, msg) { ); } else { this.assert( - obj >= n, + (obj as number) >= (n as number), 'expected #{this} to be at least #{exp}', 'expected #{this} to be below #{exp}', n @@ -1452,7 +1950,7 @@ Assertion.addMethod('greaterThanOrEqual', assertLeast); * @namespace BDD * @public */ -function assertBelow(n, msg) { +function assertBelow(this: Assertion<unknown>, n: unknown, msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), doLength = flag(this, 'doLength'), @@ -1461,11 +1959,13 @@ function assertBelow(n, msg) { ssfi = flag(this, 'ssfi'), objType = _.type(obj).toLowerCase(), nType = _.type(n).toLowerCase(), - errorMessage, + errorMessage = 'unknown error', shouldThrow = true; if (doLength && objType !== 'map' && objType !== 'set') { - new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + Assertion.create(obj as object, flagMsg, ssfi, true).to.have.property( + 'length' + ); } if (!doLength && objType === 'date' && nType !== 'date') { @@ -1489,12 +1989,12 @@ function assertBelow(n, msg) { itemsCount; if (objType === 'map' || objType === 'set') { descriptor = 'size'; - itemsCount = obj.size; + itemsCount = (obj as Set<unknown> | Map<unknown, unknown>).size; } else { - itemsCount = obj.length; + itemsCount = (obj as Array<unknown>).length; } this.assert( - itemsCount < n, + itemsCount < (n as number), 'expected #{this} to have a ' + descriptor + ' below #{exp} but got #{act}', @@ -1504,7 +2004,7 @@ function assertBelow(n, msg) { ); } else { this.assert( - obj < n, + (obj as number) < (n as number), 'expected #{this} to be below #{exp}', 'expected #{this} to be at least #{exp}', n @@ -1559,7 +2059,7 @@ Assertion.addMethod('lessThan', assertBelow); * @namespace BDD * @public */ -function assertMost(n, msg) { +function assertMost(this: Assertion<unknown>, n: unknown, msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), doLength = flag(this, 'doLength'), @@ -1568,11 +2068,13 @@ function assertMost(n, msg) { ssfi = flag(this, 'ssfi'), objType = _.type(obj).toLowerCase(), nType = _.type(n).toLowerCase(), - errorMessage, + errorMessage = 'unknown error', shouldThrow = true; if (doLength && objType !== 'map' && objType !== 'set') { - new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + Assertion.create(obj as object, flagMsg, ssfi, true).to.have.property( + 'length' + ); } if (!doLength && objType === 'date' && nType !== 'date') { @@ -1596,12 +2098,12 @@ function assertMost(n, msg) { itemsCount; if (objType === 'map' || objType === 'set') { descriptor = 'size'; - itemsCount = obj.size; + itemsCount = (obj as Set<unknown> | Map<unknown, unknown>).size; } else { - itemsCount = obj.length; + itemsCount = (obj as Array<unknown>).length; } this.assert( - itemsCount <= n, + itemsCount <= (n as number), 'expected #{this} to have a ' + descriptor + ' at most #{exp} but got #{act}', @@ -1611,7 +2113,7 @@ function assertMost(n, msg) { ); } else { this.assert( - obj <= n, + (obj as number) <= (n as number), 'expected #{this} to be at most #{exp}', 'expected #{this} to be above #{exp}', n @@ -1665,7 +2167,14 @@ Assertion.addMethod('lessThanOrEqual', assertMost); * @namespace BDD * @public */ -Assertion.addMethod('within', function (start, finish, msg) { +function assertWithin(start: number, finish: number, msg?: string): void; +function assertWithin(start: Date, finish: Date, msg?: string): void; +function assertWithin( + this: Assertion<unknown>, + start: number | Date, + finish: number | Date, + msg?: string +): void { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), doLength = flag(this, 'doLength'), @@ -1675,15 +2184,17 @@ Assertion.addMethod('within', function (start, finish, msg) { objType = _.type(obj).toLowerCase(), startType = _.type(start).toLowerCase(), finishType = _.type(finish).toLowerCase(), - errorMessage, + errorMessage = 'unknown error', shouldThrow = true, range = startType === 'date' && finishType === 'date' - ? start.toISOString() + '..' + finish.toISOString() + ? (start as Date).toISOString() + '..' + (finish as Date).toISOString() : start + '..' + finish; if (doLength && objType !== 'map' && objType !== 'set') { - new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + Assertion.create(obj as object, flagMsg, ssfi, true).to.have.property( + 'length' + ); } if ( @@ -1714,23 +2225,26 @@ Assertion.addMethod('within', function (start, finish, msg) { itemsCount; if (objType === 'map' || objType === 'set') { descriptor = 'size'; - itemsCount = obj.size; + itemsCount = (obj as Set<unknown> | Map<string, unknown>).size; } else { - itemsCount = obj.length; + itemsCount = (obj as Array<unknown>).length; } this.assert( - itemsCount >= start && itemsCount <= finish, + itemsCount >= (start as number) && itemsCount <= (finish as number), 'expected #{this} to have a ' + descriptor + ' within ' + range, 'expected #{this} to not have a ' + descriptor + ' within ' + range ); } else { this.assert( - obj >= start && obj <= finish, + (obj as number) >= (start as number) && + (obj as number) <= (finish as number), 'expected #{this} to be within ' + range, 'expected #{this} to not be within ' + range ); } -}); +} + +Assertion.addMethod('within', assertWithin); /** * ### .instanceof(constructor[, msg]) @@ -1770,7 +2284,11 @@ Assertion.addMethod('within', function (start, finish, msg) { * @namespace BDD * @public */ -function assertInstanceOf(constructor, msg) { +function assertInstanceOf( + this: Assertion<unknown>, + constructor: {new (): unknown}, + msg?: string +) { if (msg) flag(this, 'message', msg); var target = flag(this, 'object'); @@ -1919,7 +2437,12 @@ Assertion.addMethod('instanceOf', assertInstanceOf); * @namespace BDD * @public */ -function assertProperty(name, val, msg) { +function assertProperty( + this: Assertion<unknown>, + name: PropertyKey, + val?: unknown, + msg?: string +) { if (msg) flag(this, 'message', msg); var isNested = flag(this, 'nested'), @@ -1973,9 +2496,19 @@ function assertProperty(name, val, msg) { var isDeep = flag(this, 'deep'), negate = flag(this, 'negate'), - pathInfo = isNested ? _.getPathInfo(obj, name) : null, - value = isNested ? pathInfo.value : obj[name], - isEql = isDeep ? flag(this, 'eql') : (val1, val2) => val1 === val2; + isEql = isDeep + ? flag(this, 'eql') + : (val1: unknown, val2: unknown): boolean => val1 === val2; + let pathInfo; + let value; + + if (isNested) { + pathInfo = _.getPathInfo(obj, name as string); + value = pathInfo.value; + } else { + pathInfo = null; + value = (obj as Record<PropertyKey, unknown>)[name]; + } var descriptor = ''; if (isDeep) descriptor += 'deep '; @@ -1985,7 +2518,7 @@ function assertProperty(name, val, msg) { var hasProperty; if (isOwn) hasProperty = Object.prototype.hasOwnProperty.call(obj, name); - else if (isNested) hasProperty = pathInfo.exists; + else if (isNested && pathInfo) hasProperty = pathInfo.exists; else hasProperty = _.hasProperty(obj, name); // When performing a negated assertion for both name and val, merely having @@ -2023,14 +2556,15 @@ function assertProperty(name, val, msg) { Assertion.addMethod('property', assertProperty); /** - * - * @param {unknown} _name - * @param {unknown} _value - * @param {string} _msg + * Asserts that an object has an own property */ -function assertOwnProperty(_name, _value, _msg) { +function assertOwnProperty(this: Assertion<unknown>) { flag(this, 'own', true); - assertProperty.apply(this, arguments); + // TODO (43081j): remove this highly questionable cast + assertProperty.apply( + this, + arguments as unknown as Parameters<typeof assertProperty> + ); } Assertion.addMethod('ownProperty', assertOwnProperty); @@ -2154,7 +2688,12 @@ Assertion.addMethod('haveOwnProperty', assertOwnProperty); * @namespace BDD * @public */ -function assertOwnPropertyDescriptor(name, descriptor, msg) { +function assertOwnPropertyDescriptor( + this: Assertion<unknown>, + name: PropertyKey, + descriptor?: unknown, + msg?: string +) { if (typeof descriptor === 'string') { msg = descriptor; descriptor = null; @@ -2196,9 +2735,9 @@ Assertion.addMethod('ownPropertyDescriptor', assertOwnPropertyDescriptor); Assertion.addMethod('haveOwnPropertyDescriptor', assertOwnPropertyDescriptor); /** - * + * Flags that this assertion should operate on the object's length */ -function assertLengthChain() { +function assertLengthChain(this: Assertion<unknown>) { flag(this, 'doLength', true); } @@ -2259,7 +2798,7 @@ function assertLengthChain() { * @namespace BDD * @public */ -function assertLength(n, msg) { +function assertLength(this: Assertion<unknown>, n: number, msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), objType = _.type(obj).toLowerCase(), @@ -2272,11 +2811,13 @@ function assertLength(n, msg) { case 'map': case 'set': descriptor = 'size'; - itemsCount = obj.size; + itemsCount = (obj as Set<unknown> | Map<unknown, unknown>).size; break; default: - new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); - itemsCount = obj.length; + Assertion.create(obj as object, flagMsg, ssfi, true).to.have.property( + 'length' + ); + itemsCount = (obj as Array<unknown>).length; } this.assert( @@ -2318,11 +2859,11 @@ Assertion.addChainableMethod('lengthOf', assertLength, assertLengthChain); * @namespace BDD * @public */ -function assertMatch(re, msg) { +function assertMatch(this: Assertion<unknown>, re: RegExp, msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'); this.assert( - re.exec(obj), + re.exec(obj as string), 'expected #{this} to match ' + re, 'expected #{this} not to match ' + re ); @@ -2355,15 +2896,15 @@ Assertion.addMethod('matches', assertMatch); * @namespace BDD * @public */ -Assertion.addMethod('string', function (str, msg) { +Assertion.addMethod('string', function (str: string, msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), flagMsg = flag(this, 'message'), ssfi = flag(this, 'ssfi'); - new Assertion(obj, flagMsg, ssfi, true).is.a('string'); + Assertion.create(obj, flagMsg, ssfi, true).is.a('string'); this.assert( - ~obj.indexOf(str), + ~(obj as string).indexOf(str), 'expected #{this} to contain ' + _.inspect(str), 'expected #{this} to not contain ' + _.inspect(str) ); @@ -2472,7 +3013,10 @@ Assertion.addMethod('string', function (str, msg) { * @namespace BDD * @public */ -function assertKeys(keys) { +function assertKeys( + this: Assertion<unknown>, + keys: Array<PropertyKey> | Record<PropertyKey, unknown> +) { var obj = flag(this, 'object'), objType = _.type(obj), keysType = _.type(keys), @@ -2480,9 +3024,10 @@ function assertKeys(keys) { isDeep = flag(this, 'deep'), str, deepStr = '', - actual, + actual: PropertyKey[] = [], ok = true, - flagMsg = flag(this, 'message'); + flagMsg = flag(this, 'message'), + normalisedKeys: PropertyKey[] = []; flagMsg = flagMsg ? flagMsg + ': ' : ''; var mixedArgsMsg = @@ -2494,47 +3039,52 @@ function assertKeys(keys) { actual = []; // Map and Set '.keys' aren't supported in IE 11. Therefore, use .forEach. - obj.forEach(function (val, key) { - actual.push(key); + (obj as Set<unknown> | Map<unknown, unknown>).forEach(function (_val, key) { + (actual as Array<unknown>).push(key); }); if (keysType !== 'Array') { - keys = Array.prototype.slice.call(arguments); + normalisedKeys = Array.prototype.slice.call(arguments); + } else { + normalisedKeys = keys as PropertyKey[]; } } else { - actual = _.getOwnEnumerableProperties(obj); + actual = _.getOwnEnumerableProperties(obj as object); switch (keysType) { case 'Array': if (arguments.length > 1) { throw new AssertionError(mixedArgsMsg, undefined, ssfi); } + normalisedKeys = keys as PropertyKey[]; break; case 'Object': if (arguments.length > 1) { throw new AssertionError(mixedArgsMsg, undefined, ssfi); } - keys = Object.keys(keys); + normalisedKeys = Object.keys(keys); break; default: - keys = Array.prototype.slice.call(arguments); + normalisedKeys = Array.prototype.slice.call(arguments); } // Only stringify non-Symbols because Symbols would become "Symbol()" - keys = keys.map(function (val) { + normalisedKeys = normalisedKeys.map(function (val) { return typeof val === 'symbol' ? val : String(val); }); } - if (!keys.length) { + if (!normalisedKeys.length) { throw new AssertionError(flagMsg + 'keys required', undefined, ssfi); } - var len = keys.length, + var len = normalisedKeys.length, any = flag(this, 'any'), all = flag(this, 'all'), - expected = keys, - isEql = isDeep ? flag(this, 'eql') : (val1, val2) => val1 === val2; + expected = normalisedKeys, + isEql = isDeep + ? flag(this, 'eql') + : (val1: unknown, val2: unknown): boolean => val1 === val2; if (!any && !all) { all = true; @@ -2558,24 +3108,24 @@ function assertKeys(keys) { }); if (!flag(this, 'contains')) { - ok = ok && keys.length == actual.length; + ok = ok && normalisedKeys.length == actual.length; } } // Key string if (len > 1) { - keys = keys.map(function (key) { + normalisedKeys = normalisedKeys.map(function (key) { return _.inspect(key); }); - var last = keys.pop(); + var last = normalisedKeys.pop(); if (all) { - str = keys.join(', ') + ', and ' + last; + str = normalisedKeys.join(', ') + ', and ' + String(last); } if (any) { - str = keys.join(', ') + ', or ' + last; + str = normalisedKeys.join(', ') + ', or ' + String(last); } } else { - str = _.inspect(keys[0]); + str = _.inspect(normalisedKeys[0]); } // Form @@ -2757,23 +3307,45 @@ Assertion.addMethod('key', assertKeys); * @namespace BDD * @public */ -function assertThrows(errorLike, errMsgMatcher, msg) { +function assertThrows( + this: Assertion<unknown>, + errMsgMatcher: string | RegExp +): void; +function assertThrows( + this: Assertion<unknown>, + errorLike: Error | Constructor<Error>, + errMsgMatcher: string | RegExp, + msg?: string +): void; +function assertThrows( + this: Assertion<unknown>, + errorLikeOrMatcher: Error | Constructor<Error> | string | RegExp, + errMsgMatcher?: string | RegExp, + msg?: string +): void { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), ssfi = flag(this, 'ssfi'), flagMsg = flag(this, 'message'), negate = flag(this, 'negate') || false; - new Assertion(obj, flagMsg, ssfi, true).is.a('function'); + Assertion.create(obj, flagMsg, ssfi, true).is.a('function'); + + let errorLike: Error | Constructor<Error> | null; - if (_.isRegExp(errorLike) || typeof errorLike === 'string') { - errMsgMatcher = errorLike; + if ( + _.isRegExp(errorLikeOrMatcher) || + typeof errorLikeOrMatcher === 'string' + ) { + errMsgMatcher = errorLikeOrMatcher; errorLike = null; + } else { + errorLike = errorLikeOrMatcher; } let caughtErr; let errorWasThrown = false; try { - obj(); + (obj as () => void)(); } catch (err) { errorWasThrown = true; caughtErr = err; @@ -2810,7 +3382,7 @@ function assertThrows(errorLike, errMsgMatcher, msg) { (typeof caughtErr === 'object' || typeof caughtErr === 'function') ) { try { - actual = _.checkError.getConstructorName(caughtErr); + actual = _.checkError.getConstructorName(caughtErr as never); } catch (_err) { // somehow wasn't a constructor, maybe we got a function thrown // or similar @@ -2830,7 +3402,7 @@ function assertThrows(errorLike, errMsgMatcher, msg) { // We should compare instances only if `errorLike` is an instance of `Error` if (errorLike instanceof Error) { var isCompatibleInstance = _.checkError.compatibleInstance( - caughtErr, + caughtErr as Error, errorLike ); @@ -2853,7 +3425,7 @@ function assertThrows(errorLike, errMsgMatcher, msg) { } var isCompatibleConstructor = _.checkError.compatibleConstructor( - caughtErr, + caughtErr as Error, errorLike ); if (isCompatibleConstructor === negate) { @@ -2870,7 +3442,7 @@ function assertThrows(errorLike, errMsgMatcher, msg) { : errorLike && _.checkError.getConstructorName(errorLike), caughtErr instanceof Error ? caughtErr.toString() - : caughtErr && _.checkError.getConstructorName(caughtErr) + : caughtErr && _.checkError.getConstructorName(caughtErr as Error) ); } } @@ -2884,7 +3456,7 @@ function assertThrows(errorLike, errMsgMatcher, msg) { } var isCompatibleMessage = _.checkError.compatibleMessage( - caughtErr, + caughtErr as Error, errMsgMatcher ); if (isCompatibleMessage === negate) { @@ -2898,7 +3470,7 @@ function assertThrows(errorLike, errMsgMatcher, msg) { ' #{exp} but got #{act}', 'expected #{this} to throw error not ' + placeholder + ' #{exp}', errMsgMatcher, - _.checkError.getMessage(caughtErr) + _.checkError.getMessage(caughtErr as Error) ); } } @@ -2916,7 +3488,7 @@ function assertThrows(errorLike, errMsgMatcher, msg) { : errorLike && _.checkError.getConstructorName(errorLike), caughtErr instanceof Error ? caughtErr.toString() - : caughtErr && _.checkError.getConstructorName(caughtErr) + : caughtErr && _.checkError.getConstructorName(caughtErr as Error) ); } @@ -2991,14 +3563,18 @@ Assertion.addMethod('Throw', assertThrows); * @namespace BDD * @public */ -function respondTo(method, msg) { +function respondTo( + this: Assertion<unknown>, + method: PropertyKey, + msg?: string +) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), itself = flag(this, 'itself'), context = 'function' === typeof obj && !itself ? obj.prototype[method] - : obj[method]; + : (obj as Record<PropertyKey, unknown>)[method]; this.assert( 'function' === typeof context, @@ -3070,7 +3646,7 @@ Assertion.addProperty('itself', function () { * @namespace BDD * @public */ -function satisfy(matcher, msg) { +function satisfy(this: Assertion<unknown>, matcher: Function, msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'); var result = matcher(obj); @@ -3123,9 +3699,14 @@ Assertion.addMethod('satisfies', satisfy); * @namespace BDD * @public */ -function closeTo(expected, delta, msg) { +function closeTo( + this: Assertion<unknown>, + expected: number, + delta: number, + msg?: string +) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'), + var obj = flag(this, 'object') as number, flagMsg = flag(this, 'message'), ssfi = flag(this, 'ssfi'); @@ -3147,7 +3728,7 @@ function closeTo(expected, delta, msg) { ); new Assertion(expected, flagMsg, ssfi, true).is.numeric; - const abs = (x) => (x < 0n ? -x : x); + const abs = (x: number): number => (x < 0n ? -x : x); this.assert( abs(obj - expected) <= delta, @@ -3160,38 +3741,45 @@ Assertion.addMethod('closeTo', closeTo); Assertion.addMethod('approximately', closeTo); /** - * @param {unknown} _subset - * @param {unknown} _superset + * @param {unknown} subset + * @param {unknown} superset * @param {unknown} cmp * @param {unknown} contains * @param {unknown} ordered * @returns {boolean} */ -function isSubsetOf(_subset, _superset, cmp, contains, ordered) { - let superset = Array.from(_superset); - let subset = Array.from(_subset); - if (!contains) { - if (subset.length !== superset.length) return false; - superset = superset.slice(); +function isSubsetOf( + subset: Iterable<unknown>, + superset: Iterable<unknown>, + cmp: ((a: unknown, b: unknown) => boolean) | undefined, + contains: boolean, + ordered: boolean +) { + const subsetArr = [...subset]; + const supersetArr = [...superset]; + // Note: Duplicates are ignored if testing for inclusion instead of sameness. + if (!contains && subsetArr.length !== supersetArr.length) { + return false; } - return subset.every(function (elem, idx) { - if (ordered) return cmp ? cmp(elem, superset[idx]) : elem === superset[idx]; + return subsetArr.every(function (elem, idx) { + if (ordered) + return cmp ? cmp(elem, supersetArr[idx]) : elem === supersetArr[idx]; if (!cmp) { - var matchIdx = superset.indexOf(elem); + var matchIdx = supersetArr.indexOf(elem); if (matchIdx === -1) return false; - // Remove match from superset so not counted twice if duplicate in subset. - if (!contains) superset.splice(matchIdx, 1); + // Remove match from supersetArr so not counted twice if duplicate in subsetArr. + if (!contains) supersetArr.splice(matchIdx, 1); return true; } - return superset.some(function (elem2, matchIdx) { + return supersetArr.some(function (elem2, matchIdx) { if (!cmp(elem, elem2)) return false; - // Remove match from superset so not counted twice if duplicate in subset. - if (!contains) superset.splice(matchIdx, 1); + // Remove match from supersetArr so not counted twice if duplicate in subsetArr. + if (!contains) supersetArr.splice(matchIdx, 1); return true; }); }); @@ -3265,7 +3853,7 @@ function isSubsetOf(_subset, _superset, cmp, contains, ordered) { * @namespace BDD * @public */ -Assertion.addMethod('members', function (subset, msg) { +Assertion.addMethod('members', function (subset: unknown[], msg?: string) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'), flagMsg = flag(this, 'message'), @@ -3274,8 +3862,8 @@ Assertion.addMethod('members', function (subset, msg) { new Assertion(obj, flagMsg, ssfi, true).to.be.iterable; new Assertion(subset, flagMsg, ssfi, true).to.be.iterable; - var contains = flag(this, 'contains'); - var ordered = flag(this, 'ordered'); + var contains = flag(this, 'contains') === true; + var ordered = flag(this, 'ordered') === true; var subject, failMsg, failNegateMsg; @@ -3293,7 +3881,7 @@ Assertion.addMethod('members', function (subset, msg) { var cmp = flag(this, 'deep') ? flag(this, 'eql') : undefined; this.assert( - isSubsetOf(subset, obj, cmp, contains, ordered), + isSubsetOf(subset, obj as Iterable<unknown>, cmp, contains, ordered), failMsg, failNegateMsg, subset, @@ -3323,17 +3911,22 @@ Assertion.addMethod('members', function (subset, msg) { * @namespace BDD * @public */ -Assertion.addProperty('iterable', function (msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); +Assertion.addProperty( + 'iterable', + function (this: Assertion<unknown>, msg?: string) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); - this.assert( - obj != undefined && obj[Symbol.iterator], - 'expected #{this} to be an iterable', - 'expected #{this} to not be an iterable', - obj - ); -}); + this.assert( + obj !== null && + obj !== undefined && + (obj as Record<PropertyKey, unknown>)[Symbol.iterator], + 'expected #{this} to be an iterable', + 'expected #{this} to not be an iterable', + obj + ); + } +); /** * ### .oneOf(list[, msg]) @@ -3372,7 +3965,7 @@ Assertion.addProperty('iterable', function (msg) { * @namespace BDD * @public */ -function oneOf(list, msg) { +function oneOf(this: Assertion<unknown>, list: unknown[], msg?: string) { if (msg) flag(this, 'message', msg); var expected = flag(this, 'object'), flagMsg = flag(this, 'message'), @@ -3380,12 +3973,12 @@ function oneOf(list, msg) { contains = flag(this, 'contains'), isDeep = flag(this, 'deep'), eql = flag(this, 'eql'); - new Assertion(list, flagMsg, ssfi, true).to.be.an('array'); + Assertion.create(list, flagMsg, ssfi, true).to.be.an('array'); if (contains) { this.assert( list.some(function (possibility) { - return expected.indexOf(possibility) > -1; + return (expected as Array<unknown>).indexOf(possibility) > -1; }), 'expected #{this} to contain one of #{exp}', 'expected #{this} to not contain one of #{exp}', @@ -3511,26 +4104,42 @@ Assertion.addMethod('oneOf', oneOf); * @namespace BDD * @public */ -function assertChanges(subject, prop, msg) { +function assertChanges( + this: Assertion<unknown>, + subject: object, + prop: PropertyKey, + msg?: string +): void; +function assertChanges(this: Assertion<unknown>, subject: Function): void; +function assertChanges( + this: Assertion<unknown>, + subject: Function | object, + prop?: PropertyKey, + msg?: string +) { if (msg) flag(this, 'message', msg); - var fn = flag(this, 'object'), + var fn = flag(this, 'object') as () => void, flagMsg = flag(this, 'message'), ssfi = flag(this, 'ssfi'); - new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + Assertion.create(fn, flagMsg, ssfi, true).is.a('function'); var initial; if (!prop) { - new Assertion(subject, flagMsg, ssfi, true).is.a('function'); - initial = subject(); + Assertion.create(subject, flagMsg, ssfi, true).is.a('function'); + initial = (subject as () => void)(); } else { - new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); - initial = subject[prop]; + Assertion.create(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = (subject as Record<PropertyKey, unknown>)[prop]; } fn(); - var final = prop === undefined || prop === null ? subject() : subject[prop]; - var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + var final = + prop === undefined || prop === null + ? (subject as () => void)() + : (subject as Record<PropertyKey, unknown>)[prop]; + var msgObj = + prop === undefined || prop === null ? initial : '.' + String(prop); // This gets flagged because of the .by(delta) assertion flag(this, 'deltaMsgObj', msgObj); @@ -3627,29 +4236,45 @@ Assertion.addMethod('changes', assertChanges); * @namespace BDD * @public */ -function assertIncreases(subject, prop, msg) { +function assertIncreases(this: Assertion<unknown>, subject: () => void): void; +function assertIncreases( + this: Assertion<unknown>, + subject: object, + prop: PropertyKey, + msg?: string +): void; +function assertIncreases( + this: Assertion<unknown>, + subject: object | (() => number), + prop?: PropertyKey, + msg?: string +): void { if (msg) flag(this, 'message', msg); - var fn = flag(this, 'object'), + var fn = flag(this, 'object') as () => void, flagMsg = flag(this, 'message'), ssfi = flag(this, 'ssfi'); - new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + Assertion.create(fn, flagMsg, ssfi, true).is.a('function'); var initial; if (!prop) { - new Assertion(subject, flagMsg, ssfi, true).is.a('function'); - initial = subject(); + Assertion.create(subject, flagMsg, ssfi, true).is.a('function'); + initial = (subject as () => number)(); } else { - new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); - initial = subject[prop]; + Assertion.create(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = (subject as Record<PropertyKey, number>)[prop]; } // Make sure that the target is a number - new Assertion(initial, flagMsg, ssfi, true).is.a('number'); + Assertion.create(initial, flagMsg, ssfi, true).is.a('number'); fn(); - var final = prop === undefined || prop === null ? subject() : subject[prop]; - var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + var final = + prop === undefined || prop === null + ? (subject as () => number)() + : (subject as Record<PropertyKey, number>)[prop]; + var msgObj = + prop === undefined || prop === null ? initial : '.' + String(prop); flag(this, 'deltaMsgObj', msgObj); flag(this, 'initialDeltaValue', initial); @@ -3745,29 +4370,45 @@ Assertion.addMethod('increases', assertIncreases); * @namespace BDD * @public */ -function assertDecreases(subject, prop, msg) { +function assertDecreases(this: Assertion<unknown>, subject: () => number): void; +function assertDecreases( + this: Assertion<unknown>, + subject: object, + prop: PropertyKey, + msg?: string +): void; +function assertDecreases( + this: Assertion<unknown>, + subject: object | (() => number), + prop?: PropertyKey, + msg?: string +): void { if (msg) flag(this, 'message', msg); - var fn = flag(this, 'object'), + var fn = flag(this, 'object') as () => void, flagMsg = flag(this, 'message'), ssfi = flag(this, 'ssfi'); - new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + Assertion.create(fn, flagMsg, ssfi, true).is.a('function'); var initial; if (!prop) { - new Assertion(subject, flagMsg, ssfi, true).is.a('function'); - initial = subject(); + Assertion.create(subject, flagMsg, ssfi, true).is.a('function'); + initial = (subject as () => number)(); } else { - new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); - initial = subject[prop]; + Assertion.create(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = (subject as Record<PropertyKey, number>)[prop]; } // Make sure that the target is a number - new Assertion(initial, flagMsg, ssfi, true).is.a('number'); + Assertion.create(initial, flagMsg, ssfi, true).is.a('number'); fn(); - var final = prop === undefined || prop === null ? subject() : subject[prop]; - var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + var final = + prop === undefined || prop === null + ? (subject as () => number)() + : (subject as Record<PropertyKey, number>)[prop]; + var msgObj = + prop === undefined || prop === null ? initial : '.' + String(prop); flag(this, 'deltaMsgObj', msgObj); flag(this, 'initialDeltaValue', initial); @@ -3850,12 +4491,12 @@ Assertion.addMethod('decreases', assertDecreases); * @namespace BDD * @public */ -function assertDelta(delta, msg) { +function assertDelta(this: Assertion<unknown>, delta: number, msg?: string) { if (msg) flag(this, 'message', msg); var msgObj = flag(this, 'deltaMsgObj'); - var initial = flag(this, 'initialDeltaValue'); - var final = flag(this, 'finalDeltaValue'); + var initial = flag(this, 'initialDeltaValue') as number; + var final = flag(this, 'finalDeltaValue') as number; var behavior = flag(this, 'deltaBehavior'); var realDelta = flag(this, 'realDelta'); @@ -4052,7 +4693,7 @@ Assertion.addProperty('frozen', function () { * @namespace BDD * @public */ -Assertion.addProperty('finite', function (_msg) { +Assertion.addProperty('finite', function () { var obj = flag(this, 'object'); this.assert( diff --git a/lib/chai/interface/assert.js b/src/chai/interface/assert.ts similarity index 60% rename from lib/chai/interface/assert.js rename to src/chai/interface/assert.ts index 07a6767f..24c2ee8f 100644 --- a/lib/chai/interface/assert.js +++ b/src/chai/interface/assert.ts @@ -4,10 +4,576 @@ * MIT Licensed */ -import * as chai from '../../../index.js'; import {Assertion} from '../assertion.js'; import {flag, inspect} from '../utils/index.js'; import {AssertionError} from 'assertion-error'; +import { + Constructor, + LengthLike, + CollectionLike, + KeyedObject +} from '../utils/types.js'; + +export interface AssertInterface { + (expr: unknown, msg?: string): asserts expr; + fail(msg?: string): never; + fail<T>(actual: T, expected: T, message: string, operator: string): never; + isOk(val: unknown, msg?: string): void; + ok(val: unknown, msg?: string): void; + isNotOk(val: unknown, msg?: string): void; + notOk(val: unknown, msg?: string): void; + equal<TActual, TExpected extends TActual>( + actual: TActual, + expected: TExpected, + msg?: string + ): asserts actual is TExpected; + notEqual<T>(actual: T, expected: T, msg?: string): void; + strictEqual<TActual, TExpected extends TActual>( + actual: TActual, + expected: TExpected, + msg?: string + ): asserts actual is TExpected; + notStrictEqual<T>(actual: T, expected: T, msg?: string): void; + deepEqual<TActual, TExpected extends TActual>( + actual: TActual, + expected: TExpected, + msg?: string + ): asserts actual is TExpected; + deepStrictEqual<TActual, TExpected extends TActual>( + actual: TActual, + expected: TExpected, + msg?: string + ): asserts actual is TExpected; + notDeepEqual<T>(actual: T, expected: T, msg?: string): void; + isAbove<T extends Date | number>(val: T, abv: T, msg?: string): void; + isAtLeast<T extends Date | number>(val: T, atlst: T, msg?: string): void; + isBelow<T extends Date | number>(val: T, blw: T, msg?: string): void; + isAtMost<T extends Date | number>(val: T, atmst: T, msg?: string): void; + isTrue(val: unknown, msg?: string): asserts val is true; + isNotTrue<T>(val: T, msg?: string): asserts val is Exclude<T, true>; + isFalse(val: unknown, msg?: string): asserts val is false; + isNotFalse<T>(val: T, msg?: string): asserts val is Exclude<T, false>; + isNull(val: unknown, msg?: string): asserts val is null; + isNotNull<T>(val: T, msg?: string): asserts val is Exclude<T, null>; + isNaN(val: unknown, msg?: string): asserts val is number; + isNotNaN(val: unknown, msg?: string): void; + exists<T>(val: T, msg?: string): asserts val is NonNullable<T>; + notExists(val: unknown, msg?: string): void; + isUndefined(val: unknown, msg?: string): asserts val is undefined; + isDefined(val: unknown, msg?: string): void; + isFunction(val: unknown, msg?: string): asserts val is Function; + isNotFunction(val: unknown, msg?: string): void; + isObject(val: unknown, msg?: string): asserts val is object; + isNotObject(val: unknown, msg?: string): void; + isArray(val: unknown, msg?: string): asserts val is Array<unknown>; + isNotArray(val: unknown, msg?: string): void; + isString(val: unknown, msg?: string): asserts val is string; + isNotString<T>(val: T, msg?: string): asserts val is Exclude<T, string>; + isNumber(val: unknown, msg?: string): asserts val is number; + isNotNumber<T>(val: T, msg?: string): asserts val is Exclude<T, number>; + isNumeric(val: unknown, msg?: string): asserts val is number | bigint; + isNotNumeric<T>( + val: T, + msg?: string + ): asserts val is Exclude<T, number | bigint>; + isFinite(val: number, msg?: string): void; + isBoolean(val: unknown, msg?: string): asserts val is boolean; + isNotBoolean<T>(val: T, msg?: string): asserts val is Exclude<T, boolean>; + + // typeof + typeOf(val: unknown, type: 'undefined'): asserts val is undefined; + typeOf(val: unknown, type: 'null'): asserts val is null; + typeOf(val: unknown, type: 'map'): asserts val is Map<unknown, unknown>; + typeOf(val: unknown, type: 'set'): asserts val is Set<unknown>; + typeOf(val: unknown, type: 'promise'): asserts val is Promise<unknown>; + typeOf(val: unknown, type: 'string'): asserts val is string; + typeOf(val: unknown, type: 'number'): asserts val is number; + typeOf(val: unknown, type: 'array'): asserts val is Array<unknown>; + typeOf(val: unknown, type: 'boolean'): asserts val is boolean; + typeOf(val: unknown, type: string, msg?: string): void; + notTypeOf(val: unknown, type: string, msg?: string): void; + + // instanceof + instanceOf<T extends Constructor<unknown>>( + val: unknown, + type: T, + msg?: string + ): asserts val is InstanceType<T>; + notInstanceOf(val: object, type: Constructor<unknown>, msg?: string): void; + + // includes + include( + expr: CollectionLike<unknown> | string | object, + inc: unknown, + msg?: string + ): void; + notInclude( + expr: CollectionLike<unknown> | string | object, + inc: unknown, + msg?: string + ): void; + deepInclude( + expr: CollectionLike<unknown> | string | object, + inc: unknown, + msg?: string + ): void; + notDeepInclude( + expr: CollectionLike<unknown> | string | object, + inc: unknown, + msg?: string + ): void; + nestedInclude( + expr: CollectionLike<unknown> | object, + inc: unknown, + msg?: string + ): void; + notNestedInclude( + expr: CollectionLike<unknown> | object, + inc: unknown, + msg?: string + ): void; + deepNestedInclude( + expr: CollectionLike<unknown> | object, + inc: unknown, + msg?: string + ): void; + notDeepNestedInclude( + expr: CollectionLike<unknown> | object, + inc: unknown, + msg?: string + ): void; + ownInclude(expr: object, inc: unknown, msg?: string): void; + notOwnInclude(expr: object, inc: unknown, msg?: string): void; + deepOwnInclude(expr: object, inc: unknown, msg?: string): void; + notDeepOwnInclude(expr: object, inc: unknown, msg?: string): void; + + // match + match(expr: string, re: RegExp, msg?: string): void; + notMatch(expr: string, re: RegExp, msg?: string): void; + + // properties + property<T extends object, TKey extends PropertyKey>( + obj: T, + prop: TKey, + msg?: string + ): asserts obj is TKey extends keyof T ? T : T & {[k in TKey]: unknown}; + notProperty(obj: object, prop: PropertyKey, msg?: string): void; + propertyVal<T extends object, TKey extends keyof T>( + obj: T, + prop: TKey, + val: T[TKey], + msg?: string + ): void; + notPropertyVal<T extends object, TKey extends keyof T>( + obj: T, + prop: TKey, + val: T[TKey], + msg?: string + ): void; + deepPropertyVal<T extends object, TKey extends keyof T>( + obj: T, + prop: TKey, + val: T[TKey], + msg?: string + ): void; + notDeepPropertyVal<T extends object, TKey extends keyof T>( + obj: T, + prop: TKey, + val: T[TKey], + msg?: string + ): void; + ownProperty<T extends object, TKey extends PropertyKey>( + obj: T, + prop: TKey, + msg?: string + ): asserts obj is TKey extends keyof T ? T : T & {[k in TKey]: unknown}; + notOwnProperty(obj: unknown, prop: PropertyKey, msg?: string): void; + ownPropertyVal<T extends object, TKey extends keyof T>( + obj: T, + prop: TKey, + val: T[TKey], + msg?: string + ): void; + notOwnPropertyVal<T extends object, TKey extends keyof T>( + obj: T, + prop: TKey, + val: T[TKey], + msg?: string + ): void; + deepOwnPropertyVal<T extends object, TKey extends keyof T>( + obj: T, + prop: TKey, + val: T[TKey], + msg?: string + ): void; + notDeepOwnPropertyVal<T extends object, TKey extends keyof T>( + obj: T, + prop: TKey, + val: T[TKey], + msg?: string + ): void; + nestedProperty(obj: unknown, prop: string, msg?: string): void; + notNestedProperty(obj: unknown, prop: string, msg?: string): void; + nestedPropertyVal( + obj: unknown, + prop: string, + val: unknown, + msg?: string + ): void; + notNestedPropertyVal( + obj: unknown, + prop: string, + val: unknown, + msg?: string + ): void; + deepNestedPropertyVal( + obj: unknown, + prop: string, + val: unknown, + msg?: string + ): void; + notDeepNestedPropertyVal( + obj: unknown, + prop: string, + val: unknown, + msg?: string + ): void; + lengthOf(expr: LengthLike, len: number, msg?: string): void; + + // keys + hasAnyKeys( + obj: Set<unknown> | Map<unknown, unknown>, + keys: unknown[], + msg?: string + ): void; + hasAnyKeys( + obj: object, + keys: PropertyKey[] | Record<PropertyKey, unknown>, + msg?: string + ): void; + + hasAllKeys( + obj: Set<unknown> | Map<unknown, unknown>, + keys: unknown[], + msg?: string + ): void; + hasAllKeys( + obj: object, + keys: PropertyKey[] | Record<PropertyKey, unknown>, + msg?: string + ): void; + + containsAllKeys( + obj: Set<unknown> | Map<unknown, unknown>, + keys: unknown[], + msg?: string + ): void; + containsAllKeys( + obj: object, + keys: PropertyKey[] | Record<PropertyKey, unknown>, + msg?: string + ): void; + + doesNotHaveAnyKeys( + obj: Set<unknown> | Map<unknown, unknown>, + keys: unknown[], + msg?: string + ): void; + doesNotHaveAnyKeys( + obj: object, + keys: PropertyKey[] | Record<PropertyKey, unknown>, + msg?: string + ): void; + + doesNotHaveAllKeys( + obj: Set<unknown> | Map<unknown, unknown>, + keys: unknown[], + msg?: string + ): void; + doesNotHaveAllKeys( + obj: object, + keys: PropertyKey[] | Record<PropertyKey, unknown>, + msg?: string + ): void; + + hasAnyDeepKeys( + obj: Set<unknown> | Map<unknown, unknown>, + keys: unknown[], + msg?: string + ): void; + hasAnyDeepKeys( + obj: object, + keys: PropertyKey[] | Record<PropertyKey, unknown>, + msg?: string + ): void; + + hasAllDeepKeys( + obj: Set<unknown> | Map<unknown, unknown>, + keys: unknown[], + msg?: string + ): void; + hasAllDeepKeys( + obj: object, + keys: PropertyKey[] | Record<PropertyKey, unknown>, + msg?: string + ): void; + + containsAllDeepKeys( + obj: Set<unknown> | Map<unknown, unknown>, + keys: unknown[], + msg?: string + ): void; + containsAllDeepKeys( + obj: object, + keys: PropertyKey[] | Record<PropertyKey, unknown>, + msg?: string + ): void; + + doesNotHaveAnyDeepKeys( + obj: Set<unknown> | Map<unknown, unknown>, + keys: unknown[], + msg?: string + ): void; + doesNotHaveAnyDeepKeys( + obj: object, + keys: PropertyKey[] | Record<PropertyKey, unknown>, + msg?: string + ): void; + + doesNotHaveAllDeepKeys( + obj: Set<unknown> | Map<unknown, unknown>, + keys: unknown[], + msg?: string + ): void; + doesNotHaveAllDeepKeys( + obj: object, + keys: PropertyKey[] | Record<PropertyKey, unknown>, + msg?: string + ): void; + + Throw( + fn: Function, + errorLike: Error | Constructor<Error>, + errMsgMatcher: RegExp | string, + msg?: string + ): void; + Throw(fn: Function, errMsgMatcher: RegExp | string): void; + throw( + fn: Function, + errorLike: Error | Constructor<Error>, + errMsgMatcher: RegExp | string, + msg?: string + ): void; + throw(fn: Function, errMsgMatcher: RegExp | string): void; + throws( + fn: Function, + errorLike: Error | Constructor<Error>, + errMsgMatcher: RegExp | string, + msg?: string + ): void; + throws(fn: Function, errMsgMatcher: RegExp | string): void; + + doesNotThrow(fn: Function, errMsgMatcher: RegExp | string): void; + doesNotThrow( + fn: Function, + errorLike: Error | Constructor<Error>, + errMsgMatcher: RegExp | string, + msg?: string + ): void; + + operator<T>(val: T, operator: string, val2: T, msg?: string): void; + closeTo(actual: number, expected: number, delta: number, msg?: string): void; + approximately( + actual: number, + expected: number, + delta: number, + msg?: string + ): void; + + // members + sameMembers<T>(set1: T[], set2: T[], msg?: string): void; + notSameMembers<T>(set1: T[], set2: T[], msg?: string): void; + sameDeepMembers<T>(set1: T[], set2: T[], msg?: string): void; + notSameDeepMembers<T>(set1: T[], set2: T[], msg?: string): void; + sameOrderedMembers<T>(set1: T[], set2: T[], msg?: string): void; + notSameOrderedMembers<T>(set1: T[], set2: T[], msg?: string): void; + sameDeepOrderedMembers<T>(set1: T[], set2: T[], msg?: string): void; + notSameDeepOrderedMembers<T>(set1: T[], set2: T[], msg?: string): void; + includeMembers<T>(superset: T[], subset: T[], msg?: string): void; + notIncludeMembers<T>(superset: T[], subset: T[], msg?: string): void; + includeDeepMembers<T>(superset: T[], subset: T[], msg?: string): void; + notIncludeDeepMembers<T>(superset: T[], subset: T[], msg?: string): void; + includeOrderedMembers<T>(superset: T[], subset: T[], msg?: string): void; + notIncludeOrderedMembers<T>(superset: T[], subset: T[], msg?: string): void; + includeDeepOrderedMembers<T>(superset: T[], subset: T[], msg?: string): void; + notIncludeDeepOrderedMembers<T>( + superset: T[], + subset: T[], + msg?: string + ): void; + + oneOf<T extends string | unknown[]>(inList: T, list: T[], msg?: string): void; + + changes<T>(fn: Function, obj: T, prop: keyof T, msg?: string): void; + changes(fn: Function, obj: () => void, msg?: string): void; + + changesBy<T>( + fn: Function, + obj: T, + prop: keyof T, + delta: number, + msg?: string + ): void; + changesBy(fn: Function, obj: () => void, delta: number, msg?: string): void; + + doesNotChange<T>( + fn: Function, + obj: T, + prop: keyof T, + msg?: string + ): Assertion<Function>; + doesNotChange( + fn: Function, + obj: () => void, + msg?: string + ): Assertion<Function>; + + changesButNotBy<T>( + fn: Function, + obj: T, + prop: keyof T, + delta: number, + msg?: string + ): void; + changesButNotBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string + ): void; + + increases<T>( + fn: Function, + obj: T, + prop: keyof T, + msg?: string + ): Assertion<Function>; + increases(fn: Function, obj: () => void, msg?: string): Assertion<Function>; + + increasesBy<T>( + fn: Function, + obj: T, + prop: keyof T, + delta: number, + msg?: string + ): void; + increasesBy(fn: Function, obj: () => void, delta: number, msg?: string): void; + + doesNotIncrease<T>( + fn: Function, + obj: T, + prop: keyof T, + msg?: string + ): Assertion<Function>; + doesNotIncrease( + fn: Function, + obj: () => void, + msg?: string + ): Assertion<Function>; + + increasesButNotBy<T>( + fn: Function, + obj: T, + prop: keyof T, + delta: number, + msg?: string + ): void; + increasesButNotBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string + ): void; + + decreases<T>( + fn: Function, + obj: T, + prop: keyof T, + msg?: string + ): Assertion<Function>; + decreases(fn: Function, obj: () => void, msg?: string): Assertion<Function>; + + decreasesBy<T>( + fn: Function, + obj: T, + prop: keyof T, + delta: number, + msg?: string + ): void; + decreasesBy(fn: Function, obj: () => void, delta: number, msg?: string): void; + + doesNotDecrease<T>( + fn: Function, + obj: T, + prop: keyof T, + msg?: string + ): Assertion<Function>; + doesNotDecrease( + fn: Function, + obj: () => void, + msg?: string + ): Assertion<Function>; + + doesNotDecreaseBy<T>( + fn: Function, + obj: T, + prop: keyof T, + delta: number, + msg?: string + ): Assertion<Function>; + doesNotDecreaseBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string + ): Assertion<Function>; + + decreasesButNotBy<T>( + fn: Function, + obj: T, + prop: keyof T, + delta: number, + msg?: string + ): void; + decreasesButNotBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string + ): void; + + ifError(val: unknown): void; + isExtensible(obj: object, msg?: string): void; + extensible(obj: object, msg?: string): void; + isNotExtensible(obj: object, msg?: string): void; + notExtensible(obj: object, msg?: string): void; + isSealed(obj: object, msg?: string): void; + sealed(obj: object, msg?: string): void; + isNotSealed(obj: object, msg?: string): void; + notSealed(obj: object, msg?: string): void; + isFrozen(obj: object, msg?: string): void; + frozen(obj: object, msg?: string): void; + isNotFrozen(obj: object, msg?: string): void; + notFrozen(obj: object, msg?: string): void; + isEmpty(val: unknown, msg?: string): void; + empty(val: unknown, msg?: string): void; + isNotEmpty(val: unknown, msg?: string): void; + notEmpty(val: unknown, msg?: string): void; + + isCallable(val: unknown, msg?: string): void; + isNotCallable(val: unknown, msg?: string): void; + isIterable(val: unknown, msg?: string): asserts val is Iterable<unknown>; +} /** * ### assert(expression, message) @@ -23,10 +589,15 @@ import {AssertionError} from 'assertion-error'; * @namespace Assert * @public */ -export function assert(express, errmsg) { - var test = new Assertion(null, null, chai.assert, true); +const assert: AssertInterface = function assert( + express: unknown, + errmsg?: string +) { + var test = Assertion.create(null, null, assert, true); test.assert(express, errmsg, '[ negation message unavailable ]'); -} +} as AssertInterface; + +export {assert}; /** * ### .fail([message]) @@ -49,17 +620,25 @@ export function assert(express, errmsg) { * @namespace Assert * @public */ -assert.fail = function (actual, expected, message, operator) { +assert.fail = function fail( + actualOrMsg?: unknown, + expected?: unknown, + message?: string, + operator?: string +) { + let msg = message; + let actual = actualOrMsg; + if (arguments.length < 2) { // Comply with Node's fail([message]) interface - message = actual; + msg = actualOrMsg as string; actual = undefined; } - message = message || 'assert.fail()'; + msg = msg || 'assert.fail()'; throw new AssertionError( - message, + msg, { actual: actual, expected: expected, @@ -84,8 +663,8 @@ assert.fail = function (actual, expected, message, operator) { * @namespace Assert * @public */ -assert.isOk = function (val, msg) { - new Assertion(val, msg, assert.isOk, true).is.ok; +assert.isOk = assert.ok = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isOk, true).is.ok; }; /** @@ -103,8 +682,8 @@ assert.isOk = function (val, msg) { * @namespace Assert * @public */ -assert.isNotOk = function (val, msg) { - new Assertion(val, msg, assert.isNotOk, true).is.not.ok; +assert.isNotOk = assert.notOk = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNotOk, true).is.not.ok; }; /** @@ -121,8 +700,8 @@ assert.isNotOk = function (val, msg) { * @namespace Assert * @public */ -assert.equal = function (act, exp, msg) { - var test = new Assertion(act, msg, assert.equal, true); +assert.equal = function (act: unknown, exp: unknown, msg?: string) { + var test = Assertion.create(act, msg, assert.equal, true); test.assert( exp == flag(test, 'object'), @@ -148,8 +727,8 @@ assert.equal = function (act, exp, msg) { * @namespace Assert * @public */ -assert.notEqual = function (act, exp, msg) { - var test = new Assertion(act, msg, assert.notEqual, true); +assert.notEqual = function (act: unknown, exp: unknown, msg?: string) { + var test = Assertion.create(act, msg, assert.notEqual, true); test.assert( exp != flag(test, 'object'), @@ -175,8 +754,8 @@ assert.notEqual = function (act, exp, msg) { * @namespace Assert * @public */ -assert.strictEqual = function (act, exp, msg) { - new Assertion(act, msg, assert.strictEqual, true).to.equal(exp); +assert.strictEqual = function (act: unknown, exp: unknown, msg?: string) { + Assertion.create(act, msg, assert.strictEqual, true).to.equal(exp); }; /** @@ -193,8 +772,8 @@ assert.strictEqual = function (act, exp, msg) { * @namespace Assert * @public */ -assert.notStrictEqual = function (act, exp, msg) { - new Assertion(act, msg, assert.notStrictEqual, true).to.not.equal(exp); +assert.notStrictEqual = function (act: unknown, exp: unknown, msg?: string) { + Assertion.create(act, msg, assert.notStrictEqual, true).to.not.equal(exp); }; /** @@ -212,8 +791,12 @@ assert.notStrictEqual = function (act, exp, msg) { * @namespace Assert * @public */ -assert.deepEqual = assert.deepStrictEqual = function (act, exp, msg) { - new Assertion(act, msg, assert.deepEqual, true).to.eql(exp); +assert.deepEqual = assert.deepStrictEqual = function ( + act: unknown, + exp: unknown, + msg?: string +) { + Assertion.create(act, msg, assert.deepEqual, true).to.eql(exp); }; /** @@ -230,8 +813,8 @@ assert.deepEqual = assert.deepStrictEqual = function (act, exp, msg) { * @namespace Assert * @public */ -assert.notDeepEqual = function (act, exp, msg) { - new Assertion(act, msg, assert.notDeepEqual, true).to.not.eql(exp); +assert.notDeepEqual = function (act: unknown, exp: unknown, msg?: string) { + Assertion.create(act, msg, assert.notDeepEqual, true).to.not.eql(exp); }; /** @@ -248,8 +831,12 @@ assert.notDeepEqual = function (act, exp, msg) { * @namespace Assert * @public */ -assert.isAbove = function (val, abv, msg) { - new Assertion(val, msg, assert.isAbove, true).to.be.above(abv); +assert.isAbove = function isAbove<T extends Date | number>( + val: T, + abv: T, + msg?: string +): void { + Assertion.create(val, msg, assert.isAbove, true).to.be.above(abv); }; /** @@ -267,8 +854,12 @@ assert.isAbove = function (val, abv, msg) { * @namespace Assert * @public */ -assert.isAtLeast = function (val, atlst, msg) { - new Assertion(val, msg, assert.isAtLeast, true).to.be.least(atlst); +assert.isAtLeast = function isAtLeast<T extends Date | number>( + val: T, + atlst: T, + msg?: string +) { + Assertion.create(val, msg, assert.isAtLeast, true).to.be.least(atlst); }; /** @@ -285,8 +876,12 @@ assert.isAtLeast = function (val, atlst, msg) { * @namespace Assert * @public */ -assert.isBelow = function (val, blw, msg) { - new Assertion(val, msg, assert.isBelow, true).to.be.below(blw); +assert.isBelow = function isBelow<T extends Date | number>( + val: T, + blw: T, + msg?: string +) { + Assertion.create(val, msg, assert.isBelow, true).to.be.below(blw); }; /** @@ -304,8 +899,12 @@ assert.isBelow = function (val, blw, msg) { * @namespace Assert * @public */ -assert.isAtMost = function (val, atmst, msg) { - new Assertion(val, msg, assert.isAtMost, true).to.be.most(atmst); +assert.isAtMost = function isAtMost<T extends Date | number>( + val: T, + atmst: T, + msg?: string +) { + Assertion.create(val, msg, assert.isAtMost, true).to.be.most(atmst); }; /** @@ -322,8 +921,8 @@ assert.isAtMost = function (val, atmst, msg) { * @namespace Assert * @public */ -assert.isTrue = function (val, msg) { - new Assertion(val, msg, assert.isTrue, true).is['true']; +assert.isTrue = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isTrue, true).is['true']; }; /** @@ -340,8 +939,8 @@ assert.isTrue = function (val, msg) { * @namespace Assert * @public */ -assert.isNotTrue = function (val, msg) { - new Assertion(val, msg, assert.isNotTrue, true).to.not.equal(true); +assert.isNotTrue = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNotTrue, true).to.not.equal(true); }; /** @@ -358,8 +957,8 @@ assert.isNotTrue = function (val, msg) { * @namespace Assert * @public */ -assert.isFalse = function (val, msg) { - new Assertion(val, msg, assert.isFalse, true).is['false']; +assert.isFalse = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isFalse, true).is['false']; }; /** @@ -376,8 +975,8 @@ assert.isFalse = function (val, msg) { * @namespace Assert * @public */ -assert.isNotFalse = function (val, msg) { - new Assertion(val, msg, assert.isNotFalse, true).to.not.equal(false); +assert.isNotFalse = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNotFalse, true).to.not.equal(false); }; /** @@ -393,8 +992,8 @@ assert.isNotFalse = function (val, msg) { * @namespace Assert * @public */ -assert.isNull = function (val, msg) { - new Assertion(val, msg, assert.isNull, true).to.equal(null); +assert.isNull = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNull, true).to.equal(null); }; /** @@ -411,8 +1010,8 @@ assert.isNull = function (val, msg) { * @namespace Assert * @public */ -assert.isNotNull = function (val, msg) { - new Assertion(val, msg, assert.isNotNull, true).to.not.equal(null); +assert.isNotNull = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNotNull, true).to.not.equal(null); }; /** @@ -428,8 +1027,8 @@ assert.isNotNull = function (val, msg) { * @namespace Assert * @public */ -assert.isNaN = function (val, msg) { - new Assertion(val, msg, assert.isNaN, true).to.be.NaN; +assert.isNaN = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNaN, true).to.be.NaN; }; /** @@ -440,13 +1039,13 @@ assert.isNaN = function (val, msg) { * assert.isNotNaN(4, '4 is not NaN'); * * @name isNotNaN - * @param {unknown} value - * @param {string} message + * @param {unknown} val + * @param {string} msg * @namespace Assert * @public */ -assert.isNotNaN = function (value, message) { - new Assertion(value, message, assert.isNotNaN, true).not.to.be.NaN; +assert.isNotNaN = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNotNaN, true).not.to.be.NaN; }; /** @@ -463,8 +1062,8 @@ assert.isNotNaN = function (value, message) { * @namespace Assert * @public */ -assert.exists = function (val, msg) { - new Assertion(val, msg, assert.exists, true).to.exist; +assert.exists = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.exists, true).to.exist; }; /** @@ -484,8 +1083,8 @@ assert.exists = function (val, msg) { * @namespace Assert * @public */ -assert.notExists = function (val, msg) { - new Assertion(val, msg, assert.notExists, true).to.not.exist; +assert.notExists = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.notExists, true).to.not.exist; }; /** @@ -502,8 +1101,8 @@ assert.notExists = function (val, msg) { * @namespace Assert * @public */ -assert.isUndefined = function (val, msg) { - new Assertion(val, msg, assert.isUndefined, true).to.equal(undefined); +assert.isUndefined = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isUndefined, true).to.equal(undefined); }; /** @@ -520,8 +1119,8 @@ assert.isUndefined = function (val, msg) { * @namespace Assert * @public */ -assert.isDefined = function (val, msg) { - new Assertion(val, msg, assert.isDefined, true).to.not.equal(undefined); +assert.isDefined = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isDefined, true).to.not.equal(undefined); }; /** @@ -538,10 +1137,12 @@ assert.isDefined = function (val, msg) { * @namespace Assert * @public */ -assert.isCallable = function (value, message) { +assert.isCallable = function (value: unknown, message?: string) { new Assertion(value, message, assert.isCallable, true).is.callable; }; +assert.isFunction = assert.isCallable; + /** * ### .isNotCallable(value, [message]) * @@ -556,10 +1157,12 @@ assert.isCallable = function (value, message) { * @namespace Assert * @public */ -assert.isNotCallable = function (value, message) { +assert.isNotCallable = function (value: unknown, message?: string) { new Assertion(value, message, assert.isNotCallable, true).is.not.callable; }; +assert.isNotFunction = assert.isNotCallable; + /** * ### .isObject(value, [message]) * @@ -575,8 +1178,8 @@ assert.isNotCallable = function (value, message) { * @namespace Assert * @public */ -assert.isObject = function (val, msg) { - new Assertion(val, msg, assert.isObject, true).to.be.a('object'); +assert.isObject = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isObject, true).to.be.a('object'); }; /** @@ -594,8 +1197,8 @@ assert.isObject = function (val, msg) { * @namespace Assert * @public */ -assert.isNotObject = function (val, msg) { - new Assertion(val, msg, assert.isNotObject, true).to.not.be.a('object'); +assert.isNotObject = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNotObject, true).to.not.be.a('object'); }; /** @@ -612,8 +1215,8 @@ assert.isNotObject = function (val, msg) { * @namespace Assert * @public */ -assert.isArray = function (val, msg) { - new Assertion(val, msg, assert.isArray, true).to.be.an('array'); +assert.isArray = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isArray, true).to.be.an('array'); }; /** @@ -630,8 +1233,8 @@ assert.isArray = function (val, msg) { * @namespace Assert * @public */ -assert.isNotArray = function (val, msg) { - new Assertion(val, msg, assert.isNotArray, true).to.not.be.an('array'); +assert.isNotArray = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNotArray, true).to.not.be.an('array'); }; /** @@ -648,8 +1251,8 @@ assert.isNotArray = function (val, msg) { * @namespace Assert * @public */ -assert.isString = function (val, msg) { - new Assertion(val, msg, assert.isString, true).to.be.a('string'); +assert.isString = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isString, true).to.be.a('string'); }; /** @@ -666,8 +1269,8 @@ assert.isString = function (val, msg) { * @namespace Assert * @public */ -assert.isNotString = function (val, msg) { - new Assertion(val, msg, assert.isNotString, true).to.not.be.a('string'); +assert.isNotString = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNotString, true).to.not.be.a('string'); }; /** @@ -684,8 +1287,8 @@ assert.isNotString = function (val, msg) { * @namespace Assert * @public */ -assert.isNumber = function (val, msg) { - new Assertion(val, msg, assert.isNumber, true).to.be.a('number'); +assert.isNumber = function (val: number, msg?: string) { + Assertion.create(val, msg, assert.isNumber, true).to.be.a('number'); }; /** @@ -702,7 +1305,7 @@ assert.isNumber = function (val, msg) { * @namespace Assert * @public */ -assert.isNotNumber = function (val, msg) { +assert.isNotNumber = function (val: unknown, msg?: string) { new Assertion(val, msg, assert.isNotNumber, true).to.not.be.a('number'); }; @@ -723,7 +1326,7 @@ assert.isNotNumber = function (val, msg) { * @namespace Assert * @public */ -assert.isNumeric = function (val, msg) { +assert.isNumeric = function (val: unknown, msg?: string) { new Assertion(val, msg, assert.isNumeric, true).is.numeric; }; @@ -741,7 +1344,7 @@ assert.isNumeric = function (val, msg) { * @namespace Assert * @public */ -assert.isNotNumeric = function (val, msg) { +assert.isNotNumeric = function (val: unknown, msg?: string) { new Assertion(val, msg, assert.isNotNumeric, true).is.not.numeric; }; @@ -760,7 +1363,7 @@ assert.isNotNumeric = function (val, msg) { * @namespace Assert * @public */ -assert.isFinite = function (val, msg) { +assert.isFinite = function (val: unknown, msg?: string) { new Assertion(val, msg, assert.isFinite, true).to.be.finite; }; @@ -781,8 +1384,8 @@ assert.isFinite = function (val, msg) { * @namespace Assert * @public */ -assert.isBoolean = function (val, msg) { - new Assertion(val, msg, assert.isBoolean, true).to.be.a('boolean'); +assert.isBoolean = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isBoolean, true).to.be.a('boolean'); }; /** @@ -802,8 +1405,8 @@ assert.isBoolean = function (val, msg) { * @namespace Assert * @public */ -assert.isNotBoolean = function (val, msg) { - new Assertion(val, msg, assert.isNotBoolean, true).to.not.be.a('boolean'); +assert.isNotBoolean = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNotBoolean, true).to.not.be.a('boolean'); }; /** @@ -826,8 +1429,8 @@ assert.isNotBoolean = function (val, msg) { * @namespace Assert * @public */ -assert.typeOf = function (val, type, msg) { - new Assertion(val, msg, assert.typeOf, true).to.be.a(type); +assert.typeOf = function (val: unknown, type: string, msg?: string) { + Assertion.create(val, msg, assert.typeOf, true).to.be.a(type); }; /** @@ -839,14 +1442,14 @@ assert.typeOf = function (val, type, msg) { * assert.notTypeOf('tea', 'number', 'strings are not numbers'); * * @name notTypeOf - * @param {unknown} value + * @param {unknown} val * @param {string} type - * @param {string} message + * @param {string} msg * @namespace Assert * @public */ -assert.notTypeOf = function (value, type, message) { - new Assertion(value, message, assert.notTypeOf, true).to.not.be.a(type); +assert.notTypeOf = function (val: unknown, type: string, msg?: string) { + Assertion.create(val, msg, assert.notTypeOf, true).to.not.be.a(type); }; /** @@ -866,8 +1469,12 @@ assert.notTypeOf = function (value, type, message) { * @namespace Assert * @public */ -assert.instanceOf = function (val, type, msg) { - new Assertion(val, msg, assert.instanceOf, true).to.be.instanceOf(type); +assert.instanceOf = function instanceOf<T>( + val: T, + type: Constructor<T>, + msg?: string +) { + Assertion.create(val, msg, assert.instanceOf, true).to.be.instanceOf(type); }; /** @@ -887,8 +1494,12 @@ assert.instanceOf = function (val, type, msg) { * @namespace Assert * @public */ -assert.notInstanceOf = function (val, type, msg) { - new Assertion(val, msg, assert.notInstanceOf, true).to.not.be.instanceOf( +assert.notInstanceOf = function ( + val: object, + type: Constructor<unknown>, + msg?: string +) { + Assertion.create(val, msg, assert.notInstanceOf, true).to.not.be.instanceOf( type ); }; @@ -923,8 +1534,12 @@ assert.notInstanceOf = function (val, type, msg) { * @namespace Assert * @public */ -assert.include = function (exp, inc, msg) { - new Assertion(exp, msg, assert.include, true).include(inc); +assert.include = function include( + exp: CollectionLike<never> | string | object, + inc: unknown, + msg?: string +) { + Assertion.create(exp, msg, assert.include, true).include(inc); }; /** @@ -958,8 +1573,12 @@ assert.include = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.notInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.notInclude, true).not.include(inc); +assert.notInclude = function notInclude( + exp: CollectionLike<never> | string | object, + inc: unknown, + msg?: string +) { + Assertion.create(exp, msg, assert.notInclude, true).not.include(inc); }; /** @@ -982,8 +1601,12 @@ assert.notInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.deepInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.deepInclude, true).deep.include(inc); +assert.deepInclude = function deepInclude( + exp: CollectionLike<never> | object, + inc: unknown, + msg?: string +) { + Assertion.create(exp, msg, assert.deepInclude, true).deep.include(inc); }; /** @@ -1006,8 +1629,12 @@ assert.deepInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.notDeepInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.notDeepInclude, true).not.deep.include(inc); +assert.notDeepInclude = function notDeepInclude( + exp: CollectionLike<never> | object, + inc: unknown, + msg?: string +) { + Assertion.create(exp, msg, assert.notDeepInclude, true).not.deep.include(inc); }; /** @@ -1030,8 +1657,12 @@ assert.notDeepInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.nestedInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.nestedInclude, true).nested.include(inc); +assert.nestedInclude = function ( + exp: CollectionLike<never> | object, + inc: unknown, + msg?: string +) { + Assertion.create(exp, msg, assert.nestedInclude, true).nested.include(inc); }; /** @@ -1054,8 +1685,12 @@ assert.nestedInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.notNestedInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.notNestedInclude, true).not.nested.include( +assert.notNestedInclude = function ( + exp: CollectionLike<never> | object, + inc: unknown, + msg?: string +) { + Assertion.create(exp, msg, assert.notNestedInclude, true).not.nested.include( inc ); }; @@ -1080,10 +1715,17 @@ assert.notNestedInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.deepNestedInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.deepNestedInclude, true).deep.nested.include( - inc - ); +assert.deepNestedInclude = function ( + exp: CollectionLike<never> | object, + inc: unknown, + msg?: string +) { + Assertion.create( + exp, + msg, + assert.deepNestedInclude, + true + ).deep.nested.include(inc); }; /** @@ -1106,8 +1748,12 @@ assert.deepNestedInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.notDeepNestedInclude = function (exp, inc, msg) { - new Assertion( +assert.notDeepNestedInclude = function ( + exp: CollectionLike<never> | object, + inc: unknown, + msg?: string +) { + Assertion.create( exp, msg, assert.notDeepNestedInclude, @@ -1131,8 +1777,8 @@ assert.notDeepNestedInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.ownInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.ownInclude, true).own.include(inc); +assert.ownInclude = function (exp: object, inc: unknown, msg?: string) { + Assertion.create(exp, msg, assert.ownInclude, true).own.include(inc); }; /** @@ -1152,8 +1798,8 @@ assert.ownInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.notOwnInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.notOwnInclude, true).not.own.include(inc); +assert.notOwnInclude = function (exp: object, inc: unknown, msg?: string) { + Assertion.create(exp, msg, assert.notOwnInclude, true).not.own.include(inc); }; /** @@ -1172,8 +1818,8 @@ assert.notOwnInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.deepOwnInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.deepOwnInclude, true).deep.own.include(inc); +assert.deepOwnInclude = function (exp: object, inc: unknown, msg?: string) { + Assertion.create(exp, msg, assert.deepOwnInclude, true).deep.own.include(inc); }; /** @@ -1192,10 +1838,13 @@ assert.deepOwnInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.notDeepOwnInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.notDeepOwnInclude, true).not.deep.own.include( - inc - ); +assert.notDeepOwnInclude = function (exp: object, inc: unknown, msg?: string) { + Assertion.create( + exp, + msg, + assert.notDeepOwnInclude, + true + ).not.deep.own.include(inc); }; /** @@ -1212,8 +1861,8 @@ assert.notDeepOwnInclude = function (exp, inc, msg) { * @namespace Assert * @public */ -assert.match = function (exp, re, msg) { - new Assertion(exp, msg, assert.match, true).to.match(re); +assert.match = function (exp: string, re: RegExp, msg?: string) { + Assertion.create(exp, msg, assert.match, true).to.match(re); }; /** @@ -1230,8 +1879,8 @@ assert.match = function (exp, re, msg) { * @namespace Assert * @public */ -assert.notMatch = function (exp, re, msg) { - new Assertion(exp, msg, assert.notMatch, true).to.not.match(re); +assert.notMatch = function (exp: string, re: RegExp, msg?: string) { + Assertion.create(exp, msg, assert.notMatch, true).to.not.match(re); }; /** @@ -1250,8 +1899,12 @@ assert.notMatch = function (exp, re, msg) { * @namespace Assert * @public */ -assert.property = function (obj, prop, msg) { - new Assertion(obj, msg, assert.property, true).to.have.property(prop); +assert.property = function property( + obj: object, + prop: PropertyKey, + msg?: string +) { + Assertion.create(obj, msg, assert.property, true).to.have.property(prop); }; /** @@ -1269,8 +1922,10 @@ assert.property = function (obj, prop, msg) { * @namespace Assert * @public */ -assert.notProperty = function (obj, prop, msg) { - new Assertion(obj, msg, assert.notProperty, true).to.not.have.property(prop); +assert.notProperty = function (obj: object, prop: string, msg?: string) { + Assertion.create(obj, msg, assert.notProperty, true).to.not.have.property( + prop + ); }; /** @@ -1290,8 +1945,14 @@ assert.notProperty = function (obj, prop, msg) { * @namespace Assert * @public */ -assert.propertyVal = function (obj, prop, val, msg) { - new Assertion(obj, msg, assert.propertyVal, true).to.have.property(prop, val); +assert.propertyVal = function propertyVal< + T extends object, + TKey extends keyof T +>(obj: T, prop: TKey, val: T[TKey], msg?: string) { + Assertion.create(obj, msg, assert.propertyVal, true).to.have.property( + prop, + val + ); }; /** @@ -1312,8 +1973,13 @@ assert.propertyVal = function (obj, prop, val, msg) { * @namespace Assert * @public */ -assert.notPropertyVal = function (obj, prop, val, msg) { - new Assertion(obj, msg, assert.notPropertyVal, true).to.not.have.property( +assert.notPropertyVal = function notPropertyVal<T, TKey extends keyof T>( + obj: T, + prop: TKey, + val: T[TKey], + msg?: string +) { + Assertion.create(obj, msg, assert.notPropertyVal, true).to.not.have.property( prop, val ); @@ -1335,11 +2001,18 @@ assert.notPropertyVal = function (obj, prop, val, msg) { * @namespace Assert * @public */ -assert.deepPropertyVal = function (obj, prop, val, msg) { - new Assertion(obj, msg, assert.deepPropertyVal, true).to.have.deep.property( - prop, - val - ); +assert.deepPropertyVal = function deepPropertyVal<T, TKey extends keyof T>( + obj: T, + prop: TKey, + val: T[TKey], + msg?: string +) { + Assertion.create( + obj, + msg, + assert.deepPropertyVal, + true + ).to.have.deep.property(prop, val); }; /** @@ -1360,8 +2033,11 @@ assert.deepPropertyVal = function (obj, prop, val, msg) { * @namespace Assert * @public */ -assert.notDeepPropertyVal = function (obj, prop, val, msg) { - new Assertion( +assert.notDeepPropertyVal = function notDeepPropertyVal< + T, + TKey extends keyof T +>(obj: T, prop: TKey, val: T[TKey], msg?: string) { + Assertion.create( obj, msg, assert.notDeepPropertyVal, @@ -1383,8 +2059,13 @@ assert.notDeepPropertyVal = function (obj, prop, val, msg) { * @param {string} msg * @public */ -assert.ownProperty = function (obj, prop, msg) { - new Assertion(obj, msg, assert.ownProperty, true).to.have.own.property(prop); +assert.ownProperty = function ownProperty< + T extends object, + TKey extends PropertyKey +>(obj: T, prop: TKey, msg?: string) { + Assertion.create(obj, msg, assert.ownProperty, true).to.have.own.property( + prop + ); }; /** @@ -1402,10 +2083,13 @@ assert.ownProperty = function (obj, prop, msg) { * @param {string} msg * @public */ -assert.notOwnProperty = function (obj, prop, msg) { - new Assertion(obj, msg, assert.notOwnProperty, true).to.not.have.own.property( - prop - ); +assert.notOwnProperty = function (obj: object, prop: string, msg?: string) { + Assertion.create( + obj, + msg, + assert.notOwnProperty, + true + ).to.not.have.own.property(prop); }; /** @@ -1424,8 +2108,13 @@ assert.notOwnProperty = function (obj, prop, msg) { * @param {string} msg * @public */ -assert.ownPropertyVal = function (obj, prop, value, msg) { - new Assertion(obj, msg, assert.ownPropertyVal, true).to.have.own.property( +assert.ownPropertyVal = function ownPropertyVal<T, TKey extends keyof T>( + obj: T, + prop: TKey, + value: T[TKey], + msg?: string +) { + Assertion.create(obj, msg, assert.ownPropertyVal, true).to.have.own.property( prop, value ); @@ -1448,8 +2137,13 @@ assert.ownPropertyVal = function (obj, prop, value, msg) { * @param {string} msg * @public */ -assert.notOwnPropertyVal = function (obj, prop, value, msg) { - new Assertion( +assert.notOwnPropertyVal = function notOwnPropertyVal<T, TKey extends keyof T>( + obj: T, + prop: TKey, + value: T[TKey], + msg?: string +) { + Assertion.create( obj, msg, assert.notOwnPropertyVal, @@ -1473,8 +2167,11 @@ assert.notOwnPropertyVal = function (obj, prop, value, msg) { * @param {string} msg * @public */ -assert.deepOwnPropertyVal = function (obj, prop, value, msg) { - new Assertion( +assert.deepOwnPropertyVal = function deepOwnPropertyVal< + T, + TKey extends keyof T +>(obj: T, prop: TKey, value: T[TKey], msg?: string) { + Assertion.create( obj, msg, assert.deepOwnPropertyVal, @@ -1501,8 +2198,11 @@ assert.deepOwnPropertyVal = function (obj, prop, value, msg) { * @param {string} msg * @public */ -assert.notDeepOwnPropertyVal = function (obj, prop, value, msg) { - new Assertion( +assert.notDeepOwnPropertyVal = function notDeepOwnPropertyVal< + T, + TKey extends keyof T +>(obj: T, prop: TKey, value: T[TKey], msg?: string) { + Assertion.create( obj, msg, assert.notDeepOwnPropertyVal, @@ -1526,10 +2226,13 @@ assert.notDeepOwnPropertyVal = function (obj, prop, value, msg) { * @namespace Assert * @public */ -assert.nestedProperty = function (obj, prop, msg) { - new Assertion(obj, msg, assert.nestedProperty, true).to.have.nested.property( - prop - ); +assert.nestedProperty = function (obj: object, prop: string, msg?: string) { + Assertion.create( + obj, + msg, + assert.nestedProperty, + true + ).to.have.nested.property(prop); }; /** @@ -1548,8 +2251,8 @@ assert.nestedProperty = function (obj, prop, msg) { * @namespace Assert * @public */ -assert.notNestedProperty = function (obj, prop, msg) { - new Assertion( +assert.notNestedProperty = function (obj: object, prop: string, msg?: string) { + Assertion.create( obj, msg, assert.notNestedProperty, @@ -1574,8 +2277,13 @@ assert.notNestedProperty = function (obj, prop, msg) { * @namespace Assert * @public */ -assert.nestedPropertyVal = function (obj, prop, val, msg) { - new Assertion( +assert.nestedPropertyVal = function ( + obj: object, + prop: string, + val: unknown, + msg?: string +) { + Assertion.create( obj, msg, assert.nestedPropertyVal, @@ -1601,8 +2309,13 @@ assert.nestedPropertyVal = function (obj, prop, val, msg) { * @namespace Assert * @public */ -assert.notNestedPropertyVal = function (obj, prop, val, msg) { - new Assertion( +assert.notNestedPropertyVal = function ( + obj: object, + prop: string, + val: unknown, + msg?: string +) { + Assertion.create( obj, msg, assert.notNestedPropertyVal, @@ -1627,8 +2340,13 @@ assert.notNestedPropertyVal = function (obj, prop, val, msg) { * @namespace Assert * @public */ -assert.deepNestedPropertyVal = function (obj, prop, val, msg) { - new Assertion( +assert.deepNestedPropertyVal = function ( + obj: object, + prop: string, + val: unknown, + msg?: string +) { + Assertion.create( obj, msg, assert.deepNestedPropertyVal, @@ -1655,8 +2373,13 @@ assert.deepNestedPropertyVal = function (obj, prop, val, msg) { * @namespace Assert * @public */ -assert.notDeepNestedPropertyVal = function (obj, prop, val, msg) { - new Assertion( +assert.notDeepNestedPropertyVal = function ( + obj: object, + prop: string, + val: unknown, + msg?: string +) { + Assertion.create( obj, msg, assert.notDeepNestedPropertyVal, @@ -1681,8 +2404,8 @@ assert.notDeepNestedPropertyVal = function (obj, prop, val, msg) { * @namespace Assert * @public */ -assert.lengthOf = function (exp, len, msg) { - new Assertion(exp, msg, assert.lengthOf, true).to.have.lengthOf(len); +assert.lengthOf = function (exp: LengthLike, len: number, msg?: string) { + Assertion.create(exp, msg, assert.lengthOf, true).to.have.lengthOf(len); }; /** @@ -1704,8 +2427,14 @@ assert.lengthOf = function (exp, len, msg) { * @namespace Assert * @public */ -assert.hasAnyKeys = function (obj, keys, msg) { - new Assertion(obj, msg, assert.hasAnyKeys, true).to.have.any.keys(keys); +assert.hasAnyKeys = function ( + obj: KeyedObject, + keys: Array<unknown> | Record<PropertyKey, unknown>, + msg?: string +) { + Assertion.create(obj, msg, assert.hasAnyKeys, true).to.have.any.keys( + keys as PropertyKey[] + ); }; /** @@ -1727,8 +2456,8 @@ assert.hasAnyKeys = function (obj, keys, msg) { * @namespace Assert * @public */ -assert.hasAllKeys = function (obj, keys, msg) { - new Assertion(obj, msg, assert.hasAllKeys, true).to.have.all.keys(keys); +assert.hasAllKeys = function (obj: KeyedObject, keys: string[], msg?: string) { + Assertion.create(obj, msg, assert.hasAllKeys, true).to.have.all.keys(keys); }; /** @@ -1754,8 +2483,12 @@ assert.hasAllKeys = function (obj, keys, msg) { * @namespace Assert * @public */ -assert.containsAllKeys = function (obj, keys, msg) { - new Assertion(obj, msg, assert.containsAllKeys, true).to.contain.all.keys( +assert.containsAllKeys = function ( + obj: KeyedObject, + keys: string[], + msg?: string +) { + Assertion.create(obj, msg, assert.containsAllKeys, true).to.contain.all.keys( keys ); }; @@ -1779,10 +2512,17 @@ assert.containsAllKeys = function (obj, keys, msg) { * @namespace Assert * @public */ -assert.doesNotHaveAnyKeys = function (obj, keys, msg) { - new Assertion(obj, msg, assert.doesNotHaveAnyKeys, true).to.not.have.any.keys( - keys - ); +assert.doesNotHaveAnyKeys = function ( + obj: KeyedObject, + keys: string[], + msg?: string +) { + Assertion.create( + obj, + msg, + assert.doesNotHaveAnyKeys, + true + ).to.not.have.any.keys(keys); }; /** @@ -1804,10 +2544,17 @@ assert.doesNotHaveAnyKeys = function (obj, keys, msg) { * @namespace Assert * @public */ -assert.doesNotHaveAllKeys = function (obj, keys, msg) { - new Assertion(obj, msg, assert.doesNotHaveAllKeys, true).to.not.have.all.keys( - keys - ); +assert.doesNotHaveAllKeys = function ( + obj: KeyedObject, + keys: string[], + msg?: string +) { + Assertion.create( + obj, + msg, + assert.doesNotHaveAllKeys, + true + ).to.not.have.all.keys(keys); }; /** @@ -1833,9 +2580,13 @@ assert.doesNotHaveAllKeys = function (obj, keys, msg) { * @namespace Assert * @public */ -assert.hasAnyDeepKeys = function (obj, keys, msg) { - new Assertion(obj, msg, assert.hasAnyDeepKeys, true).to.have.any.deep.keys( - keys +assert.hasAnyDeepKeys = function ( + obj: KeyedObject, + keys: Array<unknown> | Record<PropertyKey, unknown>, + msg?: string +) { + Assertion.create(obj, msg, assert.hasAnyDeepKeys, true).to.have.any.deep.keys( + keys as PropertyKey[] ); }; @@ -1860,9 +2611,13 @@ assert.hasAnyDeepKeys = function (obj, keys, msg) { * @namespace Assert * @public */ -assert.hasAllDeepKeys = function (obj, keys, msg) { - new Assertion(obj, msg, assert.hasAllDeepKeys, true).to.have.all.deep.keys( - keys +assert.hasAllDeepKeys = function ( + obj: KeyedObject, + keys: Array<unknown> | Record<PropertyKey, unknown>, + msg?: string +) { + Assertion.create(obj, msg, assert.hasAllDeepKeys, true).to.have.all.deep.keys( + keys as PropertyKey[] ); }; @@ -1887,13 +2642,17 @@ assert.hasAllDeepKeys = function (obj, keys, msg) { * @namespace Assert * @public */ -assert.containsAllDeepKeys = function (obj, keys, msg) { - new Assertion( +assert.containsAllDeepKeys = function ( + obj: KeyedObject, + keys: Array<unknown> | Record<PropertyKey, unknown>, + msg?: string +) { + Assertion.create( obj, msg, assert.containsAllDeepKeys, true - ).to.contain.all.deep.keys(keys); + ).to.contain.all.deep.keys(keys as PropertyKey[]); }; /** @@ -1917,13 +2676,17 @@ assert.containsAllDeepKeys = function (obj, keys, msg) { * @namespace Assert * @public */ -assert.doesNotHaveAnyDeepKeys = function (obj, keys, msg) { - new Assertion( +assert.doesNotHaveAnyDeepKeys = function ( + obj: KeyedObject, + keys: Array<unknown> | Record<PropertyKey, unknown>, + msg?: string +) { + Assertion.create( obj, msg, assert.doesNotHaveAnyDeepKeys, true - ).to.not.have.any.deep.keys(keys); + ).to.not.have.any.deep.keys(keys as PropertyKey[]); }; /** @@ -1947,13 +2710,17 @@ assert.doesNotHaveAnyDeepKeys = function (obj, keys, msg) { * @namespace Assert * @public */ -assert.doesNotHaveAllDeepKeys = function (obj, keys, msg) { - new Assertion( +assert.doesNotHaveAllDeepKeys = function ( + obj: KeyedObject, + keys: Array<unknown> | Record<PropertyKey, unknown>, + msg?: string +) { + Assertion.create( obj, msg, assert.doesNotHaveAllDeepKeys, true - ).to.not.have.all.deep.keys(keys); + ).to.not.have.all.deep.keys(keys as PropertyKey[]); }; /** @@ -1987,18 +2754,36 @@ assert.doesNotHaveAllDeepKeys = function (obj, keys, msg) { * @namespace Assert * @public */ -assert.throws = function (fn, errorLike, errMsgMatcher, msg) { - if ('string' === typeof errorLike || errorLike instanceof RegExp) { - errMsgMatcher = errorLike; - errorLike = null; +function assertThrows( + fn: Function, + errorLike: Error | Constructor<Error>, + errMsgMatcher: RegExp | string, + msg?: string +): unknown; +function assertThrows(fn: Function, errMsgMatcher: RegExp | string): unknown; +function assertThrows( + fn: Function, + errorOrMatcher: Error | Constructor<Error> | RegExp | string, + errMsgMatcher?: RegExp | string, + msg?: string +): unknown { + let assertErr: Assertion<Function>; + + if ('string' === typeof errorOrMatcher || errorOrMatcher instanceof RegExp) { + assertErr = Assertion.create(fn, msg, assertThrows, true).to.throw( + errorOrMatcher + ); + } else { + assertErr = Assertion.create(fn, msg, assertThrows, true).to.throw( + errorOrMatcher, + errMsgMatcher as RegExp | string + ); } - var assertErr = new Assertion(fn, msg, assert.throws, true).to.throw( - errorLike, - errMsgMatcher - ); return flag(assertErr, 'object'); -}; +} + +assert.throws = assert.Throw = assert.throw = assertThrows; /** * ### .doesNotThrow(fn, [errorLike/string/regexp], [string/regexp], [message]) @@ -2021,24 +2806,39 @@ assert.throws = function (fn, errorLike, errMsgMatcher, msg) { * * @name doesNotThrow * @param {Function} fn - * @param {Error} errorLike + * @param {Error|Constructor<Error>} errorOrMatcher * @param {RegExp | string} errMsgMatcher - * @param {string} message + * @param {string} msg * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types * @namespace Assert * @public */ -assert.doesNotThrow = function (fn, errorLike, errMsgMatcher, message) { - if ('string' === typeof errorLike || errorLike instanceof RegExp) { - errMsgMatcher = errorLike; - errorLike = null; +function assertDoesNotThrow(fn: Function, errMsgMatcher: RegExp | string): void; +function assertDoesNotThrow( + fn: Function, + errorLike: Error | Constructor<Error>, + errMsgMatcher: RegExp | string, + msg?: string +): void; +function assertDoesNotThrow( + fn: Function, + errorOrMatcher: Error | Constructor<Error> | RegExp | string, + errMsgMatcher?: RegExp | string, + msg?: string +): void { + if ('string' === typeof errorOrMatcher || errorOrMatcher instanceof RegExp) { + Assertion.create(fn, msg, assertDoesNotThrow, true).to.not.throw( + errorOrMatcher + ); + } else { + Assertion.create(fn, msg, assertDoesNotThrow, true).to.not.throw( + errorOrMatcher, + errMsgMatcher as RegExp | string + ); } +} - new Assertion(fn, message, assert.doesNotThrow, true).to.not.throw( - errorLike, - errMsgMatcher - ); -}; +assert.doesNotThrow = assertDoesNotThrow; /** * ### .operator(val1, operator, val2, [message]) @@ -2056,7 +2856,12 @@ assert.doesNotThrow = function (fn, errorLike, errMsgMatcher, message) { * @namespace Assert * @public */ -assert.operator = function (val, operator, val2, msg) { +assert.operator = function ( + val: unknown, + operator: string, + val2: unknown, + msg?: string +) { var ok; switch (operator) { case '==': @@ -2066,16 +2871,16 @@ assert.operator = function (val, operator, val2, msg) { ok = val === val2; break; case '>': - ok = val > val2; + ok = (val as number) > (val2 as number); break; case '>=': - ok = val >= val2; + ok = (val as number) >= (val2 as number); break; case '<': - ok = val < val2; + ok = (val as number) < (val2 as number); break; case '<=': - ok = val <= val2; + ok = (val as number) <= (val2 as number); break; case '!=': ok = val != val2; @@ -2091,7 +2896,7 @@ assert.operator = function (val, operator, val2, msg) { assert.operator ); } - var test = new Assertion(ok, msg, assert.operator, true); + var test = Assertion.create(ok, msg, assert.operator, true); test.assert( true === flag(test, 'object'), 'expected ' + inspect(val) + ' to be ' + operator + ' ' + inspect(val2), @@ -2114,8 +2919,13 @@ assert.operator = function (val, operator, val2, msg) { * @namespace Assert * @public */ -assert.closeTo = function (act, exp, delta, msg) { - new Assertion(act, msg, assert.closeTo, true).to.be.closeTo(exp, delta); +assert.closeTo = function ( + act: number, + exp: number, + delta: number, + msg?: string +) { + Assertion.create(act, msg, assert.closeTo, true).to.be.closeTo(exp, delta); }; /** @@ -2133,8 +2943,13 @@ assert.closeTo = function (act, exp, delta, msg) { * @namespace Assert * @public */ -assert.approximately = function (act, exp, delta, msg) { - new Assertion(act, msg, assert.approximately, true).to.be.approximately( +assert.approximately = function ( + act: number, + exp: number, + delta: number, + msg?: string +) { + Assertion.create(act, msg, assert.approximately, true).to.be.approximately( exp, delta ); @@ -2155,8 +2970,10 @@ assert.approximately = function (act, exp, delta, msg) { * @namespace Assert * @public */ -assert.sameMembers = function (set1, set2, msg) { - new Assertion(set1, msg, assert.sameMembers, true).to.have.same.members(set2); +assert.sameMembers = function (set1: unknown[], set2: unknown[], msg?: string) { + Assertion.create(set1, msg, assert.sameMembers, true).to.have.same.members( + set2 + ); }; /** @@ -2174,8 +2991,12 @@ assert.sameMembers = function (set1, set2, msg) { * @namespace Assert * @public */ -assert.notSameMembers = function (set1, set2, msg) { - new Assertion( +assert.notSameMembers = function ( + set1: unknown[], + set2: unknown[], + msg?: string +) { + Assertion.create( set1, msg, assert.notSameMembers, @@ -2198,8 +3019,12 @@ assert.notSameMembers = function (set1, set2, msg) { * @namespace Assert * @public */ -assert.sameDeepMembers = function (set1, set2, msg) { - new Assertion( +assert.sameDeepMembers = function ( + set1: unknown[], + set2: unknown[], + msg?: string +) { + Assertion.create( set1, msg, assert.sameDeepMembers, @@ -2222,8 +3047,12 @@ assert.sameDeepMembers = function (set1, set2, msg) { * @namespace Assert * @public */ -assert.notSameDeepMembers = function (set1, set2, msg) { - new Assertion( +assert.notSameDeepMembers = function ( + set1: unknown[], + set2: unknown[], + msg?: string +) { + Assertion.create( set1, msg, assert.notSameDeepMembers, @@ -2246,8 +3075,12 @@ assert.notSameDeepMembers = function (set1, set2, msg) { * @namespace Assert * @public */ -assert.sameOrderedMembers = function (set1, set2, msg) { - new Assertion( +assert.sameOrderedMembers = function ( + set1: unknown[], + set2: unknown[], + msg?: string +) { + Assertion.create( set1, msg, assert.sameOrderedMembers, @@ -2270,8 +3103,12 @@ assert.sameOrderedMembers = function (set1, set2, msg) { * @namespace Assert * @public */ -assert.notSameOrderedMembers = function (set1, set2, msg) { - new Assertion( +assert.notSameOrderedMembers = function ( + set1: unknown[], + set2: unknown[], + msg?: string +) { + Assertion.create( set1, msg, assert.notSameOrderedMembers, @@ -2294,8 +3131,12 @@ assert.notSameOrderedMembers = function (set1, set2, msg) { * @namespace Assert * @public */ -assert.sameDeepOrderedMembers = function (set1, set2, msg) { - new Assertion( +assert.sameDeepOrderedMembers = function ( + set1: unknown[], + set2: unknown[], + msg?: string +) { + Assertion.create( set1, msg, assert.sameDeepOrderedMembers, @@ -2319,8 +3160,12 @@ assert.sameDeepOrderedMembers = function (set1, set2, msg) { * @namespace Assert * @public */ -assert.notSameDeepOrderedMembers = function (set1, set2, msg) { - new Assertion( +assert.notSameDeepOrderedMembers = function ( + set1: unknown[], + set2: unknown[], + msg?: string +) { + Assertion.create( set1, msg, assert.notSameDeepOrderedMembers, @@ -2343,10 +3188,17 @@ assert.notSameDeepOrderedMembers = function (set1, set2, msg) { * @namespace Assert * @public */ -assert.includeMembers = function (superset, subset, msg) { - new Assertion(superset, msg, assert.includeMembers, true).to.include.members( - subset - ); +assert.includeMembers = function ( + superset: unknown[], + subset: unknown[], + msg?: string +) { + Assertion.create( + superset, + msg, + assert.includeMembers, + true + ).to.include.members(subset); }; /** @@ -2364,8 +3216,12 @@ assert.includeMembers = function (superset, subset, msg) { * @namespace Assert * @public */ -assert.notIncludeMembers = function (superset, subset, msg) { - new Assertion( +assert.notIncludeMembers = function ( + superset: unknown[], + subset: unknown[], + msg?: string +) { + Assertion.create( superset, msg, assert.notIncludeMembers, @@ -2388,8 +3244,12 @@ assert.notIncludeMembers = function (superset, subset, msg) { * @namespace Assert * @public */ -assert.includeDeepMembers = function (superset, subset, msg) { - new Assertion( +assert.includeDeepMembers = function ( + superset: unknown[], + subset: unknown[], + msg?: string +) { + Assertion.create( superset, msg, assert.includeDeepMembers, @@ -2412,8 +3272,12 @@ assert.includeDeepMembers = function (superset, subset, msg) { * @namespace Assert * @public */ -assert.notIncludeDeepMembers = function (superset, subset, msg) { - new Assertion( +assert.notIncludeDeepMembers = function ( + superset: unknown[], + subset: unknown[], + msg?: string +) { + Assertion.create( superset, msg, assert.notIncludeDeepMembers, @@ -2437,8 +3301,12 @@ assert.notIncludeDeepMembers = function (superset, subset, msg) { * @namespace Assert * @public */ -assert.includeOrderedMembers = function (superset, subset, msg) { - new Assertion( +assert.includeOrderedMembers = function ( + superset: unknown[], + subset: unknown[], + msg?: string +) { + Assertion.create( superset, msg, assert.includeOrderedMembers, @@ -2463,8 +3331,12 @@ assert.includeOrderedMembers = function (superset, subset, msg) { * @namespace Assert * @public */ -assert.notIncludeOrderedMembers = function (superset, subset, msg) { - new Assertion( +assert.notIncludeOrderedMembers = function ( + superset: unknown[], + subset: unknown[], + msg?: string +) { + Assertion.create( superset, msg, assert.notIncludeOrderedMembers, @@ -2488,8 +3360,12 @@ assert.notIncludeOrderedMembers = function (superset, subset, msg) { * @namespace Assert * @public */ -assert.includeDeepOrderedMembers = function (superset, subset, msg) { - new Assertion( +assert.includeDeepOrderedMembers = function ( + superset: unknown[], + subset: unknown[], + msg?: string +) { + Assertion.create( superset, msg, assert.includeDeepOrderedMembers, @@ -2515,8 +3391,12 @@ assert.includeDeepOrderedMembers = function (superset, subset, msg) { * @namespace Assert * @public */ -assert.notIncludeDeepOrderedMembers = function (superset, subset, msg) { - new Assertion( +assert.notIncludeDeepOrderedMembers = function ( + superset: unknown[], + subset: unknown[], + msg?: string +) { + Assertion.create( superset, msg, assert.notIncludeDeepOrderedMembers, @@ -2538,8 +3418,12 @@ assert.notIncludeDeepOrderedMembers = function (superset, subset, msg) { * @namespace Assert * @public */ -assert.oneOf = function (inList, list, msg) { - new Assertion(inList, msg, assert.oneOf, true).to.be.oneOf(list); +assert.oneOf = function ( + inList: string | unknown[], + list: unknown[], + msg?: string +) { + Assertion.create(inList, msg, assert.oneOf, true).to.be.oneOf(list); }; /** @@ -2555,8 +3439,12 @@ assert.oneOf = function (inList, list, msg) { * @namespace Assert * @public */ -assert.isIterable = function (obj, msg) { - if (obj == undefined || !obj[Symbol.iterator]) { +assert.isIterable = function (obj: unknown, msg?: string) { + if ( + obj === null || + obj === undefined || + !(obj as Record<PropertyKey, unknown>)[Symbol.iterator] + ) { msg = msg ? `${msg} expected ${inspect(obj)} to be an iterable` : `expected ${inspect(obj)} to be an iterable`; @@ -2582,14 +3470,32 @@ assert.isIterable = function (obj, msg) { * @namespace Assert * @public */ -assert.changes = function (fn, obj, prop, msg) { +function assertChanges( + fn: Function, + obj: object, + prop: PropertyKey, + msg?: string +): void; +function assertChanges(fn: Function, obj: () => void, msg?: string): void; +function assertChanges( + fn: Function, + obj: object | (() => void), + propOrMsg?: PropertyKey | string, + msg?: string +): void { if (arguments.length === 3 && typeof obj === 'function') { - msg = prop; - prop = null; + Assertion.create(fn, propOrMsg as string, assertChanges, true).to.change( + obj + ); + } else { + Assertion.create(fn, msg, assertChanges, true).to.change( + obj as Record<PropertyKey, unknown>, + propOrMsg as PropertyKey + ); } +} - new Assertion(fn, msg, assert.changes, true).to.change(obj, prop); -}; +assert.changes = assertChanges; /** * ### .changesBy(function, object, property, delta, [message]) @@ -2609,18 +3515,45 @@ assert.changes = function (fn, obj, prop, msg) { * @namespace Assert * @public */ -assert.changesBy = function (fn, obj, prop, delta, msg) { +function assertChangesBy( + fn: Function, + obj: object, + prop: PropertyKey, + delta: number, + msg?: string +): void; +function assertChangesBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string +): void; +function assertChangesBy( + fn: Function, + obj: object | (() => void), + deltaOrProp: number | PropertyKey, + msgOrDelta?: string | number, + msg?: string +): void { if (arguments.length === 4 && typeof obj === 'function') { - var tmpMsg = delta; - delta = prop; - msg = tmpMsg; + Assertion.create(fn, msgOrDelta as string, assertChangesBy, true) + .to.change(obj) + .by(deltaOrProp as number); } else if (arguments.length === 3) { - delta = prop; - prop = null; + Assertion.create(fn, msg, assertChangesBy, true) + .to.change(obj as Function) + .by(deltaOrProp as number); + } else { + Assertion.create(fn, msg, assertChangesBy, true) + .to.change( + obj as Record<PropertyKey, unknown>, + deltaOrProp as PropertyKey + ) + .by(msgOrDelta as number); } +} - new Assertion(fn, msg, assert.changesBy, true).to.change(obj, prop).by(delta); -}; +assert.changesBy = assertChangesBy; /** * ### .doesNotChange(function, object, property, [message]) @@ -2640,17 +3573,39 @@ assert.changesBy = function (fn, obj, prop, delta, msg) { * @namespace Assert * @public */ -assert.doesNotChange = function (fn, obj, prop, msg) { +function assertDoesNotChange( + fn: Function, + obj: object, + prop: PropertyKey, + msg?: string +): Assertion<Function>; +function assertDoesNotChange( + fn: Function, + obj: () => void, + msg?: string +): Assertion<Function>; +function assertDoesNotChange( + fn: Function, + obj: object | (() => void), + propOrMsg?: PropertyKey | string, + msg?: string +): Assertion<Function> { if (arguments.length === 3 && typeof obj === 'function') { - msg = prop; - prop = null; + return Assertion.create( + fn, + propOrMsg as string, + assertDoesNotChange, + true + ).to.not.change(obj); + } else { + return Assertion.create(fn, msg, assertDoesNotChange, true).to.not.change( + obj as Record<PropertyKey, unknown>, + propOrMsg as PropertyKey + ); } +} - return new Assertion(fn, msg, assert.doesNotChange, true).to.not.change( - obj, - prop - ); -}; +assert.doesNotChange = assertDoesNotChange; /** * ### .changesButNotBy(function, object, property, delta, [message]) @@ -2670,20 +3625,45 @@ assert.doesNotChange = function (fn, obj, prop, msg) { * @namespace Assert * @public */ -assert.changesButNotBy = function (fn, obj, prop, delta, msg) { +function assertChangesButNotBy( + fn: Function, + obj: object, + prop: PropertyKey, + delta: number, + msg?: string +): void; +function assertChangesButNotBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string +): void; +function assertChangesButNotBy( + fn: Function, + obj: object | (() => void), + deltaOrProp: number | PropertyKey, + msgOrDelta?: string | number, + msg?: string +): void { if (arguments.length === 4 && typeof obj === 'function') { - var tmpMsg = delta; - delta = prop; - msg = tmpMsg; + Assertion.create(fn, msgOrDelta as string, assertChangesButNotBy, true) + .to.change(obj) + .but.not.by(deltaOrProp as number); } else if (arguments.length === 3) { - delta = prop; - prop = null; + Assertion.create(fn, msg, assertChangesButNotBy, true) + .to.change(obj as Function) + .but.not.by(deltaOrProp as number); + } else { + Assertion.create(fn, msg, assertChangesButNotBy, true) + .to.change( + obj as Record<PropertyKey, unknown>, + deltaOrProp as PropertyKey + ) + .but.not.by(msgOrDelta as number); } +} - new Assertion(fn, msg, assert.changesButNotBy, true).to - .change(obj, prop) - .but.not.by(delta); -}; +assert.changesButNotBy = assertChangesButNotBy; /** * ### .increases(function, object, property, [message]) @@ -2703,14 +3683,39 @@ assert.changesButNotBy = function (fn, obj, prop, delta, msg) { * @param {string} msg - message _optional_ * @returns {unknown} */ -assert.increases = function (fn, obj, prop, msg) { +function assertIncreases( + fn: Function, + obj: object, + prop: PropertyKey, + msg?: string +): Assertion<Function>; +function assertIncreases( + fn: Function, + obj: () => void, + msg?: string +): Assertion<Function>; +function assertIncreases( + fn: Function, + obj: object | (() => void), + propOrMsg?: PropertyKey | string, + msg?: string +): Assertion<Function> { if (arguments.length === 3 && typeof obj === 'function') { - msg = prop; - prop = null; + return Assertion.create( + fn, + propOrMsg as string, + assertIncreases, + true + ).to.increase(obj); + } else { + return Assertion.create(fn, msg, assertIncreases, true).to.increase( + obj as Record<PropertyKey, unknown>, + propOrMsg as PropertyKey + ); } +} - return new Assertion(fn, msg, assert.increases, true).to.increase(obj, prop); -}; +assert.increases = assertIncreases; /** * ### .increasesBy(function, object, property, delta, [message]) @@ -2730,20 +3735,45 @@ assert.increases = function (fn, obj, prop, msg) { * @param {number} delta - change amount (delta) * @param {string} msg - message _optional_ */ -assert.increasesBy = function (fn, obj, prop, delta, msg) { +function assertIncreasesBy( + fn: Function, + obj: object, + prop: PropertyKey, + delta: number, + msg?: string +): void; +function assertIncreasesBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string +): void; +function assertIncreasesBy( + fn: Function, + obj: object | (() => void), + deltaOrProp: number | PropertyKey, + msgOrDelta?: string | number, + msg?: string +): void { if (arguments.length === 4 && typeof obj === 'function') { - var tmpMsg = delta; - delta = prop; - msg = tmpMsg; + Assertion.create(fn, msgOrDelta as string, assertIncreasesBy, true) + .to.increase(obj) + .by(deltaOrProp as number); } else if (arguments.length === 3) { - delta = prop; - prop = null; + Assertion.create(fn, msg, assertIncreasesBy, true) + .to.increase(obj as Function) + .by(deltaOrProp as number); + } else { + Assertion.create(fn, msg, assertIncreasesBy, true) + .to.increase( + obj as Record<PropertyKey, unknown>, + deltaOrProp as PropertyKey + ) + .by(msgOrDelta as number); } +} - new Assertion(fn, msg, assert.increasesBy, true).to - .increase(obj, prop) - .by(delta); -}; +assert.increasesBy = assertIncreasesBy; /** * ### .doesNotIncrease(function, object, property, [message]) @@ -2763,17 +3793,44 @@ assert.increasesBy = function (fn, obj, prop, delta, msg) { * @namespace Assert * @public */ -assert.doesNotIncrease = function (fn, obj, prop, msg) { +function assertDoesNotIncrease( + fn: Function, + obj: object, + prop: PropertyKey, + msg?: string +): Assertion<Function>; +function assertDoesNotIncrease( + fn: Function, + obj: () => void, + msg?: string +): Assertion<Function>; +function assertDoesNotIncrease( + fn: Function, + obj: object | (() => void), + propOrMsg?: PropertyKey | string, + msg?: string +): Assertion<Function> { if (arguments.length === 3 && typeof obj === 'function') { - msg = prop; - prop = null; + return Assertion.create( + fn, + propOrMsg as string, + assertDoesNotIncrease, + true + ).to.not.increase(obj); + } else { + return Assertion.create( + fn, + msg, + assertDoesNotIncrease, + true + ).to.not.increase( + obj as Record<PropertyKey, unknown>, + propOrMsg as PropertyKey + ); } +} - return new Assertion(fn, msg, assert.doesNotIncrease, true).to.not.increase( - obj, - prop - ); -}; +assert.doesNotIncrease = assertDoesNotIncrease; /** * ### .increasesButNotBy(function, object, property, delta, [message]) @@ -2793,20 +3850,45 @@ assert.doesNotIncrease = function (fn, obj, prop, msg) { * @namespace Assert * @public */ -assert.increasesButNotBy = function (fn, obj, prop, delta, msg) { +function assertIncreasesButNotBy( + fn: Function, + obj: object, + prop: PropertyKey, + delta: number, + msg?: string +): void; +function assertIncreasesButNotBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string +): void; +function assertIncreasesButNotBy( + fn: Function, + obj: object | (() => void), + deltaOrProp: number | PropertyKey, + msgOrDelta?: string | number, + msg?: string +): void { if (arguments.length === 4 && typeof obj === 'function') { - var tmpMsg = delta; - delta = prop; - msg = tmpMsg; + Assertion.create(fn, msgOrDelta as string, assertIncreasesButNotBy, true) + .to.increase(obj) + .but.not.by(deltaOrProp as number); } else if (arguments.length === 3) { - delta = prop; - prop = null; + Assertion.create(fn, msg, assertIncreasesButNotBy, true) + .to.increase(obj as Function) + .but.not.by(deltaOrProp as number); + } else { + Assertion.create(fn, msg, assertIncreasesButNotBy, true) + .to.increase( + obj as Record<PropertyKey, unknown>, + deltaOrProp as PropertyKey + ) + .but.not.by(msgOrDelta as number); } +} - new Assertion(fn, msg, assert.increasesButNotBy, true).to - .increase(obj, prop) - .but.not.by(delta); -}; +assert.increasesButNotBy = assertIncreasesButNotBy; /** * ### .decreases(function, object, property, [message]) @@ -2826,14 +3908,39 @@ assert.increasesButNotBy = function (fn, obj, prop, delta, msg) { * @namespace Assert * @public */ -assert.decreases = function (fn, obj, prop, msg) { +function assertDecreases( + fn: Function, + obj: object, + prop: PropertyKey, + msg?: string +): Assertion<Function>; +function assertDecreases( + fn: Function, + obj: () => void, + msg?: string +): Assertion<Function>; +function assertDecreases( + fn: Function, + obj: object | (() => void), + propOrMsg?: PropertyKey | string, + msg?: string +): Assertion<Function> { if (arguments.length === 3 && typeof obj === 'function') { - msg = prop; - prop = null; + return Assertion.create( + fn, + propOrMsg as string, + assertDecreases, + true + ).to.decrease(obj); + } else { + return Assertion.create(fn, msg, assertDecreases, true).to.decrease( + obj as Record<PropertyKey, unknown>, + propOrMsg as PropertyKey + ); } +} - return new Assertion(fn, msg, assert.decreases, true).to.decrease(obj, prop); -}; +assert.decreases = assertDecreases; /** * ### .decreasesBy(function, object, property, delta, [message]) @@ -2853,20 +3960,45 @@ assert.decreases = function (fn, obj, prop, msg) { * @namespace Assert * @public */ -assert.decreasesBy = function (fn, obj, prop, delta, msg) { +function assertDecreasesBy( + fn: Function, + obj: object, + prop: PropertyKey, + delta: number, + msg?: string +): void; +function assertDecreasesBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string +): void; +function assertDecreasesBy( + fn: Function, + obj: object | (() => void), + deltaOrProp: number | PropertyKey, + msgOrDelta?: string | number, + msg?: string +): void { if (arguments.length === 4 && typeof obj === 'function') { - var tmpMsg = delta; - delta = prop; - msg = tmpMsg; + Assertion.create(fn, msgOrDelta as string, assertDecreasesBy, true) + .to.decrease(obj) + .by(deltaOrProp as number); } else if (arguments.length === 3) { - delta = prop; - prop = null; + Assertion.create(fn, msg, assertDecreasesBy, true) + .to.decrease(obj as Function) + .by(deltaOrProp as number); + } else { + Assertion.create(fn, msg, assertDecreasesBy, true) + .to.decrease( + obj as Record<PropertyKey, unknown>, + deltaOrProp as PropertyKey + ) + .by(msgOrDelta as number); } +} - new Assertion(fn, msg, assert.decreasesBy, true).to - .decrease(obj, prop) - .by(delta); -}; +assert.decreasesBy = assertDecreasesBy; /** * ### .doesNotDecrease(function, object, property, [message]) @@ -2886,17 +4018,44 @@ assert.decreasesBy = function (fn, obj, prop, delta, msg) { * @namespace Assert * @public */ -assert.doesNotDecrease = function (fn, obj, prop, msg) { +function assertDoesNotDecrease( + fn: Function, + obj: object, + prop: PropertyKey, + msg?: string +): Assertion<Function>; +function assertDoesNotDecrease( + fn: Function, + obj: () => void, + msg?: string +): Assertion<Function>; +function assertDoesNotDecrease( + fn: Function, + obj: object | (() => void), + propOrMsg?: PropertyKey | string, + msg?: string +): Assertion<Function> { if (arguments.length === 3 && typeof obj === 'function') { - msg = prop; - prop = null; + return Assertion.create( + fn, + propOrMsg as string, + assertDoesNotDecrease, + true + ).to.not.decrease(obj); + } else { + return Assertion.create( + fn, + msg, + assertDoesNotDecrease, + true + ).to.not.decrease( + obj as Record<PropertyKey, unknown>, + propOrMsg as PropertyKey + ); } +} - return new Assertion(fn, msg, assert.doesNotDecrease, true).to.not.decrease( - obj, - prop - ); -}; +assert.doesNotDecrease = assertDoesNotDecrease; /** * ### .doesNotDecreaseBy(function, object, property, delta, [message]) @@ -2917,20 +4076,50 @@ assert.doesNotDecrease = function (fn, obj, prop, msg) { * @namespace Assert * @public */ -assert.doesNotDecreaseBy = function (fn, obj, prop, delta, msg) { +function assertDoesNotDecreaseBy( + fn: Function, + obj: object, + prop: PropertyKey, + delta: number, + msg?: string +): Assertion<Function>; +function assertDoesNotDecreaseBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string +): Assertion<Function>; +function assertDoesNotDecreaseBy( + fn: Function, + obj: object | (() => void), + deltaOrProp: number | PropertyKey, + msgOrDelta?: string | number, + msg?: string +): Assertion<Function> { if (arguments.length === 4 && typeof obj === 'function') { - var tmpMsg = delta; - delta = prop; - msg = tmpMsg; + return Assertion.create( + fn, + msgOrDelta as string, + assertDoesNotDecreaseBy, + true + ) + .to.not.decrease(obj as Function) + .by(deltaOrProp as number); } else if (arguments.length === 3) { - delta = prop; - prop = null; + return Assertion.create(fn, msg, assertDoesNotDecreaseBy, true) + .to.not.decrease(obj as Function) + .by(deltaOrProp as number); + } else { + return Assertion.create(fn, msg, assertDoesNotDecreaseBy, true) + .to.not.decrease( + obj as Record<PropertyKey, unknown>, + deltaOrProp as PropertyKey + ) + .by(msgOrDelta as number); } +} - return new Assertion(fn, msg, assert.doesNotDecreaseBy, true).to.not - .decrease(obj, prop) - .by(delta); -}; +assert.doesNotDecreaseBy = assertDoesNotDecreaseBy; /** * ### .decreasesButNotBy(function, object, property, delta, [message]) @@ -2950,20 +4139,45 @@ assert.doesNotDecreaseBy = function (fn, obj, prop, delta, msg) { * @namespace Assert * @public */ -assert.decreasesButNotBy = function (fn, obj, prop, delta, msg) { +function assertDecreasesButNotBy( + fn: Function, + obj: object, + prop: PropertyKey, + delta: number, + msg?: string +): void; +function assertDecreasesButNotBy( + fn: Function, + obj: () => void, + delta: number, + msg?: string +): void; +function assertDecreasesButNotBy( + fn: Function, + obj: object | (() => void), + deltaOrProp: number | PropertyKey, + msgOrDelta?: string | number, + msg?: string +): void { if (arguments.length === 4 && typeof obj === 'function') { - var tmpMsg = delta; - delta = prop; - msg = tmpMsg; + Assertion.create(fn, msgOrDelta as string, assertDecreasesButNotBy, true) + .to.decrease(obj) + .but.not.by(deltaOrProp as number); } else if (arguments.length === 3) { - delta = prop; - prop = null; + Assertion.create(fn, msg, assertDecreasesButNotBy, true) + .to.decrease(obj as Function) + .but.not.by(deltaOrProp as number); + } else { + Assertion.create(fn, msg, assertDecreasesButNotBy, true) + .to.decrease( + obj as Record<PropertyKey, unknown>, + deltaOrProp as PropertyKey + ) + .but.not.by(msgOrDelta as number); } +} - new Assertion(fn, msg, assert.decreasesButNotBy, true).to - .decrease(obj, prop) - .but.not.by(delta); -}; +assert.decreasesButNotBy = assertDecreasesButNotBy; /** * ### .ifError(object) @@ -2980,7 +4194,7 @@ assert.decreasesButNotBy = function (fn, obj, prop, delta, msg) { * @namespace Assert * @public */ -assert.ifError = function (val) { +assert.ifError = function (val: unknown) { if (val) { throw val; } @@ -3000,8 +4214,8 @@ assert.ifError = function (val) { * @namespace Assert * @public */ -assert.isExtensible = function (obj, msg) { - new Assertion(obj, msg, assert.isExtensible, true).to.be.extensible; +assert.isExtensible = assert.extensible = function (obj: object, msg?: string) { + Assertion.create(obj, msg, assert.isExtensible, true).to.be.extensible; }; /** @@ -3024,8 +4238,11 @@ assert.isExtensible = function (obj, msg) { * @namespace Assert * @public */ -assert.isNotExtensible = function (obj, msg) { - new Assertion(obj, msg, assert.isNotExtensible, true).to.not.be.extensible; +assert.isNotExtensible = assert.notExtensible = function ( + obj: object, + msg?: string +) { + Assertion.create(obj, msg, assert.isNotExtensible, true).to.not.be.extensible; }; /** @@ -3047,8 +4264,8 @@ assert.isNotExtensible = function (obj, msg) { * @namespace Assert * @public */ -assert.isSealed = function (obj, msg) { - new Assertion(obj, msg, assert.isSealed, true).to.be.sealed; +assert.isSealed = assert.sealed = function (obj: object, msg?: string) { + Assertion.create(obj, msg, assert.isSealed, true).to.be.sealed; }; /** @@ -3065,8 +4282,8 @@ assert.isSealed = function (obj, msg) { * @namespace Assert * @public */ -assert.isNotSealed = function (obj, msg) { - new Assertion(obj, msg, assert.isNotSealed, true).to.not.be.sealed; +assert.isNotSealed = assert.notSealed = function (obj: object, msg?: string) { + Assertion.create(obj, msg, assert.isNotSealed, true).to.not.be.sealed; }; /** @@ -3085,8 +4302,8 @@ assert.isNotSealed = function (obj, msg) { * @namespace Assert * @public */ -assert.isFrozen = function (obj, msg) { - new Assertion(obj, msg, assert.isFrozen, true).to.be.frozen; +assert.isFrozen = assert.frozen = function (obj: object, msg?: string) { + Assertion.create(obj, msg, assert.isFrozen, true).to.be.frozen; }; /** @@ -3103,8 +4320,8 @@ assert.isFrozen = function (obj, msg) { * @namespace Assert * @public */ -assert.isNotFrozen = function (obj, msg) { - new Assertion(obj, msg, assert.isNotFrozen, true).to.not.be.frozen; +assert.isNotFrozen = assert.notFrozen = function (obj: object, msg?: string) { + Assertion.create(obj, msg, assert.isNotFrozen, true).to.not.be.frozen; }; /** @@ -3128,8 +4345,8 @@ assert.isNotFrozen = function (obj, msg) { * @namespace Assert * @public */ -assert.isEmpty = function (val, msg) { - new Assertion(val, msg, assert.isEmpty, true).to.be.empty; +assert.isEmpty = assert.empty = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isEmpty, true).to.be.empty; }; /** @@ -3153,33 +4370,6 @@ assert.isEmpty = function (val, msg) { * @namespace Assert * @public */ -assert.isNotEmpty = function (val, msg) { - new Assertion(val, msg, assert.isNotEmpty, true).to.not.be.empty; +assert.isNotEmpty = assert.notEmpty = function (val: unknown, msg?: string) { + Assertion.create(val, msg, assert.isNotEmpty, true).to.not.be.empty; }; - -/** - * Aliases. - * - * @param {unknown} name - * @param {unknown} as - * @returns {unknown} - */ -const aliases = [ - ['isOk', 'ok'], - ['isNotOk', 'notOk'], - ['throws', 'throw'], - ['throws', 'Throw'], - ['isExtensible', 'extensible'], - ['isNotExtensible', 'notExtensible'], - ['isSealed', 'sealed'], - ['isNotSealed', 'notSealed'], - ['isFrozen', 'frozen'], - ['isNotFrozen', 'notFrozen'], - ['isEmpty', 'empty'], - ['isNotEmpty', 'notEmpty'], - ['isCallable', 'isFunction'], - ['isNotCallable', 'isNotFunction'] -]; -for (const [name, as] of aliases) { - assert[as] = assert[name]; -} diff --git a/lib/chai/interface/expect.js b/src/chai/interface/expect.ts similarity index 50% rename from lib/chai/interface/expect.js rename to src/chai/interface/expect.ts index b7c76930..ef15f195 100644 --- a/lib/chai/interface/expect.js +++ b/src/chai/interface/expect.ts @@ -4,19 +4,23 @@ * MIT Licensed */ -import * as chai from '../../../index.js'; +import * as chai from '../../chai.js'; import {Assertion} from '../assertion.js'; import {AssertionError} from 'assertion-error'; -/** - * @param {unknown} val - * @param {string} message - * @returns {Assertion} - */ -function expect(val, message) { - return new Assertion(val, message); +export interface ExpectInterface { + <T>(val: T, message?: string): Assertion<T>; + fail(message?: string): void; + fail<T>(actual: T, expected: T, message: string, operator: string): void; } +const expect: ExpectInterface = function expect<T>( + val: T, + message?: string +): Assertion<T> { + return Assertion.create(val, message); +} as ExpectInterface; + export {expect}; /** @@ -40,15 +44,33 @@ export {expect}; * @namespace expect * @public */ -expect.fail = function (actual, expected, message, operator) { +function expectFail(message?: string): void; +function expectFail( + actual: unknown, + expected: unknown, + message?: string, + operator?: string +): void; +function expectFail( + actualOrMessage?: unknown, + expected?: unknown, + message?: string, + operator?: string +): void { + let msg: string | undefined; + let actual; + if (arguments.length < 2) { - message = actual; + msg = actualOrMessage as string | undefined; actual = undefined; + } else { + msg = message; + actual = actualOrMessage; } - message = message || 'expect.fail()'; + msg = msg || 'expect.fail()'; throw new AssertionError( - message, + msg, { actual: actual, expected: expected, @@ -56,4 +78,6 @@ expect.fail = function (actual, expected, message, operator) { }, chai.expect.fail ); -}; +} + +expect.fail = expectFail; diff --git a/lib/chai/interface/should.js b/src/chai/interface/should.ts similarity index 59% rename from lib/chai/interface/should.js rename to src/chai/interface/should.ts index 2500fda5..f3d77a92 100644 --- a/lib/chai/interface/should.js +++ b/src/chai/interface/should.ts @@ -6,16 +6,51 @@ import {Assertion} from '../assertion.js'; import {AssertionError} from 'assertion-error'; +import {Constructor} from '../utils/types.js'; + +export interface ShouldAssertions { + fail(message?: string): void; + fail<T>(actual: T, expected: T, message: string, operator: string): void; + equal<T>(val1: T, val2: T, msg: string): void; + Throw(fn: Function, errs: RegExp | string): void; + Throw( + fn: Function, + errt: Error | Constructor<Error>, + errs: RegExp, + msg?: string + ): void; + throw(fn: Function, errs: RegExp | string): void; + throw( + fn: Function, + errt: Error | Constructor<Error>, + errs: RegExp, + msg?: string + ): void; + exist(val: unknown, msg: string): void; +} + +export interface ShouldInterface extends ShouldAssertions { + not: ShouldAssertions; +} + +declare global { + interface Object { + // TODO (43081j): can this ever be strongly typed somehow? + should: Assertion<unknown>; + } +} /** - * @returns {void} + * Loads the `should` interface + * + * @returns {ShouldInterface} */ -function loadShould() { +function loadShould(): ShouldInterface { // explicitly define this method as function as to have it's name to include as `ssfi` /** * @returns {Assertion} */ - function shouldGetter() { + function shouldGetter(this: unknown) { if ( this instanceof String || this instanceof Number || @@ -23,14 +58,14 @@ function loadShould() { (typeof Symbol === 'function' && this instanceof Symbol) || (typeof BigInt === 'function' && this instanceof BigInt) ) { - return new Assertion(this.valueOf(), null, shouldGetter); + return Assertion.create(this.valueOf(), null, shouldGetter); } - return new Assertion(this, null, shouldGetter); + return Assertion.create(this, null, shouldGetter); } /** * @param {unknown} value */ - function shouldSetter(value) { + function shouldSetter(this: unknown, value: unknown) { // See https://github.com/chaijs/chai/issues/86: this makes // `whatever.should = someValue` actually set `someValue`, which is // especially useful for `global.should = require('chai').should()`. @@ -51,8 +86,6 @@ function loadShould() { configurable: true }); - var should = {}; - /** * ### .fail([message]) * ### .fail(actual, expected, [message], [operator]) @@ -74,21 +107,33 @@ function loadShould() { * @namespace BDD * @public */ - should.fail = function (actual, expected, message, operator) { + const shouldFail: ShouldAssertions['fail'] = function shouldFail( + actualOrMessage?: unknown, + expected?: unknown, + message?: string, + operator?: string + ): void { + let actual; + let msg: string | undefined; + if (arguments.length < 2) { - message = actual; + msg = actualOrMessage as string | undefined; actual = undefined; + } else { + msg = message; + actual = actualOrMessage; } - message = message || 'should.fail()'; + msg = msg || 'should.fail()'; + throw new AssertionError( - message, + msg, { actual: actual, expected: expected, operator: operator }, - should.fail + shouldFail ); }; @@ -106,8 +151,12 @@ function loadShould() { * @namespace Should * @public */ - should.equal = function (actual, expected, message) { - new Assertion(actual, message).to.equal(expected); + const shouldEqual: ShouldAssertions['equal'] = function shouldEqual( + val1: unknown, + val2: unknown, + msg: string + ) { + Assertion.create(val1, msg).to.equal(val2); }; /** @@ -133,8 +182,17 @@ function loadShould() { * @namespace Should * @public */ - should.Throw = function (fn, errt, errs, msg) { - new Assertion(fn, msg).to.Throw(errt, errs); + const shouldThrow: ShouldAssertions['throw'] = function shouldThrow( + fn: Function, + errt: Error | Constructor<Error> | RegExp | string, + errs?: RegExp | string, + msg?: string + ) { + if (errt instanceof RegExp || typeof errt === 'string') { + Assertion.create(fn, msg).to.Throw(errt); + } else { + Assertion.create(fn, msg).to.Throw(errt, errs as RegExp | string); + } }; /** @@ -151,13 +209,13 @@ function loadShould() { * @namespace Should * @public */ - should.exist = function (val, msg) { - new Assertion(val, msg).to.exist; + const shouldExist: ShouldAssertions['exist'] = function shouldExist( + val: unknown, + msg: string + ) { + Assertion.create(val, msg).to.exist; }; - // negation - should.not = {}; - /** * ### .not.equal(actual, expected, [message]) * @@ -172,8 +230,12 @@ function loadShould() { * @namespace Should * @public */ - should.not.equal = function (actual, expected, msg) { - new Assertion(actual, msg).to.not.equal(expected); + const shouldNotEqual: ShouldAssertions['equal'] = function shouldNotEqual( + val1: unknown, + val2: unknown, + msg: string + ) { + Assertion.create(val1, msg).to.not.equal(val2); }; /** @@ -195,8 +257,17 @@ function loadShould() { * @namespace Should * @public */ - should.not.Throw = function (fn, errt, errs, msg) { - new Assertion(fn, msg).to.not.Throw(errt, errs); + const shouldNotThrow: ShouldAssertions['throw'] = function shouldNotThrow( + fn: Function, + errt: Error | Constructor<Error> | RegExp | string, + errs?: RegExp | string, + msg?: string + ) { + if (errt instanceof RegExp || typeof errt === 'string') { + Assertion.create(fn, msg).to.not.Throw(errt); + } else { + Assertion.create(fn, msg).to.not.Throw(errt, errs as RegExp | string); + } }; /** @@ -213,14 +284,27 @@ function loadShould() { * @param {string} msg * @public */ - should.not.exist = function (val, msg) { - new Assertion(val, msg).to.not.exist; + const shouldNotExist: ShouldAssertions['exist'] = function shouldNotExist( + val: unknown, + msg: string + ) { + Assertion.create(val, msg).to.not.exist; }; - should['throw'] = should['Throw']; - should.not['throw'] = should.not['Throw']; - - return should; + return { + equal: shouldEqual, + exist: shouldExist, + fail: shouldFail, + throw: shouldThrow, + Throw: shouldThrow, + not: { + equal: shouldNotEqual, + exist: shouldNotExist, + fail: () => {}, // Nonsensical, so make it noop + throw: shouldNotThrow, + Throw: shouldNotThrow + } + }; } export const should = loadShould; diff --git a/lib/chai/utils/addChainableMethod.js b/src/chai/utils/addChainableMethod.ts similarity index 83% rename from lib/chai/utils/addChainableMethod.js rename to src/chai/utils/addChainableMethod.ts index 1a000c6b..ce51fe91 100644 --- a/lib/chai/utils/addChainableMethod.js +++ b/src/chai/utils/addChainableMethod.ts @@ -4,11 +4,11 @@ * MIT Licensed */ -import {Assertion} from '../assertion.js'; import {addLengthGuard} from './addLengthGuard.js'; import {flag} from './flag.js'; import {proxify} from './proxify.js'; import {transferFlags} from './transferFlags.js'; +import {ChainableBehavior} from './chainableBehavior.js'; /** * Module variables @@ -60,31 +60,42 @@ var call = Function.prototype.call, * @param {string} name of method to add * @param {Function} method function to be used for `name`, when called * @param {Function} chainingBehavior function to be called every time the property is accessed + * @param {Function=} createDefaultValue * @namespace Utils * @name addChainableMethod * @public */ -export function addChainableMethod(ctx, name, method, chainingBehavior) { +export function addChainableMethod<T extends object>( + ctx: T, + name: string, + method: (...args: never) => unknown, + chainingBehavior?: () => void, + createDefaultValue?: (ctx: T) => unknown +) { + const ctxChainable = ctx as { + __methods: Record<PropertyKey, ChainableBehavior>; + }; + if (typeof chainingBehavior !== 'function') { chainingBehavior = function () {}; } - var chainableBehavior = { + var chainableBehavior: ChainableBehavior = { method: method, chainingBehavior: chainingBehavior }; // save the methods so we can overwrite them later, if we need to. - if (!ctx.__methods) { - ctx.__methods = {}; + if (!ctxChainable.__methods) { + ctxChainable.__methods = {}; } - ctx.__methods[name] = chainableBehavior; + ctxChainable.__methods[name] = chainableBehavior; Object.defineProperty(ctx, name, { get: function chainableMethodGetter() { chainableBehavior.chainingBehavior.call(this); - var chainableMethodWrapper = function () { + var chainableMethodWrapper = function (this: T) { // Setting the `ssfi` flag to `chainableMethodWrapper` causes this // function to be the starting point for removing implementation // frames from the stack trace of a failed assertion. @@ -104,14 +115,18 @@ export function addChainableMethod(ctx, name, method, chainingBehavior) { flag(this, 'ssfi', chainableMethodWrapper); } - var result = chainableBehavior.method.apply(this, arguments); + var result = ( + chainableBehavior.method as (...args: unknown[]) => unknown + ).call(this, ...arguments); if (result !== undefined) { return result; } - var newAssertion = new Assertion(); - transferFlags(this, newAssertion); - return newAssertion; + if (createDefaultValue) { + return createDefaultValue(this); + } + + return undefined; }; addLengthGuard(chainableMethodWrapper, name, true); @@ -134,7 +149,9 @@ export function addChainableMethod(ctx, name, method, chainingBehavior) { } var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); - Object.defineProperty(chainableMethodWrapper, asserterName, pd); + if (pd) { + Object.defineProperty(chainableMethodWrapper, asserterName, pd); + } }); } diff --git a/lib/chai/utils/addLengthGuard.js b/src/chai/utils/addLengthGuard.ts similarity index 86% rename from lib/chai/utils/addLengthGuard.js rename to src/chai/utils/addLengthGuard.ts index d08aad61..71f19cbc 100644 --- a/lib/chai/utils/addLengthGuard.js +++ b/src/chai/utils/addLengthGuard.ts @@ -40,30 +40,35 @@ const fnLengthDesc = Object.getOwnPropertyDescriptor(function () {}, 'length'); * @namespace Utils * @name addLengthGuard */ -export function addLengthGuard(fn, assertionName, isChainable) { - if (!fnLengthDesc.configurable) return fn; +export function addLengthGuard( + fn: Function, + assertionName: PropertyKey, + isChainable: boolean +) { + if (!fnLengthDesc?.configurable) return fn; + const assertionNameStr = String(assertionName); Object.defineProperty(fn, 'length', { get: function () { if (isChainable) { throw Error( 'Invalid Chai property: ' + - assertionName + + assertionNameStr + '.length. Due' + ' to a compatibility issue, "length" cannot directly follow "' + - assertionName + + assertionNameStr + '". Use "' + - assertionName + + assertionNameStr + '.lengthOf" instead.' ); } throw Error( 'Invalid Chai property: ' + - assertionName + + assertionNameStr + '.length. See' + ' docs for proper usage of "' + - assertionName + + assertionNameStr + '".' ); } diff --git a/lib/chai/utils/addMethod.js b/src/chai/utils/addMethod.ts similarity index 82% rename from lib/chai/utils/addMethod.js rename to src/chai/utils/addMethod.ts index de5b04fc..52ab546b 100644 --- a/lib/chai/utils/addMethod.js +++ b/src/chai/utils/addMethod.ts @@ -7,8 +7,6 @@ import {addLengthGuard} from './addLengthGuard.js'; import {flag} from './flag.js'; import {proxify} from './proxify.js'; -import {transferFlags} from './transferFlags.js'; -import {Assertion} from '../assertion.js'; /** * ### .addMethod(ctx, name, method) @@ -31,12 +29,18 @@ import {Assertion} from '../assertion.js'; * @param {object} ctx object to which the method is added * @param {string} name of method to add * @param {Function} method function to be used for name + * @param {Function=} createDefaultValue * @namespace Utils * @name addMethod * @public */ -export function addMethod(ctx, name, method) { - var methodWrapper = function () { +export function addMethod<T extends object>( + ctx: T, + name: PropertyKey, + method: Function, + createDefaultValue?: (ctx: T) => unknown +) { + var methodWrapper = function (this: T) { // Setting the `ssfi` flag to `methodWrapper` causes this function to be the // starting point for removing implementation frames from the stack trace of // a failed assertion. @@ -56,11 +60,13 @@ export function addMethod(ctx, name, method) { var result = method.apply(this, arguments); if (result !== undefined) return result; - var newAssertion = new Assertion(); - transferFlags(this, newAssertion); - return newAssertion; + if (createDefaultValue) { + return createDefaultValue(this); + } + + return undefined; }; addLengthGuard(methodWrapper, name, false); - ctx[name] = proxify(methodWrapper, name); + (ctx as Record<PropertyKey, unknown>)[name] = proxify(methodWrapper, name); } diff --git a/lib/chai/utils/addProperty.js b/src/chai/utils/addProperty.ts similarity index 84% rename from lib/chai/utils/addProperty.js rename to src/chai/utils/addProperty.ts index 4375d61a..55d9bd16 100644 --- a/lib/chai/utils/addProperty.js +++ b/src/chai/utils/addProperty.ts @@ -4,10 +4,8 @@ * MIT Licensed */ -import {Assertion} from '../assertion.js'; import {flag} from './flag.js'; import {isProxyEnabled} from './isProxyEnabled.js'; -import {transferFlags} from './transferFlags.js'; /** * ### .addProperty(ctx, name, getter) @@ -30,12 +28,18 @@ import {transferFlags} from './transferFlags.js'; * @param {object} ctx object to which the property is added * @param {string} name of property to add * @param {Function} getter function to be used for name + * @param {Function=} getDefaultValue * @namespace Utils * @name addProperty * @public */ -export function addProperty(ctx, name, getter) { - getter = getter === undefined ? function () {} : getter; +export function addProperty<T extends object>( + ctx: T, + name: PropertyKey, + getter?: Function, + getDefaultValue?: (ctx: T) => unknown +) { + const getterFn = getter === undefined ? function () {} : getter; Object.defineProperty(ctx, name, { get: function propertyGetter() { @@ -58,12 +62,14 @@ export function addProperty(ctx, name, getter) { flag(this, 'ssfi', propertyGetter); } - var result = getter.call(this); + var result = getterFn.call(this); if (result !== undefined) return result; - var newAssertion = new Assertion(); - transferFlags(this, newAssertion); - return newAssertion; + if (getDefaultValue) { + return getDefaultValue(this); + } + + return undefined; }, configurable: true }); diff --git a/src/chai/utils/chainableBehavior.ts b/src/chai/utils/chainableBehavior.ts new file mode 100644 index 00000000..95835d88 --- /dev/null +++ b/src/chai/utils/chainableBehavior.ts @@ -0,0 +1,4 @@ +export interface ChainableBehavior { + chainingBehavior: () => void; + method: (...args: never) => unknown; +} diff --git a/lib/chai/utils/compareByInspect.js b/src/chai/utils/compareByInspect.ts similarity index 92% rename from lib/chai/utils/compareByInspect.js rename to src/chai/utils/compareByInspect.ts index 5ab27a20..f04728c7 100644 --- a/lib/chai/utils/compareByInspect.js +++ b/src/chai/utils/compareByInspect.ts @@ -21,6 +21,6 @@ import {inspect} from './inspect.js'; * @namespace Utils * @public */ -export function compareByInspect(a, b) { +export function compareByInspect(a: unknown, b: unknown) { return inspect(a) < inspect(b) ? -1 : 1; } diff --git a/lib/chai/utils/expectTypes.js b/src/chai/utils/expectTypes.ts similarity index 90% rename from lib/chai/utils/expectTypes.js rename to src/chai/utils/expectTypes.ts index ca2e1601..fef8f001 100644 --- a/lib/chai/utils/expectTypes.js +++ b/src/chai/utils/expectTypes.ts @@ -21,13 +21,13 @@ import {type} from './type-detect.js'; * @name expectTypes * @public */ -export function expectTypes(obj, types) { +export function expectTypes(obj: object, types: string[]) { var flagMsg = flag(obj, 'message'); - var ssfi = flag(obj, 'ssfi'); + var ssfi = flag(obj, 'ssfi') as Function; flagMsg = flagMsg ? flagMsg + ': ' : ''; - obj = flag(obj, 'object'); + obj = flag(obj, 'object') as object; types = types.map(function (t) { return t.toLowerCase(); }); diff --git a/src/chai/utils/flag.ts b/src/chai/utils/flag.ts new file mode 100644 index 00000000..c7cfd07a --- /dev/null +++ b/src/chai/utils/flag.ts @@ -0,0 +1,48 @@ +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> + * MIT Licensed + */ + +/** + * ### .flag(object, key, [value]) + * + * Get or set a flag value on an object. If a + * value is provided it will be set, else it will + * return the currently set value or `undefined` if + * the value is not set. + * + * utils.flag(this, 'foo', 'bar'); // setter + * utils.flag(this, 'foo'); // getter, returns `bar` + * + * @param {object} obj constructed Assertion + * @param {string} key + * @param {unknown} value (optional) + * @returns {unknown | undefined} + * @namespace Utils + * @name flag + * @private + */ +function flag<T extends {__flags: unknown}, TKey extends keyof T['__flags']>( + obj: T, + key: TKey +): T['__flags'][TKey]; +function flag<T extends {__flags: unknown}, TKey extends keyof T['__flags']>( + obj: T, + key: TKey, + value: T['__flags'][TKey] +): void; +function flag<T extends object>(obj: T, key: PropertyKey): unknown; +function flag<T extends object>(obj: T, key: PropertyKey, value: unknown): void; +function flag(obj: object, key: PropertyKey, value?: unknown): unknown { + const objWithFlags = obj as {__flags?: Record<PropertyKey, unknown>}; + const flags = + objWithFlags.__flags || (objWithFlags.__flags = Object.create(null)); + if (arguments.length === 3) { + (flags as Record<PropertyKey, unknown>)[key] = value; + } else { + return (flags as Record<PropertyKey, unknown>)[key]; + } +} + +export {flag}; diff --git a/lib/chai/utils/getActual.js b/src/chai/utils/getActual.ts similarity index 75% rename from lib/chai/utils/getActual.js rename to src/chai/utils/getActual.ts index 1b4b3aa2..66a4fcac 100644 --- a/lib/chai/utils/getActual.js +++ b/src/chai/utils/getActual.ts @@ -15,6 +15,6 @@ * @namespace Utils * @name getActual */ -export function getActual(obj, args) { - return args.length > 4 ? args[4] : obj._obj; +export function getActual(obj: object, args: IArguments) { + return args.length > 4 ? args[4] : (obj as {_obj: unknown})._obj; } diff --git a/lib/chai/utils/getMessage.js b/src/chai/utils/getMessage.ts similarity index 95% rename from lib/chai/utils/getMessage.js rename to src/chai/utils/getMessage.ts index faa841f2..81e3c077 100644 --- a/lib/chai/utils/getMessage.js +++ b/src/chai/utils/getMessage.ts @@ -27,7 +27,7 @@ import {objDisplay} from './objDisplay.js'; * @name getMessage * @public */ -export function getMessage(obj, args) { +export function getMessage(obj: object, args: IArguments): string { var negate = flag(obj, 'negate'), val = flag(obj, 'object'), expected = args[3], diff --git a/lib/chai/utils/getOperator.js b/src/chai/utils/getOperator.ts similarity index 92% rename from lib/chai/utils/getOperator.js rename to src/chai/utils/getOperator.ts index 7a57d786..f4cab223 100644 --- a/lib/chai/utils/getOperator.js +++ b/src/chai/utils/getOperator.ts @@ -5,7 +5,7 @@ import {type} from './type-detect.js'; * @param {unknown} obj * @returns {boolean} */ -function isObjectType(obj) { +function isObjectType(obj: object) { var objectType = type(obj); var objectTypes = ['Array', 'Object', 'Function']; @@ -28,7 +28,7 @@ function isObjectType(obj) { * @name getOperator * @public */ -export function getOperator(obj, args) { +export function getOperator(obj: object, args: IArguments) { var operator = flag(obj, 'operator'); var negate = flag(obj, 'negate'); var expected = args[3]; diff --git a/lib/chai/utils/getOwnEnumerableProperties.js b/src/chai/utils/getOwnEnumerableProperties.ts similarity index 82% rename from lib/chai/utils/getOwnEnumerableProperties.js rename to src/chai/utils/getOwnEnumerableProperties.ts index 9e8e830b..b515f62a 100644 --- a/lib/chai/utils/getOwnEnumerableProperties.js +++ b/src/chai/utils/getOwnEnumerableProperties.ts @@ -19,6 +19,6 @@ import {getOwnEnumerablePropertySymbols} from './getOwnEnumerablePropertySymbols * @name getOwnEnumerableProperties * @public */ -export function getOwnEnumerableProperties(obj) { - return Object.keys(obj).concat(getOwnEnumerablePropertySymbols(obj)); +export function getOwnEnumerableProperties(obj: object) { + return [...Object.keys(obj), ...getOwnEnumerablePropertySymbols(obj)]; } diff --git a/lib/chai/utils/getOwnEnumerablePropertySymbols.js b/src/chai/utils/getOwnEnumerablePropertySymbols.ts similarity index 84% rename from lib/chai/utils/getOwnEnumerablePropertySymbols.js rename to src/chai/utils/getOwnEnumerablePropertySymbols.ts index d8d6d096..5b4c3019 100644 --- a/lib/chai/utils/getOwnEnumerablePropertySymbols.js +++ b/src/chai/utils/getOwnEnumerablePropertySymbols.ts @@ -17,10 +17,10 @@ * @name getOwnEnumerablePropertySymbols * @public */ -export function getOwnEnumerablePropertySymbols(obj) { +export function getOwnEnumerablePropertySymbols(obj: object) { if (typeof Object.getOwnPropertySymbols !== 'function') return []; return Object.getOwnPropertySymbols(obj).filter(function (sym) { - return Object.getOwnPropertyDescriptor(obj, sym).enumerable; + return Object.getOwnPropertyDescriptor(obj, sym)?.enumerable; }); } diff --git a/lib/chai/utils/getProperties.js b/src/chai/utils/getProperties.ts similarity index 85% rename from lib/chai/utils/getProperties.js rename to src/chai/utils/getProperties.ts index b43c7104..37901b0b 100644 --- a/lib/chai/utils/getProperties.js +++ b/src/chai/utils/getProperties.ts @@ -16,13 +16,13 @@ * @name getProperties * @public */ -export function getProperties(object) { +export function getProperties(object: object) { var result = Object.getOwnPropertyNames(object); /** - * @param {unknown} property + * @param {string} property */ - function addProperty(property) { + function addProperty(property: string) { if (result.indexOf(property) === -1) { result.push(property); } diff --git a/lib/chai/utils/index.js b/src/chai/utils/index.ts similarity index 94% rename from lib/chai/utils/index.js rename to src/chai/utils/index.ts index 70d9f4c1..d6ae8860 100644 --- a/lib/chai/utils/index.js +++ b/src/chai/utils/index.ts @@ -47,7 +47,7 @@ export {getPathInfo, hasProperty} from 'pathval'; * @param {Function} fn * @returns {string} */ -export function getName(fn) { +export function getName(fn: Function) { return fn.name; } @@ -103,7 +103,7 @@ export {getOperator} from './getOperator.js'; * @param {*} obj Object to test * @returns {boolean} */ -export function isRegExp(obj) { +export function isRegExp(obj: unknown): obj is RegExp { return Object.prototype.toString.call(obj) === '[object RegExp]'; } @@ -113,6 +113,6 @@ export function isRegExp(obj) { * @param {unknown} obj Object to test * @returns {boolean} */ -export function isNumeric(obj) { +export function isNumeric(obj: unknown): obj is number | bigint { return ['Number', 'BigInt'].includes(type(obj)); } diff --git a/lib/chai/utils/inspect.js b/src/chai/utils/inspect.ts similarity index 90% rename from lib/chai/utils/inspect.js rename to src/chai/utils/inspect.ts index f27bf341..67bdade9 100644 --- a/lib/chai/utils/inspect.js +++ b/src/chai/utils/inspect.ts @@ -20,7 +20,12 @@ import {config} from '../config.js'; * @namespace Utils * @name inspect */ -export function inspect(obj, showHidden, depth, colors) { +export function inspect( + obj: unknown, + showHidden?: boolean, + depth?: number, + colors?: boolean +) { var options = { colors: colors, depth: typeof depth === 'undefined' ? 2 : depth, diff --git a/lib/chai/utils/isNaN.js b/src/chai/utils/isNaN.ts similarity index 94% rename from lib/chai/utils/isNaN.js rename to src/chai/utils/isNaN.ts index acc10d6f..d7f0a761 100644 --- a/lib/chai/utils/isNaN.js +++ b/src/chai/utils/isNaN.ts @@ -16,7 +16,7 @@ * @name isNaN * @private */ -function _isNaN(value) { +function _isNaN(value: unknown) { // Refer http://www.ecma-international.org/ecma-262/6.0/#sec-isnan-number // section's NOTE. return value !== value; diff --git a/lib/chai/utils/isProxyEnabled.js b/src/chai/utils/isProxyEnabled.ts similarity index 100% rename from lib/chai/utils/isProxyEnabled.js rename to src/chai/utils/isProxyEnabled.ts diff --git a/lib/chai/utils/objDisplay.js b/src/chai/utils/objDisplay.ts similarity index 77% rename from lib/chai/utils/objDisplay.js rename to src/chai/utils/objDisplay.ts index cf58d5da..52495fc0 100644 --- a/lib/chai/utils/objDisplay.js +++ b/src/chai/utils/objDisplay.ts @@ -20,19 +20,19 @@ import {config} from '../config.js'; * @namespace Utils * @public */ -export function objDisplay(obj) { +export function objDisplay(obj: unknown) { var str = inspect(obj), type = Object.prototype.toString.call(obj); if (config.truncateThreshold && str.length >= config.truncateThreshold) { if (type === '[object Function]') { - return !obj.name || obj.name === '' + return !(obj as Function).name || (obj as Function).name === '' ? '[Function]' - : '[Function: ' + obj.name + ']'; + : '[Function: ' + (obj as Function).name + ']'; } else if (type === '[object Array]') { - return '[ Array(' + obj.length + ') ]'; + return '[ Array(' + (obj as unknown[]).length + ') ]'; } else if (type === '[object Object]') { - var keys = Object.keys(obj), + var keys = Object.keys(obj as Record<PropertyKey, unknown>), kstr = keys.length > 2 ? keys.splice(0, 2).join(', ') + ', ...' diff --git a/lib/chai/utils/overwriteChainableMethod.js b/src/chai/utils/overwriteChainableMethod.ts similarity index 70% rename from lib/chai/utils/overwriteChainableMethod.js rename to src/chai/utils/overwriteChainableMethod.ts index 27fce9f4..b80062c8 100644 --- a/lib/chai/utils/overwriteChainableMethod.js +++ b/src/chai/utils/overwriteChainableMethod.ts @@ -4,8 +4,7 @@ * MIT Licensed */ -import {Assertion} from '../assertion.js'; -import {transferFlags} from './transferFlags.js'; +import {ChainableBehavior} from './chainableBehavior.js'; /** * ### .overwriteChainableMethod(ctx, name, method, chainingBehavior) @@ -35,35 +34,50 @@ import {transferFlags} from './transferFlags.js'; * @param {string} name of method / property to overwrite * @param {Function} method function that returns a function to be used for name * @param {Function} chainingBehavior function that returns a function to be used for property + * @param {Function=} createDefaultValue * @namespace Utils * @name overwriteChainableMethod * @public */ -export function overwriteChainableMethod(ctx, name, method, chainingBehavior) { - var chainableBehavior = ctx.__methods[name]; +export function overwriteChainableMethod<T extends object>( + ctx: T, + name: string, + method: Function, + chainingBehavior: Function, + createDefaultValue?: (ctx: T) => unknown +) { + var chainableBehavior = ( + ctx as {__methods: Record<PropertyKey, ChainableBehavior>} + ).__methods[name]; var _chainingBehavior = chainableBehavior.chainingBehavior; chainableBehavior.chainingBehavior = - function overwritingChainableMethodGetter() { + function overwritingChainableMethodGetter(this: T) { var result = chainingBehavior(_chainingBehavior).call(this); if (result !== undefined) { return result; } - var newAssertion = new Assertion(); - transferFlags(this, newAssertion); - return newAssertion; + if (createDefaultValue) { + return createDefaultValue(this); + } + + return undefined; }; var _method = chainableBehavior.method; - chainableBehavior.method = function overwritingChainableMethodWrapper() { + chainableBehavior.method = function overwritingChainableMethodWrapper( + this: T + ) { var result = method(_method).apply(this, arguments); if (result !== undefined) { return result; } - var newAssertion = new Assertion(); - transferFlags(this, newAssertion); - return newAssertion; + if (createDefaultValue) { + return createDefaultValue(this); + } + + return undefined; }; } diff --git a/lib/chai/utils/overwriteMethod.js b/src/chai/utils/overwriteMethod.ts similarity index 82% rename from lib/chai/utils/overwriteMethod.js rename to src/chai/utils/overwriteMethod.ts index 0fbeb3be..09607606 100644 --- a/lib/chai/utils/overwriteMethod.js +++ b/src/chai/utils/overwriteMethod.ts @@ -4,11 +4,9 @@ * MIT Licensed */ -import {Assertion} from '../assertion.js'; import {addLengthGuard} from './addLengthGuard.js'; import {flag} from './flag.js'; import {proxify} from './proxify.js'; -import {transferFlags} from './transferFlags.js'; /** * ### .overwriteMethod(ctx, name, fn) @@ -43,15 +41,20 @@ import {transferFlags} from './transferFlags.js'; * @name overwriteMethod * @public */ -export function overwriteMethod(ctx, name, method) { - var _method = ctx[name], - _super = function () { - throw new Error(name + ' is not a function'); +export function overwriteMethod<T extends object>( + ctx: T, + name: PropertyKey, + method: Function, + createDefaultValue?: (ctx: T) => unknown +) { + var _method = (ctx as Record<PropertyKey, unknown>)[name], + _super: Function = function () { + throw new Error(String(name) + ' is not a function'); }; if (_method && 'function' === typeof _method) _super = _method; - var overwritingMethodWrapper = function () { + var overwritingMethodWrapper = function (this: T) { // Setting the `ssfi` flag to `overwritingMethodWrapper` causes this // function to be the starting point for removing implementation frames from // the stack trace of a failed assertion. @@ -80,11 +83,16 @@ export function overwriteMethod(ctx, name, method) { return result; } - var newAssertion = new Assertion(); - transferFlags(this, newAssertion); - return newAssertion; + if (createDefaultValue) { + return createDefaultValue(this); + } + + return undefined; }; addLengthGuard(overwritingMethodWrapper, name, false); - ctx[name] = proxify(overwritingMethodWrapper, name); + (ctx as Record<PropertyKey, unknown>)[name] = proxify( + overwritingMethodWrapper, + name + ); } diff --git a/lib/chai/utils/overwriteProperty.js b/src/chai/utils/overwriteProperty.ts similarity index 90% rename from lib/chai/utils/overwriteProperty.js rename to src/chai/utils/overwriteProperty.ts index d1253093..9a522384 100644 --- a/lib/chai/utils/overwriteProperty.js +++ b/src/chai/utils/overwriteProperty.ts @@ -4,10 +4,8 @@ * MIT Licensed */ -import {Assertion} from '../assertion.js'; import {flag} from './flag.js'; import {isProxyEnabled} from './isProxyEnabled.js'; -import {transferFlags} from './transferFlags.js'; /** * ### .overwriteProperty(ctx, name, fn) @@ -37,11 +35,17 @@ import {transferFlags} from './transferFlags.js'; * @param {object} ctx object whose property is to be overwritten * @param {string} name of property to overwrite * @param {Function} getter function that returns a getter function to be used for name + * @param {Function=} createDefaultValue * @namespace Utils * @name overwriteProperty * @public */ -export function overwriteProperty(ctx, name, getter) { +export function overwriteProperty<T extends object>( + ctx: T, + name: PropertyKey, + getter: Function, + createDefaultValue?: (ctx: T) => unknown +) { var _get = Object.getOwnPropertyDescriptor(ctx, name), _super = function () {}; @@ -80,9 +84,11 @@ export function overwriteProperty(ctx, name, getter) { return result; } - var newAssertion = new Assertion(); - transferFlags(this, newAssertion); - return newAssertion; + if (createDefaultValue) { + return createDefaultValue(this); + } + + return undefined; }, configurable: true }); diff --git a/lib/chai/utils/proxify.js b/src/chai/utils/proxify.ts similarity index 92% rename from lib/chai/utils/proxify.js rename to src/chai/utils/proxify.ts index 4c1cf695..b92d7602 100644 --- a/lib/chai/utils/proxify.js +++ b/src/chai/utils/proxify.ts @@ -9,7 +9,7 @@ import {isProxyEnabled} from './isProxyEnabled.js'; * MIT Licensed */ -const builtins = ['__flags', '__methods', '_obj', 'assert']; +const builtins: PropertyKey[] = ['__flags', '__methods', '_obj', 'assert']; /** * ### .proxify(object) @@ -30,7 +30,10 @@ const builtins = ['__flags', '__methods', '_obj', 'assert']; * @namespace Utils * @name proxify */ -export function proxify(obj, nonChainableMethodName) { +export function proxify<T extends object>( + obj: T, + nonChainableMethodName?: PropertyKey +): T { if (!isProxyEnabled()) return obj; return new Proxy(obj, { @@ -48,11 +51,11 @@ export function proxify(obj, nonChainableMethodName) { if (nonChainableMethodName) { throw Error( 'Invalid Chai property: ' + - nonChainableMethodName + + String(nonChainableMethodName) + '.' + property + '. See docs for proper usage of "' + - nonChainableMethodName + + String(nonChainableMethodName) + '".' ); } @@ -103,7 +106,7 @@ export function proxify(obj, nonChainableMethodName) { // being called from within another assertion. In that case, the `ssfi` // flag is already set to the outer assertion's starting point. if (builtins.indexOf(property) === -1 && !flag(target, 'lockSsfi')) { - flag(target, 'ssfi', proxyGetter); + flag(target as object, 'ssfi', proxyGetter); } return Reflect.get(target, property); @@ -121,7 +124,7 @@ export function proxify(obj, nonChainableMethodName) { * @returns {number} min(string distance between strA and strB, cap) * @private */ -function stringDistanceCapped(strA, strB, cap) { +function stringDistanceCapped(strA: string, strB: string, cap: number) { if (Math.abs(strA.length - strB.length) >= cap) { return cap; } diff --git a/lib/chai/utils/test.js b/src/chai/utils/test.ts similarity index 89% rename from lib/chai/utils/test.js rename to src/chai/utils/test.ts index 6d64aeb3..172d0ee6 100644 --- a/lib/chai/utils/test.js +++ b/src/chai/utils/test.ts @@ -17,7 +17,7 @@ import {flag} from './flag.js'; * @namespace Utils * @name test */ -export function test(obj, args) { +export function test(obj: object, args: IArguments) { var negate = flag(obj, 'negate'), expr = args[0]; return negate ? !expr : expr; diff --git a/lib/chai/utils/transferFlags.js b/src/chai/utils/transferFlags.ts similarity index 69% rename from lib/chai/utils/transferFlags.js rename to src/chai/utils/transferFlags.ts index 4e5b785f..4386d27e 100644 --- a/lib/chai/utils/transferFlags.js +++ b/src/chai/utils/transferFlags.ts @@ -25,12 +25,21 @@ * @name transferFlags * @private */ -export function transferFlags(assertion, object, includeAll) { - var flags = assertion.__flags || (assertion.__flags = Object.create(null)); +export function transferFlags( + assertion: object, + object: object, + includeAll?: boolean +) { + const assertionWithFlags = assertion as { + __flags?: Record<PropertyKey, unknown>; + }; + const objWithFlags = object as {__flags?: Record<PropertyKey, unknown>}; - if (!object.__flags) { - object.__flags = Object.create(null); - } + var flags = + assertionWithFlags.__flags || + (assertionWithFlags.__flags = Object.create(null)); + const objFlags = + objWithFlags.__flags || (objWithFlags.__flags = Object.create(null)); includeAll = arguments.length === 3 ? includeAll : true; @@ -42,7 +51,7 @@ export function transferFlags(assertion, object, includeAll) { flag !== 'lockSsfi' && flag != 'message') ) { - object.__flags[flag] = flags[flag]; + objFlags[flag] = flags[flag]; } } } diff --git a/lib/chai/utils/type-detect.js b/src/chai/utils/type-detect.ts similarity index 72% rename from lib/chai/utils/type-detect.js rename to src/chai/utils/type-detect.ts index 573edf81..f0ddeca1 100644 --- a/lib/chai/utils/type-detect.js +++ b/src/chai/utils/type-detect.ts @@ -2,7 +2,7 @@ * @param {unknown} obj * @returns {string} */ -export function type(obj) { +export function type(obj: unknown) { if (typeof obj === 'undefined') { return 'undefined'; } @@ -11,7 +11,7 @@ export function type(obj) { return 'null'; } - const stringTag = obj[Symbol.toStringTag]; + const stringTag = (obj as Record<PropertyKey, unknown>)[Symbol.toStringTag]; if (typeof stringTag === 'string') { return stringTag; } diff --git a/src/chai/utils/types.ts b/src/chai/utils/types.ts new file mode 100644 index 00000000..6212c16a --- /dev/null +++ b/src/chai/utils/types.ts @@ -0,0 +1,18 @@ +export type Constructor<T> = {new (): T}; + +export type OnlyIf<T, TCondition, TResult, TElse = never> = T extends TCondition + ? TResult + : TElse; + +export type CollectionLike<T> = + | Map<unknown, T> + | Set<T> + | (T extends object ? WeakSet<T> : never) + | Array<T>; + +export type KeyedObject = Map<unknown, unknown> | object | Set<unknown>; + +export type LengthLike = + | Map<unknown, unknown> + | Set<unknown> + | {length: number}; diff --git a/test/assert.js b/test/assert.js index 89395b05..37d83294 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1,4 +1,4 @@ -import * as chai from '../index.js'; +import * as chai from '../chai.js'; import {globalErr as err} from './bootstrap/index.js'; describe('assert', function () { diff --git a/test/bootstrap/index.js b/test/bootstrap/index.js index a6cb371e..39250fc3 100644 --- a/test/bootstrap/index.js +++ b/test/bootstrap/index.js @@ -1,4 +1,4 @@ -import * as chai from '../../index.js'; +import * as chai from '../../chai.js'; var isStackSupported = false; if (typeof Error.captureStackTrace !== 'undefined') { diff --git a/test/configuration.js b/test/configuration.js index b5ac839d..7358a438 100644 --- a/test/configuration.js +++ b/test/configuration.js @@ -1,4 +1,4 @@ -import * as chai from '../index.js'; +import * as chai from '../chai.js'; import {globalErr as err} from './bootstrap/index.js'; import '../register-should.js'; diff --git a/test/display/errors.js b/test/display/errors.js index cef1b702..eae44a01 100644 --- a/test/display/errors.js +++ b/test/display/errors.js @@ -1,4 +1,4 @@ -import * as chai from '../../index.js'; +import * as chai from '../../chai.js'; var expect = chai.expect; diff --git a/test/display/message.js b/test/display/message.js index 7ce28125..405b0066 100644 --- a/test/display/message.js +++ b/test/display/message.js @@ -1,4 +1,4 @@ -import * as chai from '../../index.js' +import * as chai from '../../chai.js' const expect = chai.expect diff --git a/test/expect.js b/test/expect.js index c9549d77..04b40151 100644 --- a/test/expect.js +++ b/test/expect.js @@ -1,4 +1,4 @@ -import * as chai from '../index.js'; +import * as chai from '../chai.js'; import {globalErr as err} from './bootstrap/index.js'; describe('expect', function () { @@ -11,29 +11,34 @@ describe('expect', function () { describe('safeguards', function () { before(function () { + const getDefaultValue = (assertion) => { + var newAssertion = chai.Assertion.create(); + chai.util.transferFlags(assertion, newAssertion); + return newAssertion; + }; chai.util.addProperty(chai.Assertion.prototype, 'tmpProperty', function () { new chai.Assertion(42).equal(42); - }); + }, getDefaultValue); chai.util.overwriteProperty(chai.Assertion.prototype, 'tmpProperty', function (_super) { return function () { _super.call(this); }; - }); + }, getDefaultValue); chai.util.addMethod(chai.Assertion.prototype, 'tmpMethod', function () { new chai.Assertion(42).equal(42); - }); + }, getDefaultValue); chai.util.overwriteMethod(chai.Assertion.prototype, 'tmpMethod', function (_super) { return function () { _super.call(this); }; - }); + }, getDefaultValue); chai.util.addChainableMethod(chai.Assertion.prototype, 'tmpChainableMethod', function () { new chai.Assertion(42).equal(42); }, function () { new chai.Assertion(42).equal(42); - }); + }, getDefaultValue); chai.util.overwriteChainableMethod(chai.Assertion.prototype, 'tmpChainableMethod', function (_super) { return function () { _super.call(this); @@ -42,7 +47,7 @@ describe('expect', function () { return function () { _super.call(this); }; - }); + }, getDefaultValue); }); after(function () { diff --git a/test/globalErr.js b/test/globalErr.js index d51b3fe8..8a3e8677 100644 --- a/test/globalErr.js +++ b/test/globalErr.js @@ -1,4 +1,4 @@ -import * as chai from '../index.js'; +import * as chai from '../chai.js'; import {globalErr as err} from './bootstrap/index.js'; describe('globalErr', function () { diff --git a/test/globalShould.js b/test/globalShould.js index 5fb11eaf..0c1b2e6b 100644 --- a/test/globalShould.js +++ b/test/globalShould.js @@ -1,4 +1,4 @@ -import * as chai from '../index.js'; +import * as chai from '../chai.js'; describe('global should', function () { it('works', function () { diff --git a/test/plugins.js b/test/plugins.js index c9c013cc..f3c1605b 100644 --- a/test/plugins.js +++ b/test/plugins.js @@ -1,4 +1,4 @@ -import * as chai from '../index.js'; +import * as chai from '../chai.js'; describe('plugins', function () { diff --git a/test/should.js b/test/should.js index 4b7fbc48..54c8c3ef 100644 --- a/test/should.js +++ b/test/should.js @@ -1,4 +1,4 @@ -import * as chai from '../index.js'; +import * as chai from '../chai.js'; import {globalErr as err} from './bootstrap/index.js'; describe('should', function() { @@ -12,29 +12,34 @@ describe('should', function() { describe('safeguards', function () { before(function () { + const getDefaultValue = (assertion) => { + var newAssertion = chai.Assertion.create(); + chai.util.transferFlags(assertion, newAssertion); + return newAssertion; + }; chai.util.addProperty(chai.Assertion.prototype, 'tmpProperty', function () { new chai.Assertion(42).equal(42); - }); + }, getDefaultValue); chai.util.overwriteProperty(chai.Assertion.prototype, 'tmpProperty', function (_super) { return function () { _super.call(this); }; - }); + }, getDefaultValue); chai.util.addMethod(chai.Assertion.prototype, 'tmpMethod', function () { new chai.Assertion(42).equal(42); - }); + }, getDefaultValue); chai.util.overwriteMethod(chai.Assertion.prototype, 'tmpMethod', function (_super) { return function () { _super.call(this); }; - }); + }, getDefaultValue); chai.util.addChainableMethod(chai.Assertion.prototype, 'tmpChainableMethod', function () { new chai.Assertion(42).equal(42); }, function () { new chai.Assertion(42).equal(42); - }); + }, getDefaultValue); chai.util.overwriteChainableMethod(chai.Assertion.prototype, 'tmpChainableMethod', function (_super) { return function () { _super.call(this); @@ -43,7 +48,7 @@ describe('should', function() { return function () { _super.call(this); }; - }); + }, getDefaultValue); }); after(function () { diff --git a/test/type-detect/dom.js b/test/type-detect/dom.js index bda6bdc3..e2ceeab1 100644 --- a/test/type-detect/dom.js +++ b/test/type-detect/dom.js @@ -1,4 +1,4 @@ -import * as chai from '../../index.js'; +import * as chai from '../../chai.js'; function assert (expr, msg) { if (!expr) { diff --git a/test/type-detect/index.js b/test/type-detect/index.js index 983800d1..b1494473 100644 --- a/test/type-detect/index.js +++ b/test/type-detect/index.js @@ -1,4 +1,4 @@ -import * as chai from '../../index.js'; +import * as chai from '../../chai.js'; function assert (expr, msg) { if (!expr) { diff --git a/test/type-detect/new-ecmascript-types.js b/test/type-detect/new-ecmascript-types.js index 256c998e..c52d3fd2 100644 --- a/test/type-detect/new-ecmascript-types.js +++ b/test/type-detect/new-ecmascript-types.js @@ -1,4 +1,4 @@ -import * as chai from '../../index.js'; +import * as chai from '../../chai.js'; function assert (expr, msg) { if (!expr) { diff --git a/test/type-detect/node.js b/test/type-detect/node.js index 18552548..f59733b6 100644 --- a/test/type-detect/node.js +++ b/test/type-detect/node.js @@ -1,4 +1,4 @@ -import * as chai from '../../index.js'; +import * as chai from '../../chai.js'; function assert (expr, msg) { if (!expr) { diff --git a/test/type-detect/tostringtag-extras.js b/test/type-detect/tostringtag-extras.js index 3a0f7deb..883181a0 100644 --- a/test/type-detect/tostringtag-extras.js +++ b/test/type-detect/tostringtag-extras.js @@ -1,4 +1,4 @@ -import * as chai from '../../index.js'; +import * as chai from '../../chai.js'; function assert (expr, msg) { if (!expr) { diff --git a/test/utilities.js b/test/utilities.js index 3e956352..aacda8b1 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -1,4 +1,4 @@ -import * as chai from '../index.js'; +import * as chai from '../chai.js'; describe('utilities', function () { const expect = chai.expect; diff --git a/test/virtual-machines.js b/test/virtual-machines.js index da5ec416..e1b53fa0 100644 --- a/test/virtual-machines.js +++ b/test/virtual-machines.js @@ -1,5 +1,5 @@ import vm from 'node:vm'; -import * as chai from '../index.js'; +import * as chai from '../chai.js'; const {assert} = chai; const vmContext = {assert}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..beea3281 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es2021", + "module": "nodenext", + "moduleResolution": "nodenext", + "types": [], + "declaration": true, + "sourceMap": true, + "outDir": "./lib", + "isolatedModules": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + "include": [ + "src/**/*.ts" + ] +}