From 4880c4cfaa34bd0b3d556451a7fe2997719f1b49 Mon Sep 17 00:00:00 2001 From: Mathias Lorenzen Date: Fri, 8 Mar 2024 14:59:56 +0100 Subject: [PATCH 01/11] feat: introduce symbols --- package-lock.json | 1664 ++++++++++++++++++++++--------- spec/ClearSubstitute.spec.ts | 8 +- spec/regression/returns.spec.ts | 2 + src/Symbols.ts | 11 + src/Transformations.ts | 9 +- src/Types.ts | 8 +- src/index.ts | 1 + 7 files changed, 1193 insertions(+), 510 deletions(-) create mode 100644 src/Symbols.ts diff --git a/package-lock.json b/package-lock.json index 026eb5d..93b96f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,142 +1,215 @@ { "name": "@fluffy-spoon/substitute", "version": "2.0.0-beta.2", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@ava/typescript": { + "packages": { + "": { + "name": "@fluffy-spoon/substitute", + "version": "2.0.0-beta.2", + "license": "MIT", + "devDependencies": { + "@ava/typescript": "^3.0.1", + "@types/node": "^12.20.55", + "ava": "^4.3.3", + "typescript": "^4.8.4" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/substitute-js#section-contribute" + } + }, + "node_modules/@ava/typescript": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@ava/typescript/-/typescript-3.0.1.tgz", "integrity": "sha512-/JXIUuKsvkaneaiA9ckk3ksFTqvu0mDNlChASrTe2BnDsvMbhQdPWyqQjJ9WRJWVhhs5TWn1/0Pp1G6Rv8Syrw==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^5.0.0", "execa": "^5.1.1" + }, + "engines": { + "node": ">=12.22 <13 || >=14.17 <15 || >=16.4 <17 || >=17" } }, - "@nodelib/fs.scandir": { + "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "requires": { + "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "@nodelib/fs.stat": { + "node_modules/@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true + "dev": true, + "engines": { + "node": ">= 8" + } }, - "@nodelib/fs.walk": { + "node_modules/@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "requires": { + "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" } }, - "@types/node": { + "node_modules/@types/node": { "version": "12.20.55", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", "dev": true }, - "acorn": { + "node_modules/acorn": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } }, - "acorn-walk": { + "node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, - "aggregate-error": { + "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, - "requires": { + "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" }, - "dependencies": { - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - } + "engines": { + "node": ">=8" + } + }, + "node_modules/aggregate-error/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" } }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.1.tgz", "integrity": "sha512-qDOv24WjnYuL+wbwHdlsYZFy+cgPtrYw0Tn7GLORicQp9BkQLzrgI3Pm4VyR9ERZ41YTn7KlMPuL1n05WdZvmg==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "anymatch": { + "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "requires": { + "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "argparse": { + "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "requires": { + "dependencies": { "sprintf-js": "~1.0.2" } }, - "array-find-index": { + "node_modules/array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "array-union": { + "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "arrgv": { + "node_modules/arrgv": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8.0.0" + } }, - "arrify": { + "node_modules/arrify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "ava": { + "node_modules/ava": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/ava/-/ava-4.3.3.tgz", "integrity": "sha512-9Egq/d9R74ExrWohHeqUlexjDbgZJX5jA1Wq4KCTqc3wIfpGEK79zVy4rBtofJ9YKIxs4PzhJ8BgbW5PlAYe6w==", "dev": true, - "requires": { + "dependencies": { "acorn": "^8.7.1", "acorn-walk": "^8.2.0", "ansi-styles": "^6.1.0", @@ -182,215 +255,292 @@ "temp-dir": "^2.0.0", "write-file-atomic": "^4.0.1", "yargs": "^17.5.1" + }, + "bin": { + "ava": "entrypoints/cli.mjs" + }, + "engines": { + "node": ">=12.22 <13 || >=14.17 <15 || >=16.4 <17 || >=18" + }, + "peerDependencies": { + "@ava/typescript": "*" + }, + "peerDependenciesMeta": { + "@ava/typescript": { + "optional": true + } } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "binary-extensions": { + "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "blueimp-md5": { + "node_modules/blueimp-md5": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", "dev": true }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { + "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "requires": { + "dependencies": { "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" } }, - "callsites": { + "node_modules/callsites": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.0.0.tgz", "integrity": "sha512-y3jRROutgpKdz5vzEhWM34TidDU8vkJppF8dszITeb1PQmSqV3DTxyV8G/lyO/DNvtE1YTedehmw9MPZsCBHxQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "cbor": { + "node_modules/cbor": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", "dev": true, - "requires": { + "dependencies": { "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=12.19" } }, - "chalk": { + "node_modules/chalk": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.0.tgz", "integrity": "sha512-56zD4khRTBoIyzUYAFgDDaPhUMN/fC/rySe6aZGqbj/VWiU2eI3l6ZLOtYGFZAV5v02mwPjtpzlrOveJiz5eZQ==", - "dev": true + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "chokidar": { + "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, - "requires": { + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "chunkd": { + "node_modules/chunkd": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", "dev": true }, - "ci-info": { + "node_modules/ci-info": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", "dev": true }, - "ci-parallel-vars": { + "node_modules/ci-parallel-vars": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", "dev": true }, - "clean-stack": { + "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "clean-yaml-object": { + "node_modules/clean-yaml-object": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/clean-yaml-object/-/clean-yaml-object-0.1.0.tgz", "integrity": "sha512-3yONmlN9CSAkzNwnRCiJQ7Q2xK5mWuEfL3PuTZcAUzhObbXsfsnMptJzXwz93nc5zn9V9TwCVMmV7w4xsm43dw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "cli-truncate": { + "node_modules/cli-truncate": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", "dev": true, - "requires": { + "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "cliui": { + "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "requires": { + "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "code-excerpt": { + "node_modules/code-excerpt": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", "dev": true, - "requires": { + "dependencies": { "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "color-convert": { + "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { + "dependencies": { "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "common-path-prefix": { + "node_modules/common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", "dev": true }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "concordance": { + "node_modules/concordance": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", "dev": true, - "requires": { + "dependencies": { "date-time": "^3.1.0", "esutils": "^2.0.3", "fast-diff": "^1.2.0", @@ -399,66 +549,87 @@ "md5-hex": "^3.0.1", "semver": "^7.3.2", "well-known-symbols": "^2.0.0" + }, + "engines": { + "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" } }, - "convert-to-spaces": { + "node_modules/convert-to-spaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", - "dev": true + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } }, - "cross-spawn": { + "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "currently-unhandled": { + "node_modules/currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", "dev": true, - "requires": { + "dependencies": { "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "date-time": { + "node_modules/date-time": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", "dev": true, - "requires": { + "dependencies": { "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" } }, - "debug": { + "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, - "requires": { + "dependencies": { "ms": "2.1.2" }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "del": { + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/del": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", "dev": true, - "requires": { + "dependencies": { "globby": "^11.0.1", "graceful-fs": "^4.2.4", "is-glob": "^4.0.1", @@ -468,89 +639,133 @@ "rimraf": "^3.0.2", "slash": "^3.0.0" }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, "dependencies": { - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - } + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "dir-glob": { + "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "requires": { + "dependencies": { "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "eastasianwidth": { + "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "emittery": { + "node_modules/emittery": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.11.0.tgz", "integrity": "sha512-S/7tzL6v5i+4iJd627Nhv9cLFIo5weAIlGccqJFpnBoDB8U1TF2k5tez4J/QNuxyyhWuFqHg1L84Kd3m7iXg6g==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } }, - "emoji-regex": { + "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "escalade": { + "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "esprima": { + "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } }, - "esutils": { + "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "execa": { + "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, - "requires": { + "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", @@ -560,900 +775,1329 @@ "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "fast-diff": { + "node_modules/fast-diff": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", "dev": true }, - "fast-glob": { + "node_modules/fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, - "requires": { + "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" } }, - "fastq": { + "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, - "requires": { + "dependencies": { "reusify": "^1.0.4" } }, - "figures": { + "node_modules/figures": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/figures/-/figures-4.0.1.tgz", "integrity": "sha512-rElJwkA/xS04Vfg+CaZodpso7VqBknOYbzi6I76hI4X80RUjkSxO2oAyPmGbuXUppywjqndOrQDl817hDnI++w==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^5.0.0", "is-unicode-supported": "^1.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "fill-range": { + "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "requires": { + "dependencies": { "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "find-up": { + "node_modules/find-up": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dev": true, - "requires": { + "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "fsevents": { + "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "optional": true + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "get-caller-file": { + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } }, - "get-stream": { + "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "glob": { + "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "requires": { + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-parent": { + "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "requires": { + "dependencies": { "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "globby": { + "node_modules/globby": { "version": "13.1.2", "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", "dev": true, - "requires": { + "dependencies": { "dir-glob": "^3.0.1", "fast-glob": "^3.2.11", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^4.0.0" }, - "dependencies": { - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - } + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "graceful-fs": { + "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, - "human-signals": { + "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10.17.0" + } }, - "ignore": { + "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true + "dev": true, + "engines": { + "node": ">= 4" + } }, - "ignore-by-default": { + "node_modules/ignore-by-default": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10 <11 || >=12 <13 || >=14" + } }, - "imurmurhash": { + "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8.19" + } }, - "indent-string": { + "node_modules/indent-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "irregular-plurals": { + "node_modules/irregular-plurals": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.3.0.tgz", "integrity": "sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-binary-path": { + "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "requires": { + "dependencies": { "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-error": { + "node_modules/is-error": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz", "integrity": "sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==", "dev": true }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "is-glob": { + "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "requires": { + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-number": { + "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.12.0" + } }, - "is-path-cwd": { + "node_modules/is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "is-path-inside": { + "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-plain-object": { + "node_modules/is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-promise": { + "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "dev": true }, - "is-stream": { + "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "is-unicode-supported": { + "node_modules/is-unicode-supported": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "js-string-escape": { + "node_modules/js-string-escape": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8" + } }, - "js-yaml": { + "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "requires": { + "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "load-json-file": { + "node_modules/load-json-file": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", - "dev": true + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "locate-path": { + "node_modules/locate-path": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.1.1.tgz", "integrity": "sha512-vJXaRMJgRVD3+cUZs3Mncj2mxpt5mP0EmNOsxRSZRMlbqjvxzDEOIUWXGmavo0ZC9+tNZCBLQ66reA11nbpHZg==", "dev": true, - "requires": { + "dependencies": { "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "lodash": { + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "lru-cache": { + "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "requires": { + "dependencies": { "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "map-age-cleaner": { + "node_modules/map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "dev": true, - "requires": { + "dependencies": { "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" } }, - "matcher": { + "node_modules/matcher": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "md5-hex": { + "node_modules/md5-hex": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", "dev": true, - "requires": { + "dependencies": { "blueimp-md5": "^2.10.0" + }, + "engines": { + "node": ">=8" } }, - "mem": { + "node_modules/mem": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/mem/-/mem-9.0.2.tgz", "integrity": "sha512-F2t4YIv9XQUBHt6AOJ0y7lSmP1+cY7Fm1DRh9GClTGzKST7UWLMx6ly9WZdLH/G/ppM5RL4MlQfRT71ri9t19A==", "dev": true, - "requires": { + "dependencies": { "map-age-cleaner": "^0.1.3", "mimic-fn": "^4.0.0" }, - "dependencies": { - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - } + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sindresorhus/mem?sponsor=1" + } + }, + "node_modules/mem/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "merge-stream": { + "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "merge2": { + "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 8" + } }, - "micromatch": { + "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, - "requires": { + "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "mimic-fn": { + "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "ms": { + "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "nofilter": { + "node_modules/nofilter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", - "dev": true + "dev": true, + "engines": { + "node": ">=12.19" + } }, - "normalize-path": { + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "npm-run-path": { + "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "onetime": { + "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "requires": { + "dependencies": { "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-defer": { + "node_modules/p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "p-event": { + "node_modules/p-event": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz", "integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==", "dev": true, - "requires": { + "dependencies": { "p-timeout": "^5.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-limit": { + "node_modules/p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, - "requires": { + "dependencies": { "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { + "node_modules/p-locate": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, - "requires": { + "dependencies": { "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-map": { + "node_modules/p-map": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", "dev": true, - "requires": { + "dependencies": { "aggregate-error": "^4.0.0" }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map/node_modules/aggregate-error": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", + "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "dev": true, "dependencies": { - "aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", - "dev": true, - "requires": { - "clean-stack": "^4.0.0", - "indent-string": "^5.0.0" - } - }, - "clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", - "dev": true, - "requires": { - "escape-string-regexp": "5.0.0" - } - } + "clean-stack": "^4.0.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-timeout": { + "node_modules/p-map/node_modules/clean-stack": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", + "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "parse-ms": { + "node_modules/parse-ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "path-exists": { + "node_modules/path-exists": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-type": { + "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "picomatch": { + "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "pkg-conf": { + "node_modules/pkg-conf": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-4.0.0.tgz", "integrity": "sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w==", "dev": true, - "requires": { + "dependencies": { "find-up": "^6.0.0", "load-json-file": "^7.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "plur": { + "node_modules/plur": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", "dev": true, - "requires": { + "dependencies": { "irregular-plurals": "^3.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "pretty-ms": { + "node_modules/pretty-ms": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", "dev": true, - "requires": { + "dependencies": { "parse-ms": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "queue-microtask": { + "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "readdirp": { + "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "requires": { + "dependencies": { "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "resolve-cwd": { + "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "requires": { + "dependencies": { "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "resolve-from": { + "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "reusify": { + "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } }, - "rimraf": { + "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, - "requires": { + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "run-parallel": { + "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "requires": { + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { "queue-microtask": "^1.2.2" } }, - "semver": { + "node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, - "requires": { + "dependencies": { "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "serialize-error": { + "node_modules/serialize-error": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "dev": true, - "requires": { + "dependencies": { "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "shebang-command": { + "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "signal-exit": { + "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "slash": { + "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "slice-ansi": { + "node_modules/slice-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "sprintf-js": { + "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "stack-utils": { + "node_modules/stack-utils": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^2.0.0" }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } + "engines": { + "node": ">=10" } }, - "string-width": { + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, - "requires": { + "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "strip-final-newline": { + "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "supertap": { + "node_modules/supertap": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", "dev": true, - "requires": { + "dependencies": { "indent-string": "^5.0.0", "js-yaml": "^3.14.1", "serialize-error": "^7.0.1", "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "temp-dir": { + "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "time-zone": { + "node_modules/time-zone": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "to-regex-range": { + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "type-fest": { + "node_modules/type-fest": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "typescript": { + "node_modules/typescript": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } }, - "well-known-symbols": { + "node_modules/well-known-symbols": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "wrappy": { + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "write-file-atomic": { + "node_modules/write-file-atomic": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "requires": { + "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yallist": { + "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "yargs": { + "node_modules/yargs": { "version": "17.6.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", "dev": true, - "requires": { + "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -1462,58 +2106,80 @@ "y18n": "^5.0.5", "yargs-parser": "^21.0.0" }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } + "engines": { + "node": ">=12" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "yocto-queue": { + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/spec/ClearSubstitute.spec.ts b/spec/ClearSubstitute.spec.ts index 0dad128..c9a93df 100644 --- a/spec/ClearSubstitute.spec.ts +++ b/spec/ClearSubstitute.spec.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, SubstituteOf } from '../src' +import { Substitute, SubstituteOf, clearSubstitute, received } from '../src' import { SubstituteNode } from '../src/SubstituteNode' interface Calculator { @@ -17,13 +17,13 @@ type InstanceReturningSubstitute = SubstituteOf & { test('clears everything on a substitute', t => { const calculator = Substitute.for() as InstanceReturningSubstitute calculator.add(1, 1) - calculator.received().add(1, 1) - calculator.clearSubstitute() + calculator[received]().add(1, 1) + calculator[clearSubstitute]() t.is(calculator[SubstituteNode.instance].recorder.records.size, 0) t.is(calculator[SubstituteNode.instance].recorder.indexedRecords.size, 0) - t.throws(() => calculator.received().add(1, 1)) + t.throws(() => calculator[received]().add(1, 1)) // explicitly using 'all' calculator.add(1, 1) diff --git a/spec/regression/returns.spec.ts b/spec/regression/returns.spec.ts index 54f61a6..74081ce 100644 --- a/spec/regression/returns.spec.ts +++ b/spec/regression/returns.spec.ts @@ -32,6 +32,8 @@ test('returns a primitive value for method with specific arguments', t => { calculator.add(1, 1).returns(2) + calculator.clearSubstitute + t.is(2, calculator.add(1, 1)) t.is(2, calculator.add(1, 1)) t.true(types.isProxy(noResult)) diff --git a/src/Symbols.ts b/src/Symbols.ts new file mode 100644 index 0000000..068d3ad --- /dev/null +++ b/src/Symbols.ts @@ -0,0 +1,11 @@ + +export const received = Symbol('received'); +export const didNotReceive = Symbol('didNotReceive'); +export const mimick = Symbol('mimick'); +export const clearSubstitute = Symbol('clearSubstitute'); + +export const mimicks = Symbol('mimicks'); +export const throws = Symbol('throws'); +export const returns = Symbol('returns'); +export const resolves = Symbol('resolves'); +export const rejects = Symbol('rejects'); \ No newline at end of file diff --git a/src/Transformations.ts b/src/Transformations.ts index f824b68..6af75b5 100644 --- a/src/Transformations.ts +++ b/src/Transformations.ts @@ -1,4 +1,5 @@ import type { AllArguments } from './Arguments'; +import { clearSubstitute, didNotReceive, mimick, received } from './Symbols'; import type { ClearType, FirstLevelMethod } from './Types'; type FunctionSubstituteWithOverloads = @@ -87,9 +88,9 @@ type ObjectSubstituteTransformation> = { export type OmitProxyMethods = Omit; export type ObjectSubstitute = ObjectSubstituteTransformation & { - received(amount?: number): TerminatingObject; - didNotReceive(): TerminatingObject; - mimick(instance: OmitProxyMethods): void; - clearSubstitute(clearType?: ClearType): void; + [received](amount?: number): TerminatingObject; + [didNotReceive](): TerminatingObject; + [mimick](instance: OmitProxyMethods): void; + [clearSubstitute](clearType?: ClearType): void; } export type DisabledSubstituteObject = T extends ObjectSubstitute ? K : never; diff --git a/src/Types.ts b/src/Types.ts index d9b6228..7ff2368 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -1,7 +1,9 @@ +import type { clearSubstitute, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from "./Symbols" + export type PropertyType = 'method' | 'property' -export type AssertionMethod = 'received' | 'didNotReceive' -export type ConfigurationMethod = 'clearSubstitute' | 'mimick' -export type SubstitutionMethod = 'mimicks' | 'throws' | 'returns' | 'resolves' | 'rejects' +export type AssertionMethod = typeof received | typeof didNotReceive +export type ConfigurationMethod = typeof clearSubstitute | typeof mimick +export type SubstitutionMethod = typeof mimicks | typeof throws | typeof returns | typeof resolves | typeof rejects export type FirstLevelMethod = AssertionMethod | ConfigurationMethod export type SubstituteMethod = FirstLevelMethod | SubstitutionMethod diff --git a/src/index.ts b/src/index.ts index ffd8ce0..bd72dc8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,5 +3,6 @@ import { Substitute, SubstituteOf } from './Substitute' export { Arg } from './Arguments' export { Substitute, SubstituteOf } export { ClearType } from './Utilities' +export { clearSubstitute, didNotReceive, mimick, received } from './Symbols' export default Substitute \ No newline at end of file From 2e93eef03e4528caf6b4e6b3e2c155e67748df49 Mon Sep 17 00:00:00 2001 From: Mathias Lorenzen Date: Sun, 10 Mar 2024 18:52:39 +0100 Subject: [PATCH 02/11] fix: progress on new syntax --- spec/ClearSubstitute.spec.ts | 42 +++--------------------------------- src/SubstituteNode.ts | 17 ++++++++------- src/Symbols.ts | 2 +- src/Transformations.ts | 4 ++-- src/Types.ts | 6 ++---- src/index.ts | 2 +- 6 files changed, 18 insertions(+), 55 deletions(-) diff --git a/spec/ClearSubstitute.spec.ts b/spec/ClearSubstitute.spec.ts index c9a93df..899249f 100644 --- a/spec/ClearSubstitute.spec.ts +++ b/spec/ClearSubstitute.spec.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, SubstituteOf, clearSubstitute, received } from '../src' +import { Substitute, SubstituteOf, clearReceivedCalls, received } from '../src' import { SubstituteNode } from '../src/SubstituteNode' interface Calculator { @@ -14,51 +14,15 @@ type InstanceReturningSubstitute = SubstituteOf & { [SubstituteNode.instance]: SubstituteNode } -test('clears everything on a substitute', t => { - const calculator = Substitute.for() as InstanceReturningSubstitute - calculator.add(1, 1) - calculator[received]().add(1, 1) - calculator[clearSubstitute]() - - t.is(calculator[SubstituteNode.instance].recorder.records.size, 0) - t.is(calculator[SubstituteNode.instance].recorder.indexedRecords.size, 0) - - t.throws(() => calculator[received]().add(1, 1)) - - // explicitly using 'all' - calculator.add(1, 1) - calculator.received().add(1, 1) - calculator.clearSubstitute('all') - - t.is(calculator[SubstituteNode.instance].recorder.records.size, 0) - t.is(calculator[SubstituteNode.instance].recorder.indexedRecords.size, 0) - - t.throws(() => calculator.received().add(1, 1)) -}) - test('clears received calls on a substitute', t => { const calculator = Substitute.for() as InstanceReturningSubstitute calculator.add(1, 1) calculator.add(1, 1).returns(2) - calculator.clearSubstitute('receivedCalls') + calculator[clearReceivedCalls](); t.is(calculator[SubstituteNode.instance].recorder.records.size, 2) t.is(calculator[SubstituteNode.instance].recorder.indexedRecords.size, 2) - t.throws(() => calculator.received().add(1, 1)) + t.throws(() => calculator[received]().add(1, 1)) t.is(2, calculator.add(1, 1)) -}) - -test('clears return values on a substitute', t => { - const calculator = Substitute.for() as InstanceReturningSubstitute - calculator.add(1, 1) - calculator.add(1, 1).returns(2) - calculator.clearSubstitute('substituteValues') - - t.is(calculator[SubstituteNode.instance].recorder.records.size, 2) - t.is(calculator[SubstituteNode.instance].recorder.indexedRecords.size, 2) - - t.notThrows(() => calculator.received().add(1, 1)) - // @ts-expect-error - t.true(calculator.add(1, 1)[SubstituteNode.instance] instanceof SubstituteNode) }) \ No newline at end of file diff --git a/src/SubstituteNode.ts b/src/SubstituteNode.ts index 03119ce..dfb0be5 100644 --- a/src/SubstituteNode.ts +++ b/src/SubstituteNode.ts @@ -6,6 +6,7 @@ import { ClearType as ClearTypeMap, PropertyType as PropertyTypeMap, isAssertion import { SubstituteException } from './SubstituteException' import type { FilterFunction, SubstituteContext, SubstitutionMethod, ClearType, PropertyType } from './Types' import type { ObjectSubstitute } from './Transformations' +import { didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from './Symbols' const instance = Symbol('Substitute:Instance') const clearTypeToFilterMap: Record> = { @@ -114,17 +115,17 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu return this._recordedArguments } - public received(amount?: number): SubstituteNode { + public [received](amount?: number): SubstituteNode { this.handleMethod([amount]) return this.proxy } - public didNotReceive(): SubstituteNode { + public [didNotReceive](): SubstituteNode { this.handleMethod([0]) return this.proxy } - public mimick() { + public [mimick]() { throw new Error('Mimick is not implemented yet') } @@ -165,17 +166,17 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu ? this.child.recordedArguments.value?.shift() : this.child.recordedArguments.value[0] switch (substitutionMethod) { - case 'throws': + case throws: throw substitutionValue - case 'mimicks': + case mimicks: if (this.propertyType === PropertyTypeMap.Property) return substitutionValue() if (!contextArguments.hasArguments()) throw new TypeError('Context arguments cannot be undefined') return substitutionValue(...contextArguments.value) - case 'resolves': + case resolves: return Promise.resolve(substitutionValue) - case 'rejects': + case rejects: return Promise.reject(substitutionValue) - case 'returns': + case returns: return substitutionValue default: throw SubstituteException.generic(`Substitution method '${substitutionMethod}' not implemented`) diff --git a/src/Symbols.ts b/src/Symbols.ts index 068d3ad..2f2bc0a 100644 --- a/src/Symbols.ts +++ b/src/Symbols.ts @@ -2,7 +2,7 @@ export const received = Symbol('received'); export const didNotReceive = Symbol('didNotReceive'); export const mimick = Symbol('mimick'); -export const clearSubstitute = Symbol('clearSubstitute'); +export const clearReceivedCalls = Symbol('clearReceivedCalls'); export const mimicks = Symbol('mimicks'); export const throws = Symbol('throws'); diff --git a/src/Transformations.ts b/src/Transformations.ts index 380fc3b..4432320 100644 --- a/src/Transformations.ts +++ b/src/Transformations.ts @@ -1,5 +1,5 @@ import type { AllArguments } from './Arguments'; -import { clearSubstitute, didNotReceive, mimick, received } from './Symbols'; +import { clearReceivedCalls, didNotReceive, mimick, received } from './Symbols'; import type { ClearType, FirstLevelMethod } from './Types'; type FunctionSubstituteWithOverloads = @@ -96,6 +96,6 @@ export type ObjectSubstitute = ObjectSubstituteTransformation & { [received](amount?: number): TerminatingObject; [didNotReceive](): TerminatingObject; [mimick](instance: OmitProxyMethods): void; - [clearSubstitute](clearType?: ClearType): void; + [clearReceivedCalls](clearType?: ClearType): void; } export type DisabledSubstituteObject = T extends ObjectSubstitute ? K : never; diff --git a/src/Types.ts b/src/Types.ts index 7ff2368..77dd1ab 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -1,14 +1,12 @@ -import type { clearSubstitute, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from "./Symbols" +import type { clearReceivedCalls, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from "./Symbols" export type PropertyType = 'method' | 'property' export type AssertionMethod = typeof received | typeof didNotReceive -export type ConfigurationMethod = typeof clearSubstitute | typeof mimick +export type ConfigurationMethod = typeof clearReceivedCalls | typeof mimick export type SubstitutionMethod = typeof mimicks | typeof throws | typeof returns | typeof resolves | typeof rejects export type FirstLevelMethod = AssertionMethod | ConfigurationMethod export type SubstituteMethod = FirstLevelMethod | SubstitutionMethod export type SubstituteContext = SubstituteMethod | 'none' -export type ClearType = 'all' | 'receivedCalls' | 'substituteValues' - export type FilterFunction = (item: T) => boolean \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index bd72dc8..ac40401 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,6 @@ import { Substitute, SubstituteOf } from './Substitute' export { Arg } from './Arguments' export { Substitute, SubstituteOf } export { ClearType } from './Utilities' -export { clearSubstitute, didNotReceive, mimick, received } from './Symbols' +export { clearReceivedCalls, didNotReceive, mimick, received } from './Symbols' export default Substitute \ No newline at end of file From cdd3d04285a64fc8865b08c7d16f9bee9e0c083c Mon Sep 17 00:00:00 2001 From: Mathias Lorenzen Date: Sun, 10 Mar 2024 19:51:25 +0100 Subject: [PATCH 03/11] fix: progress on symbols --- spec/regression/didNotReceive.spec.ts | 32 +++++------ spec/regression/returns.spec.ts | 2 - src/Substitute.ts | 1 + src/SubstituteNode.ts | 82 ++++++++++++++++++--------- src/Symbols.ts | 11 ---- src/Transformations.ts | 18 ++++-- src/Types.ts | 2 +- src/index.ts | 2 +- 8 files changed, 89 insertions(+), 61 deletions(-) delete mode 100644 src/Symbols.ts diff --git a/spec/regression/didNotReceive.spec.ts b/spec/regression/didNotReceive.spec.ts index 2b01ecc..0403257 100644 --- a/spec/regression/didNotReceive.spec.ts +++ b/spec/regression/didNotReceive.spec.ts @@ -1,5 +1,5 @@ import test from 'ava' -import { Substitute, Arg } from '../../src' +import { Substitute, Arg, didNotReceive, received } from '../../src' import { SubstituteException } from '../../src/SubstituteException' interface Calculator { @@ -12,41 +12,41 @@ interface Calculator { test('not calling a method correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.didNotReceive().add(1, 1) - t.throws(() => calculator.received(1).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received().add(Arg.all()), { instanceOf: SubstituteException }) + calculator[didNotReceive]().add(1, 1) + t.throws(() => calculator[received]().add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received]().add(Arg.all()), { instanceOf: SubstituteException }) }) test('not getting a property correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.didNotReceive().isEnabled - t.throws(() => calculator.received(1).isEnabled, { instanceOf: SubstituteException }) - t.throws(() => calculator.received().isEnabled, { instanceOf: SubstituteException }) + calculator[didNotReceive]().isEnabled + t.throws(() => calculator[received](1).isEnabled, { instanceOf: SubstituteException }) + t.throws(() => calculator[received]().isEnabled, { instanceOf: SubstituteException }) }) test('not setting a property correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.didNotReceive().isEnabled = true - t.throws(() => calculator.received(1).isEnabled = true, { instanceOf: SubstituteException }) - t.throws(() => calculator.received().isEnabled = true, { instanceOf: SubstituteException }) + calculator[didNotReceive]().isEnabled = true + t.throws(() => calculator[received](1).isEnabled = true, { instanceOf: SubstituteException }) + t.throws(() => calculator[received]().isEnabled = true, { instanceOf: SubstituteException }) }) test('not calling a method with mock correctly asserts the call count', t => { const calculator = Substitute.for() calculator.add(1, 1).returns(2) - calculator.didNotReceive().add(1, 1) - t.throws(() => calculator.received(1).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received().add(Arg.all()), { instanceOf: SubstituteException }) + calculator[didNotReceive]().add(1, 1) + t.throws(() => calculator[received](1).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received]().add(Arg.all()), { instanceOf: SubstituteException }) }) test('not getting a property with mock correctly asserts the call count', t => { const calculator = Substitute.for() calculator.isEnabled.returns(true) - calculator.didNotReceive().isEnabled - t.throws(() => calculator.received(1).isEnabled, { instanceOf: SubstituteException }) - t.throws(() => calculator.received().isEnabled, { instanceOf: SubstituteException }) + calculator[didNotReceive]().isEnabled + t.throws(() => calculator[received](1).isEnabled, { instanceOf: SubstituteException }) + t.throws(() => calculator[received]().isEnabled, { instanceOf: SubstituteException }) }) \ No newline at end of file diff --git a/spec/regression/returns.spec.ts b/spec/regression/returns.spec.ts index 74081ce..54f61a6 100644 --- a/spec/regression/returns.spec.ts +++ b/spec/regression/returns.spec.ts @@ -32,8 +32,6 @@ test('returns a primitive value for method with specific arguments', t => { calculator.add(1, 1).returns(2) - calculator.clearSubstitute - t.is(2, calculator.add(1, 1)) t.is(2, calculator.add(1, 1)) t.true(types.isProxy(noResult)) diff --git a/src/Substitute.ts b/src/Substitute.ts index 3f87849..323495f 100644 --- a/src/Substitute.ts +++ b/src/Substitute.ts @@ -2,6 +2,7 @@ import { DisabledSubstituteObject, ObjectSubstitute } from './Transformations' import { SubstituteNode } from './SubstituteNode' export type SubstituteOf = ObjectSubstitute & T + type InstantiableSubstitute> = T & { [SubstituteNode.instance]: SubstituteNode } export class Substitute { diff --git a/src/SubstituteNode.ts b/src/SubstituteNode.ts index dfb0be5..4f275b8 100644 --- a/src/SubstituteNode.ts +++ b/src/SubstituteNode.ts @@ -4,16 +4,11 @@ import { SubstituteNodeBase } from './SubstituteNodeBase' import { RecordedArguments } from './RecordedArguments' import { ClearType as ClearTypeMap, PropertyType as PropertyTypeMap, isAssertionMethod, isSubstituteMethod, isSubstitutionMethod, textModifier, isConfigurationMethod } from './Utilities' import { SubstituteException } from './SubstituteException' -import type { FilterFunction, SubstituteContext, SubstitutionMethod, ClearType, PropertyType } from './Types' +import type { FilterFunction, SubstituteContext, SubstitutionMethod, PropertyType } from './Types' import type { ObjectSubstitute } from './Transformations' -import { didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from './Symbols' +import { didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws, clearReceivedCalls } from './Transformations' const instance = Symbol('Substitute:Instance') -const clearTypeToFilterMap: Record> = { - all: () => true, - receivedCalls: node => !node.hasContext, - substituteValues: node => node.isSubstitution -} type SpecialProperty = typeof instance | typeof inspect.custom | 'then' type RootContext = { substituteMethodsEnabled: boolean } @@ -31,35 +26,54 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu private constructor(key: PropertyKey, parent?: SubstituteNode) { super(key, parent) - if (this.isRoot()) this._rootContext = { substituteMethodsEnabled: true } - else this._rootContext = this.root.rootContext + if (this.isRoot()) { + this._rootContext = { substituteMethodsEnabled: true } + } + else { + this._rootContext = this.root.rootContext + } + this._proxy = new Proxy( this, { get: function (target, property) { - if (target.isSpecialProperty(property)) return target.evaluateSpecialProperty(property) - if (target._retrySubstitutionExecutionAttempt) return target.reattemptSubstitutionExecution()[property] + if (target.isSpecialProperty(property)) + return target.evaluateSpecialProperty(property) + + if (target._retrySubstitutionExecutionAttempt) + return target.reattemptSubstitutionExecution()[property] + const newNode = SubstituteNode.createChild(property, target) - if (target.isAssertion) newNode.executeAssertion() + if (target.isAssertion) + newNode.executeAssertion() + if (target.isRoot() && target.rootContext.substituteMethodsEnabled && (isAssertionMethod(property) || isConfigurationMethod(property))) { newNode.assignContext(property) return newNode[property].bind(newNode) } + return newNode.attemptSubstitutionExecution() }, set: function (target, property, value) { const newNode = SubstituteNode.createChild(property, target) newNode.handleSetter(value) - if (target.isAssertion) newNode.executeAssertion() + if (target.isAssertion) + newNode.executeAssertion() + return true }, apply: function (target, _thisArg, rawArguments) { target.handleMethod(rawArguments) if (target.hasDepthOfAtLeast(2)) { - if (isSubstitutionMethod(target.property)) return target.parent.assignContext(target.property) - if (target.parent.isAssertion) return target.executeAssertion() + if (isSubstitutionMethod(target.property)) + return target.parent.assignContext(target.property) + + if (target.parent.isAssertion) + return target.executeAssertion() } - return target.isAssertion ? target.proxy : target.attemptSubstitutionExecution() + return target.isAssertion ? + target.proxy : + target.attemptSubstitutionExecution() } } ) @@ -129,9 +143,10 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu throw new Error('Mimick is not implemented yet') } - public clearSubstitute(clearType: ClearType = ClearTypeMap.All): void { - this.handleMethod([clearType]) - const filter = clearTypeToFilterMap[clearType] + public [clearReceivedCalls](): void { + this.handleMethod([]) + + const filter = (node: SubstituteNode) => !node.hasContext this.recorder.clearRecords(filter) } @@ -140,7 +155,9 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu } private assignContext(context: SubstituteContext): void { - if (!isSubstituteMethod(context)) throw new Error(`Cannot assign context for property ${context.toString()}`) + if (!isSubstituteMethod(context)) + throw new Error(`Cannot assign context for property ${context.toString()}`) + this._context = context } @@ -158,8 +175,11 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu } private executeSubstitution(contextArguments: RecordedArguments) { - if (!this.hasChild()) throw new TypeError('Substitue node has no child') - if (!this.child.recordedArguments.hasArguments()) throw new TypeError('Child args') + if (!this.hasChild()) + throw new TypeError('Substitue node has no child') + + if (!this.child.recordedArguments.hasArguments()) + throw new TypeError('Child args') const substitutionMethod = this.context as SubstitutionMethod const substitutionValue = this.child.recordedArguments.value.length > 1 @@ -168,10 +188,16 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu switch (substitutionMethod) { case throws: throw substitutionValue + case mimicks: - if (this.propertyType === PropertyTypeMap.Property) return substitutionValue() - if (!contextArguments.hasArguments()) throw new TypeError('Context arguments cannot be undefined') + if (this.propertyType === PropertyTypeMap.Property) + return substitutionValue() + + if (!contextArguments.hasArguments()) + throw new TypeError('Context arguments cannot be undefined') + return substitutionValue(...contextArguments.value) + case resolves: return Promise.resolve(substitutionValue) case rejects: @@ -184,8 +210,12 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu } private executeAssertion(): void | never { - if (!this.hasDepthOfAtLeast(2)) throw new Error('Not possible') - if (!this.parent.recordedArguments.hasArguments()) throw new TypeError('Parent args') + if (!this.hasDepthOfAtLeast(2)) + throw new Error('Not possible') + + if (!this.parent.recordedArguments.hasArguments()) + throw new TypeError('Parent args') + const expectedCount = this.parent.recordedArguments.value[0] ?? undefined const finiteExpectation = expectedCount !== undefined if (finiteExpectation && (!Number.isInteger(expectedCount) || expectedCount < 0)) throw new Error('Expected count has to be a positive integer') diff --git a/src/Symbols.ts b/src/Symbols.ts deleted file mode 100644 index 2f2bc0a..0000000 --- a/src/Symbols.ts +++ /dev/null @@ -1,11 +0,0 @@ - -export const received = Symbol('received'); -export const didNotReceive = Symbol('didNotReceive'); -export const mimick = Symbol('mimick'); -export const clearReceivedCalls = Symbol('clearReceivedCalls'); - -export const mimicks = Symbol('mimicks'); -export const throws = Symbol('throws'); -export const returns = Symbol('returns'); -export const resolves = Symbol('resolves'); -export const rejects = Symbol('rejects'); \ No newline at end of file diff --git a/src/Transformations.ts b/src/Transformations.ts index 4432320..7f75ae2 100644 --- a/src/Transformations.ts +++ b/src/Transformations.ts @@ -1,6 +1,5 @@ import type { AllArguments } from './Arguments'; -import { clearReceivedCalls, didNotReceive, mimick, received } from './Symbols'; -import type { ClearType, FirstLevelMethod } from './Types'; +import type { FirstLevelMethod } from './Types'; type FunctionSubstituteWithOverloads = TFunc extends { @@ -91,11 +90,22 @@ type ObjectSubstituteTransformation> = { [P in keyof T]: TryToExpandNonArgumentedFunctionSubstitute & TryToExpandArgumentedFunctionSubstitute & TryToExpandPropertySubstitute; } +export const received = Symbol('received'); +export const didNotReceive = Symbol('didNotReceive'); +export const mimick = Symbol('mimick'); +export const clearReceivedCalls = Symbol('clearReceivedCalls'); + +export const mimicks = Symbol('mimicks'); +export const throws = Symbol('throws'); +export const returns = Symbol('returns'); +export const resolves = Symbol('resolves'); +export const rejects = Symbol('rejects'); + export type OmitProxyMethods = Omit; export type ObjectSubstitute = ObjectSubstituteTransformation & { [received](amount?: number): TerminatingObject; [didNotReceive](): TerminatingObject; [mimick](instance: OmitProxyMethods): void; - [clearReceivedCalls](clearType?: ClearType): void; + [clearReceivedCalls](): void; } -export type DisabledSubstituteObject = T extends ObjectSubstitute ? K : never; +export type DisabledSubstituteObject = T extends ObjectSubstitute ? K : never; \ No newline at end of file diff --git a/src/Types.ts b/src/Types.ts index 77dd1ab..5586edd 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -1,4 +1,4 @@ -import type { clearReceivedCalls, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from "./Symbols" +import type { clearReceivedCalls, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from "./Transformations" export type PropertyType = 'method' | 'property' export type AssertionMethod = typeof received | typeof didNotReceive diff --git a/src/index.ts b/src/index.ts index ac40401..bfb7fef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,6 @@ import { Substitute, SubstituteOf } from './Substitute' export { Arg } from './Arguments' export { Substitute, SubstituteOf } export { ClearType } from './Utilities' -export { clearReceivedCalls, didNotReceive, mimick, received } from './Symbols' +export { clearReceivedCalls, didNotReceive, mimick, received } from './Transformations' export default Substitute \ No newline at end of file From ee5c4fd416f21447ef940b54b6d1016e01647a2d Mon Sep 17 00:00:00 2001 From: Mathias Lorenzen Date: Mon, 11 Mar 2024 09:40:32 +0100 Subject: [PATCH 04/11] feat: symbols for methods - removed Substitute.disableFor --- spec/ClearSubstitute.spec.ts | 3 +- spec/Recorder.spec.ts | 3 +- spec/regression/didNotReceive.spec.ts | 5 +- spec/regression/index.test.ts | 61 ++++++-------- spec/regression/issues/11.test.ts | 6 +- spec/regression/issues/178.test.ts | 18 ++--- spec/regression/issues/23.test.ts | 6 +- spec/regression/issues/36.test.ts | 8 +- spec/regression/issues/45.test.ts | 6 +- spec/regression/issues/59.test.ts | 8 +- spec/regression/mimicks.spec.ts | 19 ++--- spec/regression/received.spec.ts | 110 ++++++++++++------------- spec/regression/rejects.spec.ts | 12 +-- spec/regression/resolves.spec.ts | 12 +-- spec/regression/returns.spec.ts | 56 ++++++------- spec/regression/throws.spec.ts | 14 ++-- src/Substitute.ts | 28 +------ src/SubstituteNode.ts | 111 +++++++++++++++----------- src/Transformations.ts | 37 +++++---- src/Utilities.ts | 14 ++-- src/index.ts | 3 +- 21 files changed, 262 insertions(+), 278 deletions(-) diff --git a/spec/ClearSubstitute.spec.ts b/spec/ClearSubstitute.spec.ts index 899249f..adbb47f 100644 --- a/spec/ClearSubstitute.spec.ts +++ b/spec/ClearSubstitute.spec.ts @@ -2,6 +2,7 @@ import test from 'ava' import { Substitute, SubstituteOf, clearReceivedCalls, received } from '../src' import { SubstituteNode } from '../src/SubstituteNode' +import { returns } from '../src/Transformations' interface Calculator { add(a: number, b: number): number @@ -17,7 +18,7 @@ type InstanceReturningSubstitute = SubstituteOf & { test('clears received calls on a substitute', t => { const calculator = Substitute.for() as InstanceReturningSubstitute calculator.add(1, 1) - calculator.add(1, 1).returns(2) + calculator.add(1, 1)[returns](2) calculator[clearReceivedCalls](); t.is(calculator[SubstituteNode.instance].recorder.records.size, 2) diff --git a/spec/Recorder.spec.ts b/spec/Recorder.spec.ts index 348a0b9..a06fc1c 100644 --- a/spec/Recorder.spec.ts +++ b/spec/Recorder.spec.ts @@ -4,10 +4,11 @@ import { Recorder } from '../src/Recorder' import { RecordsSet } from '../src/RecordsSet' import { Substitute } from '../src/Substitute' import { SubstituteNodeBase } from '../src/SubstituteNodeBase' +import { returns } from '../src/Transformations' const nodeFactory = (key: string) => { const node = Substitute.for() - node.key.returns(key) + node.key[returns](key) return node } diff --git a/spec/regression/didNotReceive.spec.ts b/spec/regression/didNotReceive.spec.ts index 0403257..65f41f6 100644 --- a/spec/regression/didNotReceive.spec.ts +++ b/spec/regression/didNotReceive.spec.ts @@ -1,6 +1,7 @@ import test from 'ava' import { Substitute, Arg, didNotReceive, received } from '../../src' import { SubstituteException } from '../../src/SubstituteException' +import { returns } from '../../src/Transformations' interface Calculator { add(a: number, b: number): number @@ -35,7 +36,7 @@ test('not setting a property correctly asserts the call count', t => { test('not calling a method with mock correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.add(1, 1).returns(2) + calculator.add(1, 1)[returns](2) calculator[didNotReceive]().add(1, 1) t.throws(() => calculator[received](1).add(1, 1), { instanceOf: SubstituteException }) @@ -44,7 +45,7 @@ test('not calling a method with mock correctly asserts the call count', t => { test('not getting a property with mock correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.isEnabled.returns(true) + calculator.isEnabled[returns](true) calculator[didNotReceive]().isEnabled t.throws(() => calculator[received](1).isEnabled, { instanceOf: SubstituteException }) diff --git a/spec/regression/index.test.ts b/spec/regression/index.test.ts index 6a5aff0..241069d 100644 --- a/spec/regression/index.test.ts +++ b/spec/regression/index.test.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, Arg, SubstituteOf } from '../../src' +import { Substitute, Arg, SubstituteOf, received, mimicks, resolves, returns } from '../../src' class Dummy { @@ -47,22 +47,13 @@ function initialize() { const textModifierRegex = /\x1b\[\d+m/g -test('class with method called \'received\' can be used for call count verification when proxies are suspended', t => { +test('class with method called \'received\' can be used for call count verification when using symbols', t => { initialize() - Substitute.disableFor(substitute).received(2) + substitute.received(2) - t.throws(() => substitute.received(2).received(2)) - t.notThrows(() => substitute.received(1).received(2)) -}) - -test('class with method called \'received\' can be used for call count verification', t => { - initialize() - - Substitute.disableFor(substitute).received('foo') - - t.notThrows(() => substitute.received(1).received('foo')) - t.throws(() => substitute.received(2).received('foo')) + t.throws(() => substitute[received](2).received(2)) + t.notThrows(() => substitute[received](1).received(2)) }) test('class string field set received', t => { @@ -79,22 +70,22 @@ test('class string field set received', t => { runLogic(substitute) - t.notThrows(() => substitute.received().v = 'hello') - t.notThrows(() => substitute.received(5).v = Arg.any()) - t.notThrows(() => substitute.received().v = Arg.any()) - t.notThrows(() => substitute.received(2).v = 'hello') - t.notThrows(() => substitute.received(2).v = Arg.is(x => typeof x === 'string' && x.indexOf('ll') > -1)) + t.notThrows(() => substitute[received]().v = 'hello') + t.notThrows(() => substitute[received](5).v = Arg.any()) + t.notThrows(() => substitute[received]().v = Arg.any()) + t.notThrows(() => substitute[received](2).v = 'hello') + t.notThrows(() => substitute[received](2).v = Arg.is(x => typeof x === 'string' && x.indexOf('ll') > -1)) - t.throws(() => substitute.received(2).v = Arg.any()) - t.throws(() => substitute.received(1).v = Arg.any()) - t.throws(() => substitute.received(1).v = Arg.is(x => typeof x === 'string' && x.indexOf('ll') > -1)) - t.throws(() => substitute.received(3).v = 'hello') + t.throws(() => substitute[received](2).v = Arg.any()) + t.throws(() => substitute[received](1).v = Arg.any()) + t.throws(() => substitute[received](1).v = Arg.is(x => typeof x === 'string' && x.indexOf('ll') > -1)) + t.throws(() => substitute[received](3).v = 'hello') }) test('resolving promises works', async t => { initialize() - substitute.returnPromise().resolves(1338) + substitute.returnPromise()[resolves](1338) t.is(1338, await substitute.returnPromise() as number) }) @@ -102,7 +93,7 @@ test('resolving promises works', async t => { test('class void returns', t => { initialize() - substitute.foo().returns(void 0, null) + substitute.foo()[returns](void 0, null) t.is(substitute.foo(), void 0) t.is(substitute.foo(), null) @@ -117,9 +108,9 @@ test('class method received', t => { void substitute.c('hi', 'there') void substitute.c('hi', 'there') - t.notThrows(() => substitute.received(4).c('hi', 'there')) - t.notThrows(() => substitute.received(1).c('hi', 'the1re')) - t.notThrows(() => substitute.received().c('hi', 'there')) + t.notThrows(() => substitute[received](4).c('hi', 'there')) + t.notThrows(() => substitute[received](1).c('hi', 'the1re')) + t.notThrows(() => substitute[received]().c('hi', 'there')) const expectedMessage = 'Expected 7 calls to the method c with arguments [\'hi\', \'there\'], but received 4 of such calls.\n' + 'All calls received to method c:\n' + @@ -128,24 +119,24 @@ test('class method received', t => { '-> call with arguments [\'hi\', \'there\']\n' + '-> call with arguments [\'hi\', \'there\']\n' + '-> call with arguments [\'hi\', \'there\']' - const { message } = t.throws(() => { substitute.received(7).c('hi', 'there') }) + const { message } = t.throws(() => { substitute[received](7).c('hi', 'there') }) t.is(message.replace(textModifierRegex, ''), expectedMessage) }) test('received call matches after partial mocks using property instance mimicks', t => { initialize() - substitute.d.mimicks(() => instance.d) + substitute.d[mimicks](() => instance.d) substitute.c('lala', 'bar') - substitute.received(1).c('lala', 'bar') - substitute.received(1).c('lala', 'bar') + substitute[received](1).c('lala', 'bar') + substitute[received](1).c('lala', 'bar') - t.notThrows(() => substitute.received(1).c('lala', 'bar')) + t.notThrows(() => substitute[received](1).c('lala', 'bar')) const expectedMessage = 'Expected 2 calls to the method c with arguments [\'lala\', \'bar\'], but received 1 of such calls.\n' + 'All calls received to method c:\n' + '-> call with arguments [\'lala\', \'bar\']' - const { message } = t.throws(() => substitute.received(2).c('lala', 'bar')) + const { message } = t.throws(() => substitute[received](2).c('lala', 'bar')) t.is(message.replace(textModifierRegex, ''), expectedMessage) t.deepEqual(substitute.d, 1337) }) @@ -153,7 +144,7 @@ test('received call matches after partial mocks using property instance mimicks' test('partial mocks using property instance mimicks', t => { initialize() - substitute.d.mimicks(() => instance.d) + substitute.d[mimicks](() => instance.d) t.deepEqual(substitute.d, 1337) }) diff --git a/spec/regression/issues/11.test.ts b/spec/regression/issues/11.test.ts index 4bd6d77..127526c 100644 --- a/spec/regression/issues/11.test.ts +++ b/spec/regression/issues/11.test.ts @@ -1,5 +1,5 @@ import test from 'ava' -import { Substitute, Arg } from '../../../src' +import { Substitute, Arg, received, returns } from '../../../src' type Addands = { op1: number @@ -14,12 +14,12 @@ class RealCalculator { test('issue 11: arg.is is only called once', async t => { let mockedCalculator = Substitute.for() - mockedCalculator.add(Arg.any()).returns(4) + mockedCalculator.add(Arg.any())[returns](4) let count = 0 mockedCalculator.add({ op1: 1, op2: 2 }) - mockedCalculator.received(1).add(Arg.is(a => { + mockedCalculator[received](1).add(Arg.is(a => { count++ return a.op1 === 1 && a.op2 === 2 })) diff --git a/spec/regression/issues/178.test.ts b/spec/regression/issues/178.test.ts index dacf607..72e8341 100644 --- a/spec/regression/issues/178.test.ts +++ b/spec/regression/issues/178.test.ts @@ -2,7 +2,7 @@ import test, { ExecutionContext, ThrowsExpectation } from 'ava' import * as fakeTimers from '@sinonjs/fake-timers' import { types } from 'util' -import { Substitute } from '../../../src' +import { Substitute, didNotReceive, received, returns } from '../../../src' import { SubstituteException } from '../../../src/SubstituteException' interface Library { @@ -24,17 +24,17 @@ const throwsUncaughtException = (cb: () => any, t: ExecutionContext, expectation test('can substitute callable interfaces', async t => { const lib = Substitute.for() - lib.subSection().returns('subSection as method') - lib.subSection.returns({ subMethod: () => 'subSection as property' } as Subsection) + lib.subSection()[returns]('subSection as method') + lib.subSection[returns]({ subMethod: () => 'subSection as property' } as Subsection) t.is('subSection as method', lib.subSection()) t.true(types.isProxy(lib.subSection), 'Expected proxy: given the context, it\'s not possible to determine the property type') t.is('subSection as property', lib.subSection.subMethod()) - lib.received().subSection() - lib.received(1).subSection() - lib.received(2).subSection - t.throws(() => lib.didNotReceive().subSection(), { instanceOf: SubstituteException }) - t.throws(() => lib.received(2).subSection(), { instanceOf: SubstituteException }) - throwsUncaughtException(() => lib.received(3).subSection, t, { instanceOf: SubstituteException }) + lib[received]().subSection() + lib[received](1).subSection() + lib[received](2).subSection + t.throws(() => lib[didNotReceive]().subSection(), { instanceOf: SubstituteException }) + t.throws(() => lib[received](2).subSection(), { instanceOf: SubstituteException }) + throwsUncaughtException(() => lib[received](3).subSection, t, { instanceOf: SubstituteException }) }) diff --git a/spec/regression/issues/23.test.ts b/spec/regression/issues/23.test.ts index a26666f..c4fde96 100644 --- a/spec/regression/issues/23.test.ts +++ b/spec/regression/issues/23.test.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, Arg } from '../../../src' +import { Substitute, Arg, received, mimicks } from '../../../src' interface CalculatorInterface { add(a: number, b: number): number @@ -14,12 +14,12 @@ test('issue 23: mimick received should not call method', t => { let calls = 0 - mockedCalculator.add(Arg.all()).mimicks((a, b) => { + mockedCalculator.add(Arg.all())[mimicks]((a, b) => { t.deepEqual(++calls, 1, 'mimick called twice') return a + b }) mockedCalculator.add(1, 1) // ok - mockedCalculator.received(1).add(1, 1) // not ok, calls mimick func + mockedCalculator[received](1).add(1, 1) // not ok, calls mimick func }) diff --git a/spec/regression/issues/36.test.ts b/spec/regression/issues/36.test.ts index 508b3c3..d9c97ee 100644 --- a/spec/regression/issues/36.test.ts +++ b/spec/regression/issues/36.test.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, Arg } from '../../../src' +import { Substitute, Arg, received, returns } from '../../../src' class Key { private constructor(private _value: string) { } @@ -49,7 +49,7 @@ class Service { test('issue 36 - promises returning object with properties', async t => { const emptyFetch = Substitute.for() - emptyFetch.getUpdates(Key.create()).returns(Promise.resolve(IData.create())) + emptyFetch.getUpdates(Key.create())[returns](Promise.resolve(IData.create())) const result = await emptyFetch.getUpdates(Key.create()) t.true(result.serverCheck instanceof Date, 'given date is instanceof Date') t.deepEqual(result.data, [1], 'arrays are deep equal') @@ -58,12 +58,12 @@ test('issue 36 - promises returning object with properties', async t => { test('using objects or classes as arguments should be able to match mock', async t => { const db = Substitute.for() const data = IData.create() - db.getUpdates(Key.create()).returns(Promise.resolve(data)) + db.getUpdates(Key.create())[returns](Promise.resolve(data)) const service = new Service(db) await service.handle(Key.create()) - db.received(1).storeUpdates(Arg.is((arg: IData) => + db[received](1).storeUpdates(Arg.is((arg: IData) => arg.serverCheck instanceof Date && arg instanceof IData && arg.data[0] === 100 diff --git a/spec/regression/issues/45.test.ts b/spec/regression/issues/45.test.ts index e77f985..c9178b8 100644 --- a/spec/regression/issues/45.test.ts +++ b/spec/regression/issues/45.test.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, Arg } from '../../../src' +import { Substitute, Arg, received } from '../../../src' class DependencyClass { public methodOne() { } @@ -30,7 +30,7 @@ test('issue 45 Checking received calls off at times', async t => { subject.callToMethodTwo() t.notThrows(() => { - mock.received(1).methodOne() - mock.received(1).methodTwo(Arg.is(x => x === 'string')) + mock[received](1).methodOne() + mock[received](1).methodTwo(Arg.is(x => x === 'string')) }) }) diff --git a/spec/regression/issues/59.test.ts b/spec/regression/issues/59.test.ts index 36dd296..72dffc5 100644 --- a/spec/regression/issues/59.test.ts +++ b/spec/regression/issues/59.test.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute } from '../../../src' +import { Substitute, received, returns } from '../../../src' interface IEcho { echo(a: string): string @@ -9,10 +9,10 @@ interface IEcho { test('issue 59 - Mock function with optional parameters', (t) => { const echoer = Substitute.for() - echoer.maybeEcho('foo').returns('bar') - echoer.maybeEcho().returns('baz') + echoer.maybeEcho('foo')[returns]('bar') + echoer.maybeEcho()[returns]('baz') t.is('bar', echoer.maybeEcho('foo')) - echoer.received().maybeEcho('foo') + echoer[received]().maybeEcho('foo') t.is('baz', echoer.maybeEcho()) }) diff --git a/spec/regression/mimicks.spec.ts b/spec/regression/mimicks.spec.ts index a6b5b8a..70ceb86 100644 --- a/spec/regression/mimicks.spec.ts +++ b/spec/regression/mimicks.spec.ts @@ -1,5 +1,6 @@ import test from 'ava' import { Substitute, Arg } from '../../src' +import { mimicks } from '../../src/Transformations' interface Calculator { add(a: number, b: number): number @@ -16,7 +17,7 @@ test('mimicks a method with specific arguments', t => { const calculator = Substitute.for() const addMimick = (a: number, b: number) => a + b - calculator.add(1, 1).mimicks(addMimick) + calculator.add(1, 1)[mimicks](addMimick) t.is(2, calculator.add(1, 1)) }) @@ -24,8 +25,8 @@ test('mimicks a method with specific and conditional arguments', t => { const calculator = Substitute.for() const addMimick = (a: number, b: number) => a + b - calculator.add(Arg.any('number'), Arg.is((input: number) => input >= 0 && input <= 10)).mimicks(addMimick) - calculator.add(42, -42).mimicks((a: number, b: number) => 0) + calculator.add(Arg.any('number'), Arg.is((input: number) => input >= 0 && input <= 10))[mimicks](addMimick) + calculator.add(42, -42)[mimicks]((a: number, b: number) => 0) t.is(1240, calculator.add(1234, 6)) t.is(0, calculator.add(42, -42)) @@ -36,7 +37,7 @@ test('mimicks a method with Arg.all', t => { const addMimick = (a: number, b: number) => a + b - calculator.add(Arg.all()).mimicks(addMimick) + calculator.add(Arg.all())[mimicks](addMimick) t.is(100, calculator.add(42, 58)) }) @@ -45,9 +46,9 @@ test('mimicks a method with optional arguments', t => { const multiplyOneArgMimicks = (a: number) => a * a const multiplyMimicks = (a: number, b?: number) => a * (b ?? 0) - calculator.multiply(0, Arg.is((b: number) => b > 10 && b < 20)).mimicks(multiplyMimicks) - calculator.multiply(Arg.any('number'), Arg.is((b: number) => b === 2)).mimicks(multiplyMimicks) - calculator.multiply(2).mimicks(multiplyOneArgMimicks) + calculator.multiply(0, Arg.is((b: number) => b > 10 && b < 20))[mimicks](multiplyMimicks) + calculator.multiply(Arg.any('number'), Arg.is((b: number) => b === 2))[mimicks](multiplyMimicks) + calculator.multiply(2)[mimicks](multiplyOneArgMimicks) t.is(0, calculator.multiply(0, 13)) t.is(84, calculator.multiply(42, 2)) @@ -57,8 +58,8 @@ test('mimicks a method with optional arguments', t => { test('mimicks a method where it\'s only argument is optional', t => { const calculator = Substitute.for() - calculator.viewResult().mimicks(() => 0) - calculator.viewResult(3).mimicks(() => 42) + calculator.viewResult()[mimicks](() => 0) + calculator.viewResult(3)[mimicks](() => 42) t.is(0, calculator.viewResult()) t.is(42, calculator.viewResult(3)) diff --git a/spec/regression/received.spec.ts b/spec/regression/received.spec.ts index c9a4519..ad8633b 100644 --- a/spec/regression/received.spec.ts +++ b/spec/regression/received.spec.ts @@ -1,5 +1,5 @@ import test from 'ava' -import { Substitute, Arg } from '../../src' +import { Substitute, Arg, received, returns } from '../../src' import { SubstituteException } from '../../src/SubstituteException' interface Calculator { @@ -13,24 +13,24 @@ test('calling a method twice correctly asserts the call count', t => { calculator.add(1, 1) calculator.add(1, 1) - calculator.received(2).add(1, 1) - calculator.received().add(1, 1) - t.throws(() => calculator.received(0).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received(1).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received(2).add(1, 0), { instanceOf: SubstituteException }) - t.throws(() => calculator.received().add(1, 0), { instanceOf: SubstituteException }) + calculator[received](2).add(1, 1) + calculator[received]().add(1, 1) + t.throws(() => calculator[received](0).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received](1).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received](2).add(1, 0), { instanceOf: SubstituteException }) + t.throws(() => calculator[received]().add(1, 0), { instanceOf: SubstituteException }) }) test('calling a method twice correctly asserts the call count each time', t => { const calculator = Substitute.for() calculator.add(1, 1) - calculator.received(1).add(1, 1) + calculator[received](1).add(1, 1) calculator.add(1, 1) - calculator.received(2).add(1, 1) + calculator[received](2).add(1, 1) - t.throws(() => calculator.received(1).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received](1).add(1, 1), { instanceOf: SubstituteException }) }) test('calling a method with optional arguments correctly asserts the call count', t => { @@ -39,16 +39,16 @@ test('calling a method with optional arguments correctly asserts the call count' calculator.multiply(1, 1) calculator.multiply(2, 1) - calculator.received(1).multiply(1) - calculator.received(1).multiply(1, 1) - calculator.received(0).multiply(2) - calculator.received(1).multiply(2, 1) + calculator[received](1).multiply(1) + calculator[received](1).multiply(1, 1) + calculator[received](0).multiply(2) + calculator[received](1).multiply(2, 1) - t.throws(() => calculator.received(0).multiply(1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received(0).multiply(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received(2).multiply(1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received(2).multiply(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received(1).multiply(2), { instanceOf: SubstituteException }) + t.throws(() => calculator[received](0).multiply(1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received](0).multiply(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received](2).multiply(1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received](2).multiply(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received](1).multiply(2), { instanceOf: SubstituteException }) }) test('getting a property twice correctly asserts the call count', t => { @@ -56,10 +56,10 @@ test('getting a property twice correctly asserts the call count', t => { calculator.isEnabled calculator.isEnabled - calculator.received(2).isEnabled - calculator.received().isEnabled - t.throws(() => calculator.received(0).isEnabled, { instanceOf: SubstituteException }) - t.throws(() => calculator.received(1).isEnabled, { instanceOf: SubstituteException }) + calculator[received](2).isEnabled + calculator[received]().isEnabled + t.throws(() => calculator[received](0).isEnabled, { instanceOf: SubstituteException }) + t.throws(() => calculator[received](1).isEnabled, { instanceOf: SubstituteException }) }) test('setting a property twice correctly asserts the call count', t => { @@ -70,50 +70,50 @@ test('setting a property twice correctly asserts the call count', t => { } runLogic(calculator) - calculator.received(2).isEnabled = true - calculator.received().isEnabled = true - t.throws(() => calculator.received(0).isEnabled = true, { instanceOf: SubstituteException }) - t.throws(() => calculator.received(1).isEnabled = true, { instanceOf: SubstituteException }) - t.throws(() => calculator.received(2).isEnabled = false, { instanceOf: SubstituteException }) - t.throws(() => calculator.received().isEnabled = false, { instanceOf: SubstituteException }) + calculator[received](2).isEnabled = true + calculator[received]().isEnabled = true + t.throws(() => calculator[received](0).isEnabled = true, { instanceOf: SubstituteException }) + t.throws(() => calculator[received](1).isEnabled = true, { instanceOf: SubstituteException }) + t.throws(() => calculator[received](2).isEnabled = false, { instanceOf: SubstituteException }) + t.throws(() => calculator[received]().isEnabled = false, { instanceOf: SubstituteException }) }) test('calling a method twice with mock correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.add(1, 1).returns(2) + calculator.add(1, 1)[returns](2) calculator.add(1, 1) calculator.add(1, 1) - calculator.received(2).add(1, 1) - calculator.received().add(1, 1) - t.throws(() => calculator.received(0).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received(1).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received(2).add(1, 0), { instanceOf: SubstituteException }) - t.throws(() => calculator.received().add(1, 0), { instanceOf: SubstituteException }) + calculator[received](2).add(1, 1) + calculator[received]().add(1, 1) + t.throws(() => calculator[received](0).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received](1).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received](2).add(1, 0), { instanceOf: SubstituteException }) + t.throws(() => calculator[received]().add(1, 0), { instanceOf: SubstituteException }) }) test('calling a method with mock based on Arg correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.add(Arg.all()).returns(0) + calculator.add(Arg.all())[returns](0) calculator.add(1, 1) - calculator.received().add(Arg.all()) - calculator.received().add(Arg.any(), Arg.any()) - calculator.received().add(Arg.any('number'), Arg.any('number')) - calculator.received().add(1, 1) - t.throws(() => calculator.received().add(1, 0), { instanceOf: SubstituteException }) + calculator[received]().add(Arg.all()) + calculator[received]().add(Arg.any(), Arg.any()) + calculator[received]().add(Arg.any('number'), Arg.any('number')) + calculator[received]().add(1, 1) + t.throws(() => calculator[received]().add(1, 0), { instanceOf: SubstituteException }) }) test('getting a property twice with mock correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.isEnabled.returns(true) + calculator.isEnabled[returns](true) calculator.isEnabled calculator.isEnabled - calculator.received(2).isEnabled - calculator.received().isEnabled - t.throws(() => calculator.received(0).isEnabled, { instanceOf: SubstituteException }) - t.throws(() => calculator.received(1).isEnabled, { instanceOf: SubstituteException }) + calculator[received](2).isEnabled + calculator[received]().isEnabled + t.throws(() => calculator[received](0).isEnabled, { instanceOf: SubstituteException }) + t.throws(() => calculator[received](1).isEnabled, { instanceOf: SubstituteException }) }) test('calling a method with Arg correctly asserts the call count with Arg', t => { @@ -121,9 +121,9 @@ test('calling a method with Arg correctly asserts the call count with Arg', t => const calculator = Substitute.for() calculator.add(Arg.all()) - calculator.received(1).add(Arg.all()) - calculator.received().add(Arg.all()) - t.throws(() => calculator.received(0).add(1, 1), { instanceOf: SubstituteException }) + calculator[received](1).add(Arg.all()) + calculator[received]().add(Arg.all()) + t.throws(() => calculator[received](0).add(1, 1), { instanceOf: SubstituteException }) }) test('calling a method does not interfere with other properties or methods call counts', t => { @@ -131,10 +131,10 @@ test('calling a method does not interfere with other properties or methods call const calculator = Substitute.for() calculator.add(1, 1) - calculator.received(0).multiply(1, 1) - calculator.received(0).multiply(Arg.all()) - calculator.received(0).isEnabled + calculator[received](0).multiply(1, 1) + calculator[received](0).multiply(Arg.all()) + calculator[received](0).isEnabled - t.throws(() => calculator.received().multiply(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator.received().multiply(Arg.all()), { instanceOf: SubstituteException }) + t.throws(() => calculator[received]().multiply(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator[received]().multiply(Arg.all()), { instanceOf: SubstituteException }) }) \ No newline at end of file diff --git a/spec/regression/rejects.spec.ts b/spec/regression/rejects.spec.ts index a92cdbf..ac6872b 100644 --- a/spec/regression/rejects.spec.ts +++ b/spec/regression/rejects.spec.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, Arg } from '../../src' +import { Substitute, Arg, rejects } from '../../src' interface Calculator { getMemory(): Promise @@ -10,21 +10,21 @@ interface Calculator { test('rejects a method with no arguments', async t => { const calculator = Substitute.for() - calculator.getMemory().rejects(new Error('No memory')) + calculator.getMemory()[rejects](new Error('No memory')) await t.throwsAsync(calculator.getMemory(), { instanceOf: Error, message: 'No memory' }) }) test('rejects a method with arguments', async t => { const calculator = Substitute.for() - calculator.heavyOperation(0, 1, 1, 2, 4, 5, 8).rejects(new Error('Wrong sequence!')) + calculator.heavyOperation(0, 1, 1, 2, 4, 5, 8)[rejects](new Error('Wrong sequence!')) await t.throwsAsync(calculator.heavyOperation(0, 1, 1, 2, 4, 5, 8), { instanceOf: Error, message: 'Wrong sequence!' }) }) test('rejects different values in the specified order on a method', async t => { const calculator = Substitute.for() - calculator.heavyOperation(Arg.any('number')).rejects(new Error('Wrong!'), new Error('Wrong again!')) + calculator.heavyOperation(Arg.any('number'))[rejects](new Error('Wrong!'), new Error('Wrong again!')) await t.throwsAsync(calculator.heavyOperation(0), { instanceOf: Error, message: 'Wrong!' }) await t.throwsAsync(calculator.heavyOperation(0), { instanceOf: Error, message: 'Wrong again!' }) @@ -33,14 +33,14 @@ test('rejects different values in the specified order on a method', async t => { test('rejects a property', async t => { const calculator = Substitute.for() - calculator.model.rejects(new Error('No model')) + calculator.model[rejects](new Error('No model')) await t.throwsAsync(calculator.model, { instanceOf: Error, message: 'No model' }) }) test('rejects different values in the specified order on a property', async t => { const calculator = Substitute.for() - calculator.model.rejects(new Error('No model'), new Error('I said "no model"')) + calculator.model[rejects](new Error('No model'), new Error('I said "no model"')) await t.throwsAsync(calculator.model, { instanceOf: Error, message: 'No model' }) await t.throwsAsync(calculator.model, { instanceOf: Error, message: 'I said "no model"' }) diff --git a/spec/regression/resolves.spec.ts b/spec/regression/resolves.spec.ts index 6916d1b..87125cb 100644 --- a/spec/regression/resolves.spec.ts +++ b/spec/regression/resolves.spec.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, Arg } from '../../src' +import { Substitute, Arg, resolves } from '../../src' interface Calculator { getMemory(): Promise @@ -10,21 +10,21 @@ interface Calculator { test('resolves a method with no arguments', async t => { const calculator = Substitute.for() - calculator.getMemory().resolves(0) + calculator.getMemory()[resolves](0) t.is(await calculator.getMemory(), 0) }) test('resolves a method with arguments', async t => { const calculator = Substitute.for() - calculator.heavyOperation(0, 1, 1, 2, 3, 5, 8).resolves(13) + calculator.heavyOperation(0, 1, 1, 2, 3, 5, 8)[resolves](13) t.is(await calculator.heavyOperation(0, 1, 1, 2, 3, 5, 8), 13) }) test('resolves different values in the specified order on a method', async t => { const calculator = Substitute.for() - calculator.heavyOperation(Arg.any('number')).resolves(1, 2, 3) + calculator.heavyOperation(Arg.any('number'))[resolves](1, 2, 3) t.is(await calculator.heavyOperation(0), 1) t.is(await calculator.heavyOperation(0), 2) @@ -34,14 +34,14 @@ test('resolves different values in the specified order on a method', async t => test('resolves a property', async t => { const calculator = Substitute.for() - calculator.model.resolves('Casio FX-82') + calculator.model[resolves]('Casio FX-82') t.is(await calculator.model, 'Casio FX-82') }) test('resolves different values in the specified order on a property', async t => { const calculator = Substitute.for() - calculator.model.resolves('Casio FX-82', 'TI-84 Plus') + calculator.model[resolves]('Casio FX-82', 'TI-84 Plus') t.is(await calculator.model, 'Casio FX-82') t.is(await calculator.model, 'TI-84 Plus') diff --git a/spec/regression/returns.spec.ts b/spec/regression/returns.spec.ts index 54f61a6..d076c04 100644 --- a/spec/regression/returns.spec.ts +++ b/spec/regression/returns.spec.ts @@ -1,7 +1,7 @@ import test from 'ava' import { types } from 'util' -import { Substitute, Arg } from '../../src' +import { Substitute, Arg, returns } from '../../src' interface Calculator { add(a: number, b: number): number @@ -18,7 +18,7 @@ interface Calculator { test('returns a primitive value for method with no arguments', t => { const calculator = Substitute.for() - calculator.clear().returns() + calculator.clear()[returns]() // calculator.add(1, 2).toExponential() // calculator.isEnabled2.viewResult().returns(3) // calculator.other().viewResult().returns(2) @@ -30,7 +30,7 @@ test('returns a primitive value for method with specific arguments', t => { const calculator = Substitute.for() const noResult = calculator.add(1, 1) - calculator.add(1, 1).returns(2) + calculator.add(1, 1)[returns](2) t.is(2, calculator.add(1, 1)) t.is(2, calculator.add(1, 1)) @@ -40,10 +40,10 @@ test('returns a primitive value for method with specific arguments', t => { test('returns a primitive value for method with specific arguments where the last argument is optional', t => { const calculator = Substitute.for() - calculator.multiply(2).returns(4) - calculator.multiply(0, Arg.any('number')).returns(0) - calculator.multiply(1, Arg.any()).returns(10) - calculator.multiply(1).returns(1) + calculator.multiply(2)[returns](4) + calculator.multiply(0, Arg.any('number'))[returns](0) + calculator.multiply(1, Arg.any())[returns](10) + calculator.multiply(1)[returns](1) t.is(4, calculator.multiply(2)) t.is(0, calculator.multiply(0, 10)) @@ -59,10 +59,10 @@ test('returns a primitive value for method with specific arguments where the las test('returns a primitive value for method with specific and conditional arguments', t => { const calculator = Substitute.for() - calculator.add(0, 0).returns(0) - calculator.add(1, Arg.is((input: number) => input === 1)).returns(2) - calculator.add(2, Arg.any('number')).returns(10) - calculator.add(Arg.is((input: number) => input > 2), Arg.any('number')).returns(42) + calculator.add(0, 0)[returns](0) + calculator.add(1, Arg.is((input: number) => input === 1))[returns](2) + calculator.add(2, Arg.any('number'))[returns](10) + calculator.add(Arg.is((input: number) => input > 2), Arg.any('number'))[returns](42) const results = [calculator.add(0, 0), calculator.add(1, 1), calculator.add(2, 100), calculator.add(42, 84)] @@ -72,7 +72,7 @@ test('returns a primitive value for method with specific and conditional argumen test('returns a primitive value for method with Arg.all', t => { // #25: call verification does not work when using Arg.all() to set up return values https://github.com/ffMathy/FluffySpoon.JavaScript.Testing.Faking/issues/25 const calculator = Substitute.for() - calculator.add(Arg.all()).returns(42) + calculator.add(Arg.all())[returns](42) const results = [calculator.add(0, 0), calculator.add(1, 1), calculator.add(2, 100)] @@ -82,8 +82,8 @@ test('returns a primitive value for method with Arg.all', t => { test('returns a primitive value for method with one optional argument', t => { // #24: Mocked method arguments not allowed when verifying method was called https://github.com/ffMathy/FluffySpoon.JavaScript.Testing.Faking/issues/24 const calculator = Substitute.for() - calculator.viewResult().returns(0) - calculator.viewResult(3).returns(123) + calculator.viewResult()[returns](0) + calculator.viewResult(3)[returns](123) t.is(0, calculator.viewResult()) t.is(123, calculator.viewResult(3)) @@ -91,14 +91,14 @@ test('returns a primitive value for method with one optional argument', t => { test('returns a promise for method with no arguments', async t => { const calculator = Substitute.for() - calculator.getMemory().returns(Promise.resolve(42)) + calculator.getMemory()[returns](Promise.resolve(42)) t.is(await calculator.getMemory(), 42) }) test('returns a promise for method with specific arguments', async t => { const calculator = Substitute.for() - calculator.heavyOperation(1, 1).returns(Promise.resolve(true)) + calculator.heavyOperation(1, 1)[returns](Promise.resolve(true)) const result = await calculator.heavyOperation(1, 1) const noResult = calculator.heavyOperation(1, 1, 1) @@ -109,9 +109,9 @@ test('returns a promise for method with specific arguments', async t => { test('returns a promise for method with specific and conditional arguments', async t => { const calculator = Substitute.for() - calculator.heavyOperation(0).returns(Promise.resolve(true)) - calculator.heavyOperation(1, Arg.is((input: number) => input === 1)).returns(Promise.resolve(false)) - calculator.heavyOperation(2, Arg.any('number'), 100).returns(Promise.resolve(true)) + calculator.heavyOperation(0)[returns](Promise.resolve(true)) + calculator.heavyOperation(1, Arg.is((input: number) => input === 1))[returns](Promise.resolve(false)) + calculator.heavyOperation(2, Arg.any('number'), 100)[returns](Promise.resolve(true)) const results = await Promise.all([calculator.heavyOperation(0), calculator.heavyOperation(1, 1), calculator.heavyOperation(2, 4321, 100)]) @@ -120,7 +120,7 @@ test('returns a promise for method with specific and conditional arguments', asy test('returns a promise for method with Arg.all', async t => { const calculator = Substitute.for() - calculator.heavyOperation(Arg.all()).returns(Promise.resolve(true)) + calculator.heavyOperation(Arg.all())[returns](Promise.resolve(true)) const results = await Promise.all([calculator.heavyOperation(0), calculator.heavyOperation(4321, 11, 42, 1234), calculator.heavyOperation(-1, 444)]) @@ -129,7 +129,7 @@ test('returns a promise for method with Arg.all', async t => { test('returns different primitive values in the specified order for method with arguments', t => { const calculator = Substitute.for() - calculator.add(1, Arg.any()).returns(1, NaN) + calculator.add(1, Arg.any())[returns](1, NaN) t.is(1, calculator.add(1, 1)) t.is(NaN, calculator.add(1, 0)) @@ -140,8 +140,8 @@ test('returns different primitive values in the specified order for method with test('returns another substituted instance for method with arguments', t => { const calculator = Substitute.for() const addResult = Substitute.for() - addResult.toLocaleString().returns('What a weird number') - calculator.add(1, Arg.any()).returns(addResult) + addResult.toLocaleString()[returns]('What a weird number') + calculator.add(1, Arg.any())[returns](addResult) const result = calculator.add(1, 1) @@ -154,7 +154,7 @@ test('returns a primitive value on a property', t => { const calculator = Substitute.for() const noResult = calculator.isEnabled - calculator.isEnabled.returns(true) + calculator.isEnabled[returns](true) t.true(calculator.isEnabled) t.true(calculator.isEnabled) @@ -163,14 +163,14 @@ test('returns a primitive value on a property', t => { test('returns a promise on a property', async t => { const calculator = Substitute.for() - calculator.model.returns(Promise.resolve('Casio FX-82')) + calculator.model[returns](Promise.resolve('Casio FX-82')) t.is(await calculator.model, 'Casio FX-82') }) test('returns different primitive values in the specified order on a property', t => { const calculator = Substitute.for() - calculator.isEnabled.returns(false, true) + calculator.isEnabled[returns](false, true) t.is(false, calculator.isEnabled) t.is(true, calculator.isEnabled) @@ -180,8 +180,8 @@ test('returns different primitive values in the specified order on a property', test('returns another substituted instance on a property', async t => { const calculator = Substitute.for() const modelResult = Substitute.for() - modelResult.replace(Arg.all()).returns('TI-83') - calculator.model.returns(Promise.resolve(modelResult)) + modelResult.replace(Arg.all())[returns]('TI-83') + calculator.model[returns](Promise.resolve(modelResult)) const result = await calculator.model t.is(result, modelResult) diff --git a/spec/regression/throws.spec.ts b/spec/regression/throws.spec.ts index 80d3c21..1396b29 100644 --- a/spec/regression/throws.spec.ts +++ b/spec/regression/throws.spec.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, Arg } from '../../src' +import { Substitute, Arg, returns, throws } from '../../src' interface Calculator { add(a: number, b: number): number @@ -12,22 +12,22 @@ interface Calculator { test('throws on a method with arguments', t => { const calculator = Substitute.for() - calculator.divide(Arg.any(), 0).throws(new Error('Cannot divide by 0')) + calculator.divide(Arg.any(), 0)[throws](new Error('Cannot divide by 0')) t.throws(() => calculator.divide(1, 0), { instanceOf: Error, message: 'Cannot divide by 0' }) }) test('throws on a property being called', t => { const calculator = Substitute.for() - calculator.mode.throws(new Error('Property not set')) + calculator.mode[throws](new Error('Property not set')) t.throws(() => calculator.mode, { instanceOf: Error, message: 'Property not set' }) }) test('does not throw on methods that do not match arguments', t => { const calculator = Substitute.for() - calculator.divide(Arg.any(), 0).throws(new Error('Cannot divide by 0')) - calculator.divide(4, 2).returns(2) + calculator.divide(Arg.any(), 0)[throws](new Error('Cannot divide by 0')) + calculator.divide(4, 2)[returns](2) t.is(2, calculator.divide(4, 2)) t.throws(() => calculator.divide(1, 0), { instanceOf: Error, message: 'Cannot divide by 0' }) @@ -35,8 +35,8 @@ test('does not throw on methods that do not match arguments', t => { test('can set multiple throws for same method with different arguments', t => { const calculator = Substitute.for() - calculator.divide(Arg.any(), 0).throws(new Error('Cannot divide by 0')) - calculator.divide(Arg.any(), Arg.is(number => !Number.isInteger(number))).throws(new Error('Only integers supported')) + calculator.divide(Arg.any(), 0)[throws](new Error('Cannot divide by 0')) + calculator.divide(Arg.any(), Arg.is(number => !Number.isInteger(number)))[throws](new Error('Only integers supported')) t.throws(() => calculator.divide(1, 1.135), { instanceOf: Error, message: 'Only integers supported' }) t.throws(() => calculator.divide(1, 0), { instanceOf: Error, message: 'Cannot divide by 0' }) diff --git a/src/Substitute.ts b/src/Substitute.ts index 323495f..c604436 100644 --- a/src/Substitute.ts +++ b/src/Substitute.ts @@ -1,4 +1,4 @@ -import { DisabledSubstituteObject, ObjectSubstitute } from './Transformations' +import { ObjectSubstitute } from './Transformations' import { SubstituteNode } from './SubstituteNode' export type SubstituteOf = ObjectSubstitute & T @@ -11,32 +11,6 @@ export class Substitute { return substitute.proxy as unknown as SubstituteOf } - public static disableFor>(substituteProxy: T): DisabledSubstituteObject { - const substitute = this.extractSubstituteNodeFromSubstitute(substituteProxy as InstantiableSubstitute) - - const disableProxy = < - TParameters extends unknown[], - TReturnType extends unknown - >(reflection: (...args: TParameters) => TReturnType): typeof reflection => (...args) => { - substitute.rootContext.substituteMethodsEnabled = false - const reflectionResult = reflection(...args) - substitute.rootContext.substituteMethodsEnabled = true - return reflectionResult - } - - return new Proxy(substitute.proxy, { - get: function (target, property) { - return disableProxy(Reflect.get)(target, property) - }, - set: function (target, property, value) { - return disableProxy(Reflect.set)(target, property, value) - }, - apply: function (target, _, args) { - return disableProxy(Reflect.apply)(target, _, args) - } - }) as DisabledSubstituteObject - } - private static extractSubstituteNodeFromSubstitute(substitute: InstantiableSubstitute>): SubstituteNode { return substitute[SubstituteNode.instance] } diff --git a/src/SubstituteNode.ts b/src/SubstituteNode.ts index 4f275b8..49cbdf3 100644 --- a/src/SubstituteNode.ts +++ b/src/SubstituteNode.ts @@ -2,9 +2,9 @@ import { inspect, InspectOptions, types } from 'util' import { SubstituteNodeBase } from './SubstituteNodeBase' import { RecordedArguments } from './RecordedArguments' -import { ClearType as ClearTypeMap, PropertyType as PropertyTypeMap, isAssertionMethod, isSubstituteMethod, isSubstitutionMethod, textModifier, isConfigurationMethod } from './Utilities' +import { PropertyType as PropertyTypeMap, isAssertionMethod, isSubstituteMethod, isSubstitutionMethod, textModifier, isConfigurationMethod } from './Utilities' import { SubstituteException } from './SubstituteException' -import type { FilterFunction, SubstituteContext, SubstitutionMethod, PropertyType } from './Types' +import type { SubstituteContext, SubstitutionMethod, PropertyType } from './Types' import type { ObjectSubstitute } from './Transformations' import { didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws, clearReceivedCalls } from './Transformations' @@ -37,14 +37,14 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu this, { get: function (target, property) { - if (target.isSpecialProperty(property)) + if (target.isSpecialProperty(property)) return target.evaluateSpecialProperty(property) - if (target._retrySubstitutionExecutionAttempt) + if (target._retrySubstitutionExecutionAttempt) return target.reattemptSubstitutionExecution()[property] const newNode = SubstituteNode.createChild(property, target) - if (target.isAssertion) + if (target.isAssertion) newNode.executeAssertion() if (target.isRoot() && target.rootContext.substituteMethodsEnabled && (isAssertionMethod(property) || isConfigurationMethod(property))) { @@ -57,7 +57,7 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu set: function (target, property, value) { const newNode = SubstituteNode.createChild(property, target) newNode.handleSetter(value) - if (target.isAssertion) + if (target.isAssertion) newNode.executeAssertion() return true @@ -65,14 +65,14 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu apply: function (target, _thisArg, rawArguments) { target.handleMethod(rawArguments) if (target.hasDepthOfAtLeast(2)) { - if (isSubstitutionMethod(target.property)) + if (isSubstitutionMethod(target.property)) return target.parent.assignContext(target.property) - if (target.parent.isAssertion) + if (target.parent.isAssertion) return target.executeAssertion() } - return target.isAssertion ? - target.proxy : + return target.isAssertion ? + target.proxy : target.attemptSubstitutionExecution() } } @@ -155,7 +155,7 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu } private assignContext(context: SubstituteContext): void { - if (!isSubstituteMethod(context)) + if (!isSubstituteMethod(context)) throw new Error(`Cannot assign context for property ${context.toString()}`) this._context = context @@ -175,50 +175,57 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu } private executeSubstitution(contextArguments: RecordedArguments) { - if (!this.hasChild()) + if (!this.hasChild()) throw new TypeError('Substitue node has no child') - if (!this.child.recordedArguments.hasArguments()) + if (!this.child.recordedArguments.hasArguments()) throw new TypeError('Child args') const substitutionMethod = this.context as SubstitutionMethod const substitutionValue = this.child.recordedArguments.value.length > 1 ? this.child.recordedArguments.value?.shift() : this.child.recordedArguments.value[0] - switch (substitutionMethod) { - case throws: - throw substitutionValue - case mimicks: - if (this.propertyType === PropertyTypeMap.Property) - return substitutionValue() + if (substitutionMethod === throws) { + throw substitutionValue; + } - if (!contextArguments.hasArguments()) - throw new TypeError('Context arguments cannot be undefined') + if (substitutionMethod === mimicks) { + if (this.propertyType === PropertyTypeMap.Property) + return substitutionValue() - return substitutionValue(...contextArguments.value) + if (!contextArguments.hasArguments()) + throw new TypeError('Context arguments cannot be undefined') - case resolves: - return Promise.resolve(substitutionValue) - case rejects: - return Promise.reject(substitutionValue) - case returns: - return substitutionValue - default: - throw SubstituteException.generic(`Substitution method '${substitutionMethod}' not implemented`) + return substitutionValue(...contextArguments.value) + } + + if (substitutionMethod === resolves) { + return Promise.resolve(substitutionValue) + } + + if (substitutionMethod === rejects) { + return Promise.reject(substitutionValue) } + + if (substitutionMethod === returns) { + return substitutionValue + } + + throw SubstituteException.generic(`Substitution method '${substitutionMethod}' not implemented. Make sure you invoke ".returns(...)" or any other configuration call on the given method.`) } private executeAssertion(): void | never { - if (!this.hasDepthOfAtLeast(2)) + if (!this.hasDepthOfAtLeast(2)) throw new Error('Not possible') - if (!this.parent.recordedArguments.hasArguments()) + if (!this.parent.recordedArguments.hasArguments()) throw new TypeError('Parent args') const expectedCount = this.parent.recordedArguments.value[0] ?? undefined const finiteExpectation = expectedCount !== undefined - if (finiteExpectation && (!Number.isInteger(expectedCount) || expectedCount < 0)) throw new Error('Expected count has to be a positive integer') + if (finiteExpectation && (!Number.isInteger(expectedCount) || expectedCount < 0)) + throw new Error('Expected count has to be a positive integer') const siblings = [...this.getAllSiblings().filter(n => !n.hasContext && n.accessorType === this.accessorType)] const hasBeenCalled = siblings.length > 0 @@ -228,25 +235,33 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu if ( !hasBeenCalled && (!finiteExpectation || expectedCount > 0) - ) throw SubstituteException.forCallCountMissMatch( // Here we don't know here if it's a property or method, so we should throw something more generic - { expected: expectedCount, received: 0 }, - { type: this.propertyType, value: this.property }, - { expected: this.recordedArguments, received: allRecordedArguments } - ) + ) { + throw SubstituteException.forCallCountMissMatch( // Here we don't know here if it's a property or method, so we should throw something more generic + { expected: expectedCount, received: 0 }, + { type: this.propertyType, value: this.property }, + { expected: this.recordedArguments, received: allRecordedArguments } + ) + } if (!hasBeenCalled || hasSiblingOfSamePropertyType) { this._scheduledAssertionException = undefined const actualCount = allRecordedArguments.filter(r => r.match(this.recordedArguments)).length const matchedExpectation = (!finiteExpectation && actualCount > 0) || expectedCount === actualCount - if (matchedExpectation) return + if (matchedExpectation) + return + const exception = SubstituteException.forCallCountMissMatch( { expected: expectedCount, received: actualCount }, { type: this.propertyType, value: this.property }, { expected: this.recordedArguments, received: allRecordedArguments } ) const potentialMethodAssertion = this.propertyType === PropertyTypeMap.Property && siblings.some(sibling => sibling.propertyType === PropertyTypeMap.Method) - if (potentialMethodAssertion) this.schedulePropertyAssertionException(exception) - else throw exception + if (potentialMethodAssertion) { + this.schedulePropertyAssertionException(exception) + } + else { + throw exception + } } } @@ -283,8 +298,11 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu const potentialSuitableSubstitutions = [...potentialSuitableSubstitutionsSet] const hasSuitableSubstitutions = strictSuitableSubstitutions.length > 0 const onlySubstitutionsWithThisNodePropertyType = potentialSuitableSubstitutions.length === 0 - if (onlySubstitutionsWithThisNodePropertyType && hasSuitableSubstitutions) return RecordedArguments.sort(strictSuitableSubstitutions)[0] - if (!onlySubstitutionsWithThisNodePropertyType) this._retrySubstitutionExecutionAttempt = true + if (onlySubstitutionsWithThisNodePropertyType && hasSuitableSubstitutions) + return RecordedArguments.sort(strictSuitableSubstitutions)[0] + + if (!onlySubstitutionsWithThisNodePropertyType) + this._retrySubstitutionExecutionAttempt = true } private isSpecialProperty(property: PropertyKey): property is SpecialProperty { @@ -295,12 +313,15 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu switch (property) { case SubstituteNode.instance: return this + case inspect.custom: return this.printableForm.bind(this) + case 'then': return + default: - throw SubstituteException.generic(`Evaluation of special property ${property} is not implemented`) + throw SubstituteException.generic(`Evaluation of special property ${property} is not implemented.`) } } @@ -311,7 +332,7 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu private printRootNode(options: InspectOptions): string { const records = inspect(this.recorder, options) const instanceName = '*Substitute' // Substitute - return instanceName + ' {' + records + '\n}' + return instanceName + ' {' + records + '}\n' } private printNode(options: InspectOptions): string { diff --git a/src/Transformations.ts b/src/Transformations.ts index 7f75ae2..74291dd 100644 --- a/src/Transformations.ts +++ b/src/Transformations.ts @@ -1,6 +1,17 @@ import type { AllArguments } from './Arguments'; import type { FirstLevelMethod } from './Types'; +export const received = Symbol('received'); +export const didNotReceive = Symbol('didNotReceive'); +export const mimick = Symbol('mimick'); +export const clearReceivedCalls = Symbol('clearReceivedCalls'); + +export const mimicks = Symbol('mimicks'); +export const throws = Symbol('throws'); +export const returns = Symbol('returns'); +export const resolves = Symbol('resolves'); +export const rejects = Symbol('rejects'); + type FunctionSubstituteWithOverloads = TFunc extends { (...args: infer A1): infer R1; @@ -50,21 +61,21 @@ export type PropertySubstitute = TReturnType & NoArgumentMockObject type OneArgumentRequiredFunction = (requiredInput: TArgs, ...restInputs: TArgs[]) => TReturnType; type MockObjectPromise = TReturnType extends Promise ? { - resolves: OneArgumentRequiredFunction; - rejects: OneArgumentRequiredFunction; + [resolves]: OneArgumentRequiredFunction; + [rejects]: OneArgumentRequiredFunction; } : {} type BaseMockObjectMixin = MockObjectPromise & { - returns: OneArgumentRequiredFunction; - throws: OneArgumentRequiredFunction; + [returns]: OneArgumentRequiredFunction; + [throws]: OneArgumentRequiredFunction; } type NoArgumentMockObjectMixin = BaseMockObjectMixin & { - mimicks: OneArgumentRequiredFunction<() => TReturnType, void>; + [mimicks]: OneArgumentRequiredFunction<() => TReturnType, void>; } type MockObjectMixin = BaseMockObjectMixin & { - mimicks: OneArgumentRequiredFunction<(...args: TArguments) => TReturnType, void>; + [mimicks]: OneArgumentRequiredFunction<(...args: TArguments) => TReturnType, void>; } type TerminatingFunction = ((...args: TArguments) => void) & ((arg: AllArguments) => void) @@ -90,22 +101,10 @@ type ObjectSubstituteTransformation> = { [P in keyof T]: TryToExpandNonArgumentedFunctionSubstitute & TryToExpandArgumentedFunctionSubstitute & TryToExpandPropertySubstitute; } -export const received = Symbol('received'); -export const didNotReceive = Symbol('didNotReceive'); -export const mimick = Symbol('mimick'); -export const clearReceivedCalls = Symbol('clearReceivedCalls'); - -export const mimicks = Symbol('mimicks'); -export const throws = Symbol('throws'); -export const returns = Symbol('returns'); -export const resolves = Symbol('resolves'); -export const rejects = Symbol('rejects'); - export type OmitProxyMethods = Omit; export type ObjectSubstitute = ObjectSubstituteTransformation & { [received](amount?: number): TerminatingObject; [didNotReceive](): TerminatingObject; [mimick](instance: OmitProxyMethods): void; [clearReceivedCalls](): void; -} -export type DisabledSubstituteObject = T extends ObjectSubstitute ? K : never; \ No newline at end of file +} \ No newline at end of file diff --git a/src/Utilities.ts b/src/Utilities.ts index 87414f4..ade6596 100644 --- a/src/Utilities.ts +++ b/src/Utilities.ts @@ -1,6 +1,7 @@ import { inspect } from 'util' import { RecordedArguments } from './RecordedArguments' import type { AssertionMethod, ConfigurationMethod, SubstituteMethod, SubstitutionMethod } from './Types' +import { clearReceivedCalls, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from './Transformations' export const PropertyType = { Method: 'method', @@ -8,22 +9,17 @@ export const PropertyType = { } as const export const isAssertionMethod = (property: PropertyKey): property is AssertionMethod => - property === 'received' || property === 'didNotReceive' + property === received || property === didNotReceive -export const isConfigurationMethod = (property: PropertyKey): property is ConfigurationMethod => property === 'clearSubstitute' || property === 'mimick' +export const isConfigurationMethod = (property: PropertyKey): property is ConfigurationMethod => + property === clearReceivedCalls || property === mimick export const isSubstitutionMethod = (property: PropertyKey): property is SubstitutionMethod => - property === 'mimicks' || property === 'returns' || property === 'throws' || property === 'resolves' || property === 'rejects' + property === mimicks || property === returns || property === throws || property === resolves || property === rejects export const isSubstituteMethod = (property: PropertyKey): property is SubstituteMethod => isSubstitutionMethod(property) || isConfigurationMethod(property) || isAssertionMethod(property) -export const ClearType = { - All: 'all', - ReceivedCalls: 'receivedCalls', - SubstituteValues: 'substituteValues' -} as const - export const stringifyArguments = (args: RecordedArguments) => textModifier.faint( args.hasArguments() ? `arguments [${args.value.map(x => inspect(x, { colors: true })).join(', ')}]` : diff --git a/src/index.ts b/src/index.ts index bfb7fef..0fc5f52 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,6 @@ import { Substitute, SubstituteOf } from './Substitute' export { Arg } from './Arguments' export { Substitute, SubstituteOf } -export { ClearType } from './Utilities' -export { clearReceivedCalls, didNotReceive, mimick, received } from './Transformations' +export { clearReceivedCalls, didNotReceive, mimick, received, mimicks, rejects, resolves, returns, throws } from './Transformations' export default Substitute \ No newline at end of file From 69b73af60940fbef3f62bfe01daa22ff18fc0276 Mon Sep 17 00:00:00 2001 From: Mathias Lorenzen Date: Mon, 11 Mar 2024 09:42:48 +0100 Subject: [PATCH 05/11] refactor: import path alignment --- spec/ClearSubstitute.spec.ts | 3 +-- spec/Recorder.spec.ts | 2 +- spec/regression/didNotReceive.spec.ts | 3 +-- spec/regression/mimicks.spec.ts | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/spec/ClearSubstitute.spec.ts b/spec/ClearSubstitute.spec.ts index adbb47f..4c85557 100644 --- a/spec/ClearSubstitute.spec.ts +++ b/spec/ClearSubstitute.spec.ts @@ -1,8 +1,7 @@ import test from 'ava' -import { Substitute, SubstituteOf, clearReceivedCalls, received } from '../src' +import { Substitute, SubstituteOf, clearReceivedCalls, received, returns } from '../src' import { SubstituteNode } from '../src/SubstituteNode' -import { returns } from '../src/Transformations' interface Calculator { add(a: number, b: number): number diff --git a/spec/Recorder.spec.ts b/spec/Recorder.spec.ts index a06fc1c..14a96f0 100644 --- a/spec/Recorder.spec.ts +++ b/spec/Recorder.spec.ts @@ -4,7 +4,7 @@ import { Recorder } from '../src/Recorder' import { RecordsSet } from '../src/RecordsSet' import { Substitute } from '../src/Substitute' import { SubstituteNodeBase } from '../src/SubstituteNodeBase' -import { returns } from '../src/Transformations' +import { returns } from '../src' const nodeFactory = (key: string) => { const node = Substitute.for() diff --git a/spec/regression/didNotReceive.spec.ts b/spec/regression/didNotReceive.spec.ts index 65f41f6..3eaad21 100644 --- a/spec/regression/didNotReceive.spec.ts +++ b/spec/regression/didNotReceive.spec.ts @@ -1,7 +1,6 @@ import test from 'ava' -import { Substitute, Arg, didNotReceive, received } from '../../src' +import { Substitute, Arg, didNotReceive, received, returns } from '../../src' import { SubstituteException } from '../../src/SubstituteException' -import { returns } from '../../src/Transformations' interface Calculator { add(a: number, b: number): number diff --git a/spec/regression/mimicks.spec.ts b/spec/regression/mimicks.spec.ts index 70ceb86..4805344 100644 --- a/spec/regression/mimicks.spec.ts +++ b/spec/regression/mimicks.spec.ts @@ -1,6 +1,5 @@ import test from 'ava' -import { Substitute, Arg } from '../../src' -import { mimicks } from '../../src/Transformations' +import { Substitute, Arg, mimicks } from '../../src' interface Calculator { add(a: number, b: number): number From 240682c7d28cdc9541e30e45ef56842a6b51598e Mon Sep 17 00:00:00 2001 From: Mathias Lorenzen Date: Mon, 11 Mar 2024 13:13:05 +0100 Subject: [PATCH 06/11] refactor: symbols only when needed --- package-lock.json | 1553 ++++++++++++++++++++++++- package.json | 2 +- spec/ClearSubstitute.spec.ts | 14 +- spec/Recorder.spec.ts | 2 +- spec/regression/didNotReceive.spec.ts | 34 +- spec/regression/index.test.ts | 20 +- spec/regression/issues/11.test.ts | 4 +- spec/regression/issues/178.test.ts | 16 +- spec/regression/issues/23.test.ts | 4 +- spec/regression/issues/36.test.ts | 6 +- spec/regression/issues/45.test.ts | 4 +- spec/regression/issues/59.test.ts | 6 +- spec/regression/mimicks.spec.ts | 18 +- spec/regression/received.spec.ts | 108 +- spec/regression/rejects.spec.ts | 10 +- spec/regression/resolves.spec.ts | 10 +- spec/regression/returns.spec.ts | 57 +- spec/regression/throws.spec.ts | 12 +- src/Substitute.ts | 6 - src/SubstituteNode.ts | 83 +- src/Transformations.ts | 130 ++- src/Types.ts | 22 +- src/Utilities.ts | 64 +- 23 files changed, 1938 insertions(+), 247 deletions(-) diff --git a/package-lock.json b/package-lock.json index 469b091..62ec608 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@types/node": "^12.20.55", "@types/sinonjs__fake-timers": "^8.1.5", "ava": "^4.3.3", - "typescript": "^4.8.4" + "typescript": "^5.4.2" }, "engines": { "node": ">=10" @@ -1975,16 +1975,16 @@ } }, "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/well-known-symbols": { @@ -2216,5 +2216,1548 @@ "url": "https://github.com/sponsors/sindresorhus" } } + }, + "dependencies": { + "@ava/typescript": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ava/typescript/-/typescript-3.0.1.tgz", + "integrity": "sha512-/JXIUuKsvkaneaiA9ckk3ksFTqvu0mDNlChASrTe2BnDsvMbhQdPWyqQjJ9WRJWVhhs5TWn1/0Pp1G6Rv8Syrw==", + "dev": true, + "requires": { + "escape-string-regexp": "^5.0.0", + "execa": "^5.1.1" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + }, + "@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.1.tgz", + "integrity": "sha512-qDOv24WjnYuL+wbwHdlsYZFy+cgPtrYw0Tn7GLORicQp9BkQLzrgI3Pm4VyR9ERZ41YTn7KlMPuL1n05WdZvmg==", + "dev": true + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "arrgv": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", + "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", + "dev": true + }, + "arrify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", + "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", + "dev": true + }, + "ava": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/ava/-/ava-4.3.3.tgz", + "integrity": "sha512-9Egq/d9R74ExrWohHeqUlexjDbgZJX5jA1Wq4KCTqc3wIfpGEK79zVy4rBtofJ9YKIxs4PzhJ8BgbW5PlAYe6w==", + "dev": true, + "requires": { + "acorn": "^8.7.1", + "acorn-walk": "^8.2.0", + "ansi-styles": "^6.1.0", + "arrgv": "^1.0.2", + "arrify": "^3.0.0", + "callsites": "^4.0.0", + "cbor": "^8.1.0", + "chalk": "^5.0.1", + "chokidar": "^3.5.3", + "chunkd": "^2.0.1", + "ci-info": "^3.3.1", + "ci-parallel-vars": "^1.0.1", + "clean-yaml-object": "^0.1.0", + "cli-truncate": "^3.1.0", + "code-excerpt": "^4.0.0", + "common-path-prefix": "^3.0.0", + "concordance": "^5.0.4", + "currently-unhandled": "^0.4.1", + "debug": "^4.3.4", + "del": "^6.1.1", + "emittery": "^0.11.0", + "figures": "^4.0.1", + "globby": "^13.1.1", + "ignore-by-default": "^2.1.0", + "indent-string": "^5.0.0", + "is-error": "^2.2.2", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "matcher": "^5.0.0", + "mem": "^9.0.2", + "ms": "^2.1.3", + "p-event": "^5.0.1", + "p-map": "^5.4.0", + "picomatch": "^2.3.1", + "pkg-conf": "^4.0.0", + "plur": "^5.1.0", + "pretty-ms": "^7.0.1", + "resolve-cwd": "^3.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.5", + "strip-ansi": "^7.0.1", + "supertap": "^3.0.1", + "temp-dir": "^2.0.0", + "write-file-atomic": "^4.0.1", + "yargs": "^17.5.1" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "callsites": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.0.0.tgz", + "integrity": "sha512-y3jRROutgpKdz5vzEhWM34TidDU8vkJppF8dszITeb1PQmSqV3DTxyV8G/lyO/DNvtE1YTedehmw9MPZsCBHxQ==", + "dev": true + }, + "cbor": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", + "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", + "dev": true, + "requires": { + "nofilter": "^3.1.0" + } + }, + "chalk": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.0.tgz", + "integrity": "sha512-56zD4khRTBoIyzUYAFgDDaPhUMN/fC/rySe6aZGqbj/VWiU2eI3l6ZLOtYGFZAV5v02mwPjtpzlrOveJiz5eZQ==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chunkd": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", + "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", + "dev": true + }, + "ci-info": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", + "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", + "dev": true + }, + "ci-parallel-vars": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", + "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "clean-yaml-object": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/clean-yaml-object/-/clean-yaml-object-0.1.0.tgz", + "integrity": "sha512-3yONmlN9CSAkzNwnRCiJQ7Q2xK5mWuEfL3PuTZcAUzhObbXsfsnMptJzXwz93nc5zn9V9TwCVMmV7w4xsm43dw==", + "dev": true + }, + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "requires": { + "convert-to-spaces": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "concordance": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "requires": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + } + }, + "convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "requires": { + "time-zone": "^1.0.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "emittery": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.11.0.tgz", + "integrity": "sha512-S/7tzL6v5i+4iJd627Nhv9cLFIo5weAIlGccqJFpnBoDB8U1TF2k5tez4J/QNuxyyhWuFqHg1L84Kd3m7iXg6g==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "figures": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/figures/-/figures-4.0.1.tgz", + "integrity": "sha512-rElJwkA/xS04Vfg+CaZodpso7VqBknOYbzi6I76hI4X80RUjkSxO2oAyPmGbuXUppywjqndOrQDl817hDnI++w==", + "dev": true, + "requires": { + "escape-string-regexp": "^5.0.0", + "is-unicode-supported": "^1.2.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "requires": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "dependencies": { + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "ignore-by-default": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", + "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "irregular-plurals": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.3.0.tgz", + "integrity": "sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-error": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz", + "integrity": "sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "load-json-file": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", + "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", + "dev": true + }, + "locate-path": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.1.1.tgz", + "integrity": "sha512-vJXaRMJgRVD3+cUZs3Mncj2mxpt5mP0EmNOsxRSZRMlbqjvxzDEOIUWXGmavo0ZC9+tNZCBLQ66reA11nbpHZg==", + "dev": true, + "requires": { + "p-locate": "^6.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "matcher": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", + "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", + "dev": true, + "requires": { + "escape-string-regexp": "^5.0.0" + } + }, + "md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "requires": { + "blueimp-md5": "^2.10.0" + } + }, + "mem": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mem/-/mem-9.0.2.tgz", + "integrity": "sha512-F2t4YIv9XQUBHt6AOJ0y7lSmP1+cY7Fm1DRh9GClTGzKST7UWLMx6ly9WZdLH/G/ppM5RL4MlQfRT71ri9t19A==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^4.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", + "dev": true + }, + "p-event": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz", + "integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==", + "dev": true, + "requires": { + "p-timeout": "^5.0.2" + } + }, + "p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "requires": { + "yocto-queue": "^1.0.0" + } + }, + "p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "requires": { + "p-limit": "^4.0.0" + } + }, + "p-map": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", + "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", + "dev": true, + "requires": { + "aggregate-error": "^4.0.0" + }, + "dependencies": { + "aggregate-error": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", + "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "dev": true, + "requires": { + "clean-stack": "^4.0.0", + "indent-string": "^5.0.0" + } + }, + "clean-stack": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", + "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "dev": true, + "requires": { + "escape-string-regexp": "5.0.0" + } + } + } + }, + "p-timeout": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", + "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", + "dev": true + }, + "parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "dev": true + }, + "path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pkg-conf": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-4.0.0.tgz", + "integrity": "sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w==", + "dev": true, + "requires": { + "find-up": "^6.0.0", + "load-json-file": "^7.0.0" + } + }, + "plur": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", + "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", + "dev": true, + "requires": { + "irregular-plurals": "^3.3.0" + } + }, + "pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "dev": true, + "requires": { + "parse-ms": "^2.1.0" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "requires": { + "type-fest": "^0.13.1" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "supertap": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", + "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", + "dev": true, + "requires": { + "indent-string": "^5.0.0", + "js-yaml": "^3.14.1", + "serialize-error": "^7.0.1", + "strip-ansi": "^7.0.1" + } + }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true + }, + "time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true + }, + "typescript": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "dev": true + }, + "well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", + "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true + } } } diff --git a/package.json b/package.json index 737ac2a..32905e0 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,6 @@ "@types/node": "^12.20.55", "@types/sinonjs__fake-timers": "^8.1.5", "ava": "^4.3.3", - "typescript": "^4.8.4" + "typescript": "^5.4.2" } } diff --git a/spec/ClearSubstitute.spec.ts b/spec/ClearSubstitute.spec.ts index 4c85557..f16339d 100644 --- a/spec/ClearSubstitute.spec.ts +++ b/spec/ClearSubstitute.spec.ts @@ -1,7 +1,7 @@ import test from 'ava' import { Substitute, SubstituteOf, clearReceivedCalls, received, returns } from '../src' -import { SubstituteNode } from '../src/SubstituteNode' +import { SubstituteNode, instance } from '../src/SubstituteNode' interface Calculator { add(a: number, b: number): number @@ -11,18 +11,18 @@ interface Calculator { } type InstanceReturningSubstitute = SubstituteOf & { - [SubstituteNode.instance]: SubstituteNode + [instance]: SubstituteNode } test('clears received calls on a substitute', t => { const calculator = Substitute.for() as InstanceReturningSubstitute calculator.add(1, 1) - calculator.add(1, 1)[returns](2) - calculator[clearReceivedCalls](); + calculator.add(1, 1).returns(2) + calculator.clearReceivedCalls(); - t.is(calculator[SubstituteNode.instance].recorder.records.size, 2) - t.is(calculator[SubstituteNode.instance].recorder.indexedRecords.size, 2) + t.is(calculator[instance].recorder.records.size, 2) + t.is(calculator[instance].recorder.indexedRecords.size, 2) - t.throws(() => calculator[received]().add(1, 1)) + t.throws(() => calculator.received().add(1, 1)) t.is(2, calculator.add(1, 1)) }) \ No newline at end of file diff --git a/spec/Recorder.spec.ts b/spec/Recorder.spec.ts index 14a96f0..a743e26 100644 --- a/spec/Recorder.spec.ts +++ b/spec/Recorder.spec.ts @@ -8,7 +8,7 @@ import { returns } from '../src' const nodeFactory = (key: string) => { const node = Substitute.for() - node.key[returns](key) + node.key.returns(key) return node } diff --git a/spec/regression/didNotReceive.spec.ts b/spec/regression/didNotReceive.spec.ts index 3eaad21..55f4dba 100644 --- a/spec/regression/didNotReceive.spec.ts +++ b/spec/regression/didNotReceive.spec.ts @@ -12,41 +12,41 @@ interface Calculator { test('not calling a method correctly asserts the call count', t => { const calculator = Substitute.for() - calculator[didNotReceive]().add(1, 1) - t.throws(() => calculator[received]().add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received]().add(Arg.all()), { instanceOf: SubstituteException }) + calculator.didNotReceive().add(1, 1) + t.throws(() => calculator.received().add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received().add(Arg.all()), { instanceOf: SubstituteException }) }) test('not getting a property correctly asserts the call count', t => { const calculator = Substitute.for() - calculator[didNotReceive]().isEnabled - t.throws(() => calculator[received](1).isEnabled, { instanceOf: SubstituteException }) - t.throws(() => calculator[received]().isEnabled, { instanceOf: SubstituteException }) + calculator.didNotReceive().isEnabled + t.throws(() => calculator.received(1).isEnabled, { instanceOf: SubstituteException }) + t.throws(() => calculator.received().isEnabled, { instanceOf: SubstituteException }) }) test('not setting a property correctly asserts the call count', t => { const calculator = Substitute.for() - calculator[didNotReceive]().isEnabled = true - t.throws(() => calculator[received](1).isEnabled = true, { instanceOf: SubstituteException }) - t.throws(() => calculator[received]().isEnabled = true, { instanceOf: SubstituteException }) + calculator.didNotReceive().isEnabled = true + t.throws(() => calculator.received(1).isEnabled = true, { instanceOf: SubstituteException }) + t.throws(() => calculator.received().isEnabled = true, { instanceOf: SubstituteException }) }) test('not calling a method with mock correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.add(1, 1)[returns](2) + calculator.add(1, 1).returns(2) - calculator[didNotReceive]().add(1, 1) - t.throws(() => calculator[received](1).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received]().add(Arg.all()), { instanceOf: SubstituteException }) + calculator.didNotReceive().add(1, 1) + t.throws(() => calculator.received(1).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received().add(Arg.all()), { instanceOf: SubstituteException }) }) test('not getting a property with mock correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.isEnabled[returns](true) + calculator.isEnabled.returns(true) - calculator[didNotReceive]().isEnabled - t.throws(() => calculator[received](1).isEnabled, { instanceOf: SubstituteException }) - t.throws(() => calculator[received]().isEnabled, { instanceOf: SubstituteException }) + calculator.didNotReceive().isEnabled + t.throws(() => calculator.received(1).isEnabled, { instanceOf: SubstituteException }) + t.throws(() => calculator.received().isEnabled, { instanceOf: SubstituteException }) }) \ No newline at end of file diff --git a/spec/regression/index.test.ts b/spec/regression/index.test.ts index 241069d..33c97de 100644 --- a/spec/regression/index.test.ts +++ b/spec/regression/index.test.ts @@ -20,7 +20,7 @@ export class Example { set v(x: string | null | undefined) { } - received(stuff: number | string) { + received(_stuff: string) { } @@ -28,7 +28,7 @@ export class Example { return Promise.resolve(new Dummy()) } - foo(): string | undefined | null { + foo(_arg?: string): string | undefined | null { return 'stuff' } @@ -48,12 +48,12 @@ function initialize() { const textModifierRegex = /\x1b\[\d+m/g test('class with method called \'received\' can be used for call count verification when using symbols', t => { - initialize() + const substitute = Substitute.for() - substitute.received(2) + substitute.received("foo") - t.throws(() => substitute[received](2).received(2)) - t.notThrows(() => substitute[received](1).received(2)) + t.notThrows(() => substitute[received](1).received("foo")) + t.throws(() => substitute[received](2).received("foo")) }) test('class string field set received', t => { @@ -85,7 +85,7 @@ test('class string field set received', t => { test('resolving promises works', async t => { initialize() - substitute.returnPromise()[resolves](1338) + substitute.returnPromise().resolves(1338) t.is(1338, await substitute.returnPromise() as number) }) @@ -93,7 +93,7 @@ test('resolving promises works', async t => { test('class void returns', t => { initialize() - substitute.foo()[returns](void 0, null) + substitute.foo().returns(void 0, null) t.is(substitute.foo(), void 0) t.is(substitute.foo(), null) @@ -126,7 +126,7 @@ test('class method received', t => { test('received call matches after partial mocks using property instance mimicks', t => { initialize() - substitute.d[mimicks](() => instance.d) + substitute.d.mimicks(() => instance.d) substitute.c('lala', 'bar') substitute[received](1).c('lala', 'bar') @@ -144,7 +144,7 @@ test('received call matches after partial mocks using property instance mimicks' test('partial mocks using property instance mimicks', t => { initialize() - substitute.d[mimicks](() => instance.d) + substitute.d.mimicks(() => instance.d) t.deepEqual(substitute.d, 1337) }) diff --git a/spec/regression/issues/11.test.ts b/spec/regression/issues/11.test.ts index 127526c..64e7b08 100644 --- a/spec/regression/issues/11.test.ts +++ b/spec/regression/issues/11.test.ts @@ -14,12 +14,12 @@ class RealCalculator { test('issue 11: arg.is is only called once', async t => { let mockedCalculator = Substitute.for() - mockedCalculator.add(Arg.any())[returns](4) + mockedCalculator.add(Arg.any()).returns(4) let count = 0 mockedCalculator.add({ op1: 1, op2: 2 }) - mockedCalculator[received](1).add(Arg.is(a => { + mockedCalculator.received(1).add(Arg.is(a => { count++ return a.op1 === 1 && a.op2 === 2 })) diff --git a/spec/regression/issues/178.test.ts b/spec/regression/issues/178.test.ts index 72e8341..018b208 100644 --- a/spec/regression/issues/178.test.ts +++ b/spec/regression/issues/178.test.ts @@ -24,17 +24,17 @@ const throwsUncaughtException = (cb: () => any, t: ExecutionContext, expectation test('can substitute callable interfaces', async t => { const lib = Substitute.for() - lib.subSection()[returns]('subSection as method') - lib.subSection[returns]({ subMethod: () => 'subSection as property' } as Subsection) + lib.subSection().returns('subSection as method') + lib.subSection.returns({ subMethod: () => 'subSection as property' } as Subsection) t.is('subSection as method', lib.subSection()) t.true(types.isProxy(lib.subSection), 'Expected proxy: given the context, it\'s not possible to determine the property type') t.is('subSection as property', lib.subSection.subMethod()) - lib[received]().subSection() - lib[received](1).subSection() - lib[received](2).subSection - t.throws(() => lib[didNotReceive]().subSection(), { instanceOf: SubstituteException }) - t.throws(() => lib[received](2).subSection(), { instanceOf: SubstituteException }) - throwsUncaughtException(() => lib[received](3).subSection, t, { instanceOf: SubstituteException }) + lib.received().subSection() + lib.received(1).subSection() + lib.received(2).subSection + t.throws(() => lib.didNotReceive().subSection(), { instanceOf: SubstituteException }) + t.throws(() => lib.received(2).subSection(), { instanceOf: SubstituteException }) + throwsUncaughtException(() => lib.received(3).subSection, t, { instanceOf: SubstituteException }) }) diff --git a/spec/regression/issues/23.test.ts b/spec/regression/issues/23.test.ts index c4fde96..1126b7e 100644 --- a/spec/regression/issues/23.test.ts +++ b/spec/regression/issues/23.test.ts @@ -14,12 +14,12 @@ test('issue 23: mimick received should not call method', t => { let calls = 0 - mockedCalculator.add(Arg.all())[mimicks]((a, b) => { + mockedCalculator.add(Arg.all()).mimicks((a, b) => { t.deepEqual(++calls, 1, 'mimick called twice') return a + b }) mockedCalculator.add(1, 1) // ok - mockedCalculator[received](1).add(1, 1) // not ok, calls mimick func + mockedCalculator.received(1).add(1, 1) // not ok, calls mimick func }) diff --git a/spec/regression/issues/36.test.ts b/spec/regression/issues/36.test.ts index d9c97ee..9d9be54 100644 --- a/spec/regression/issues/36.test.ts +++ b/spec/regression/issues/36.test.ts @@ -49,7 +49,7 @@ class Service { test('issue 36 - promises returning object with properties', async t => { const emptyFetch = Substitute.for() - emptyFetch.getUpdates(Key.create())[returns](Promise.resolve(IData.create())) + emptyFetch.getUpdates(Key.create()).returns(Promise.resolve(IData.create())) const result = await emptyFetch.getUpdates(Key.create()) t.true(result.serverCheck instanceof Date, 'given date is instanceof Date') t.deepEqual(result.data, [1], 'arrays are deep equal') @@ -58,12 +58,12 @@ test('issue 36 - promises returning object with properties', async t => { test('using objects or classes as arguments should be able to match mock', async t => { const db = Substitute.for() const data = IData.create() - db.getUpdates(Key.create())[returns](Promise.resolve(data)) + db.getUpdates(Key.create()).returns(Promise.resolve(data)) const service = new Service(db) await service.handle(Key.create()) - db[received](1).storeUpdates(Arg.is((arg: IData) => + db.received(1).storeUpdates(Arg.is((arg: IData) => arg.serverCheck instanceof Date && arg instanceof IData && arg.data[0] === 100 diff --git a/spec/regression/issues/45.test.ts b/spec/regression/issues/45.test.ts index c9178b8..9c87540 100644 --- a/spec/regression/issues/45.test.ts +++ b/spec/regression/issues/45.test.ts @@ -30,7 +30,7 @@ test('issue 45 Checking received calls off at times', async t => { subject.callToMethodTwo() t.notThrows(() => { - mock[received](1).methodOne() - mock[received](1).methodTwo(Arg.is(x => x === 'string')) + mock.received(1).methodOne() + mock.received(1).methodTwo(Arg.is(x => x === 'string')) }) }) diff --git a/spec/regression/issues/59.test.ts b/spec/regression/issues/59.test.ts index 72dffc5..fbe6603 100644 --- a/spec/regression/issues/59.test.ts +++ b/spec/regression/issues/59.test.ts @@ -9,10 +9,10 @@ interface IEcho { test('issue 59 - Mock function with optional parameters', (t) => { const echoer = Substitute.for() - echoer.maybeEcho('foo')[returns]('bar') - echoer.maybeEcho()[returns]('baz') + echoer.maybeEcho('foo').returns('bar') + echoer.maybeEcho().returns('baz') t.is('bar', echoer.maybeEcho('foo')) - echoer[received]().maybeEcho('foo') + echoer.received().maybeEcho('foo') t.is('baz', echoer.maybeEcho()) }) diff --git a/spec/regression/mimicks.spec.ts b/spec/regression/mimicks.spec.ts index 4805344..d741131 100644 --- a/spec/regression/mimicks.spec.ts +++ b/spec/regression/mimicks.spec.ts @@ -16,7 +16,7 @@ test('mimicks a method with specific arguments', t => { const calculator = Substitute.for() const addMimick = (a: number, b: number) => a + b - calculator.add(1, 1)[mimicks](addMimick) + calculator.add(1, 1).mimicks(addMimick) t.is(2, calculator.add(1, 1)) }) @@ -24,8 +24,8 @@ test('mimicks a method with specific and conditional arguments', t => { const calculator = Substitute.for() const addMimick = (a: number, b: number) => a + b - calculator.add(Arg.any('number'), Arg.is((input: number) => input >= 0 && input <= 10))[mimicks](addMimick) - calculator.add(42, -42)[mimicks]((a: number, b: number) => 0) + calculator.add(Arg.any('number'), Arg.is((input: number) => input >= 0 && input <= 10)).mimicks(addMimick) + calculator.add(42, -42).mimicks((a: number, b: number) => 0) t.is(1240, calculator.add(1234, 6)) t.is(0, calculator.add(42, -42)) @@ -36,7 +36,7 @@ test('mimicks a method with Arg.all', t => { const addMimick = (a: number, b: number) => a + b - calculator.add(Arg.all())[mimicks](addMimick) + calculator.add(Arg.all()).mimicks(addMimick) t.is(100, calculator.add(42, 58)) }) @@ -45,9 +45,9 @@ test('mimicks a method with optional arguments', t => { const multiplyOneArgMimicks = (a: number) => a * a const multiplyMimicks = (a: number, b?: number) => a * (b ?? 0) - calculator.multiply(0, Arg.is((b: number) => b > 10 && b < 20))[mimicks](multiplyMimicks) - calculator.multiply(Arg.any('number'), Arg.is((b: number) => b === 2))[mimicks](multiplyMimicks) - calculator.multiply(2)[mimicks](multiplyOneArgMimicks) + calculator.multiply(0, Arg.is((b: number) => b > 10 && b < 20)).mimicks(multiplyMimicks) + calculator.multiply(Arg.any('number'), Arg.is((b: number) => b === 2)).mimicks(multiplyMimicks) + calculator.multiply(2).mimicks(multiplyOneArgMimicks) t.is(0, calculator.multiply(0, 13)) t.is(84, calculator.multiply(42, 2)) @@ -57,8 +57,8 @@ test('mimicks a method with optional arguments', t => { test('mimicks a method where it\'s only argument is optional', t => { const calculator = Substitute.for() - calculator.viewResult()[mimicks](() => 0) - calculator.viewResult(3)[mimicks](() => 42) + calculator.viewResult().mimicks(() => 0) + calculator.viewResult(3).mimicks(() => 42) t.is(0, calculator.viewResult()) t.is(42, calculator.viewResult(3)) diff --git a/spec/regression/received.spec.ts b/spec/regression/received.spec.ts index ad8633b..8b6b112 100644 --- a/spec/regression/received.spec.ts +++ b/spec/regression/received.spec.ts @@ -13,24 +13,24 @@ test('calling a method twice correctly asserts the call count', t => { calculator.add(1, 1) calculator.add(1, 1) - calculator[received](2).add(1, 1) - calculator[received]().add(1, 1) - t.throws(() => calculator[received](0).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received](1).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received](2).add(1, 0), { instanceOf: SubstituteException }) - t.throws(() => calculator[received]().add(1, 0), { instanceOf: SubstituteException }) + calculator.received(2).add(1, 1) + calculator.received().add(1, 1) + t.throws(() => calculator.received(0).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received(1).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received(2).add(1, 0), { instanceOf: SubstituteException }) + t.throws(() => calculator.received().add(1, 0), { instanceOf: SubstituteException }) }) test('calling a method twice correctly asserts the call count each time', t => { const calculator = Substitute.for() calculator.add(1, 1) - calculator[received](1).add(1, 1) + calculator.received(1).add(1, 1) calculator.add(1, 1) - calculator[received](2).add(1, 1) + calculator.received(2).add(1, 1) - t.throws(() => calculator[received](1).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received(1).add(1, 1), { instanceOf: SubstituteException }) }) test('calling a method with optional arguments correctly asserts the call count', t => { @@ -39,16 +39,16 @@ test('calling a method with optional arguments correctly asserts the call count' calculator.multiply(1, 1) calculator.multiply(2, 1) - calculator[received](1).multiply(1) - calculator[received](1).multiply(1, 1) - calculator[received](0).multiply(2) - calculator[received](1).multiply(2, 1) + calculator.received(1).multiply(1) + calculator.received(1).multiply(1, 1) + calculator.received(0).multiply(2) + calculator.received(1).multiply(2, 1) - t.throws(() => calculator[received](0).multiply(1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received](0).multiply(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received](2).multiply(1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received](2).multiply(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received](1).multiply(2), { instanceOf: SubstituteException }) + t.throws(() => calculator.received(0).multiply(1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received(0).multiply(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received(2).multiply(1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received(2).multiply(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received(1).multiply(2), { instanceOf: SubstituteException }) }) test('getting a property twice correctly asserts the call count', t => { @@ -56,10 +56,10 @@ test('getting a property twice correctly asserts the call count', t => { calculator.isEnabled calculator.isEnabled - calculator[received](2).isEnabled - calculator[received]().isEnabled - t.throws(() => calculator[received](0).isEnabled, { instanceOf: SubstituteException }) - t.throws(() => calculator[received](1).isEnabled, { instanceOf: SubstituteException }) + calculator.received(2).isEnabled + calculator.received().isEnabled + t.throws(() => calculator.received(0).isEnabled, { instanceOf: SubstituteException }) + t.throws(() => calculator.received(1).isEnabled, { instanceOf: SubstituteException }) }) test('setting a property twice correctly asserts the call count', t => { @@ -70,50 +70,50 @@ test('setting a property twice correctly asserts the call count', t => { } runLogic(calculator) - calculator[received](2).isEnabled = true - calculator[received]().isEnabled = true - t.throws(() => calculator[received](0).isEnabled = true, { instanceOf: SubstituteException }) - t.throws(() => calculator[received](1).isEnabled = true, { instanceOf: SubstituteException }) - t.throws(() => calculator[received](2).isEnabled = false, { instanceOf: SubstituteException }) - t.throws(() => calculator[received]().isEnabled = false, { instanceOf: SubstituteException }) + calculator.received(2).isEnabled = true + calculator.received().isEnabled = true + t.throws(() => calculator.received(0).isEnabled = true, { instanceOf: SubstituteException }) + t.throws(() => calculator.received(1).isEnabled = true, { instanceOf: SubstituteException }) + t.throws(() => calculator.received(2).isEnabled = false, { instanceOf: SubstituteException }) + t.throws(() => calculator.received().isEnabled = false, { instanceOf: SubstituteException }) }) test('calling a method twice with mock correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.add(1, 1)[returns](2) + calculator.add(1, 1).returns(2) calculator.add(1, 1) calculator.add(1, 1) - calculator[received](2).add(1, 1) - calculator[received]().add(1, 1) - t.throws(() => calculator[received](0).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received](1).add(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received](2).add(1, 0), { instanceOf: SubstituteException }) - t.throws(() => calculator[received]().add(1, 0), { instanceOf: SubstituteException }) + calculator.received(2).add(1, 1) + calculator.received().add(1, 1) + t.throws(() => calculator.received(0).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received(1).add(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received(2).add(1, 0), { instanceOf: SubstituteException }) + t.throws(() => calculator.received().add(1, 0), { instanceOf: SubstituteException }) }) test('calling a method with mock based on Arg correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.add(Arg.all())[returns](0) + calculator.add(Arg.all()).returns(0) calculator.add(1, 1) - calculator[received]().add(Arg.all()) - calculator[received]().add(Arg.any(), Arg.any()) - calculator[received]().add(Arg.any('number'), Arg.any('number')) - calculator[received]().add(1, 1) - t.throws(() => calculator[received]().add(1, 0), { instanceOf: SubstituteException }) + calculator.received().add(Arg.all()) + calculator.received().add(Arg.any(), Arg.any()) + calculator.received().add(Arg.any('number'), Arg.any('number')) + calculator.received().add(1, 1) + t.throws(() => calculator.received().add(1, 0), { instanceOf: SubstituteException }) }) test('getting a property twice with mock correctly asserts the call count', t => { const calculator = Substitute.for() - calculator.isEnabled[returns](true) + calculator.isEnabled.returns(true) calculator.isEnabled calculator.isEnabled - calculator[received](2).isEnabled - calculator[received]().isEnabled - t.throws(() => calculator[received](0).isEnabled, { instanceOf: SubstituteException }) - t.throws(() => calculator[received](1).isEnabled, { instanceOf: SubstituteException }) + calculator.received(2).isEnabled + calculator.received().isEnabled + t.throws(() => calculator.received(0).isEnabled, { instanceOf: SubstituteException }) + t.throws(() => calculator.received(1).isEnabled, { instanceOf: SubstituteException }) }) test('calling a method with Arg correctly asserts the call count with Arg', t => { @@ -121,9 +121,9 @@ test('calling a method with Arg correctly asserts the call count with Arg', t => const calculator = Substitute.for() calculator.add(Arg.all()) - calculator[received](1).add(Arg.all()) - calculator[received]().add(Arg.all()) - t.throws(() => calculator[received](0).add(1, 1), { instanceOf: SubstituteException }) + calculator.received(1).add(Arg.all()) + calculator.received().add(Arg.all()) + t.throws(() => calculator.received(0).add(1, 1), { instanceOf: SubstituteException }) }) test('calling a method does not interfere with other properties or methods call counts', t => { @@ -131,10 +131,10 @@ test('calling a method does not interfere with other properties or methods call const calculator = Substitute.for() calculator.add(1, 1) - calculator[received](0).multiply(1, 1) - calculator[received](0).multiply(Arg.all()) - calculator[received](0).isEnabled + calculator.received(0).multiply(1, 1) + calculator.received(0).multiply(Arg.all()) + calculator.received(0).isEnabled - t.throws(() => calculator[received]().multiply(1, 1), { instanceOf: SubstituteException }) - t.throws(() => calculator[received]().multiply(Arg.all()), { instanceOf: SubstituteException }) + t.throws(() => calculator.received().multiply(1, 1), { instanceOf: SubstituteException }) + t.throws(() => calculator.received().multiply(Arg.all()), { instanceOf: SubstituteException }) }) \ No newline at end of file diff --git a/spec/regression/rejects.spec.ts b/spec/regression/rejects.spec.ts index ac6872b..016fa7b 100644 --- a/spec/regression/rejects.spec.ts +++ b/spec/regression/rejects.spec.ts @@ -10,21 +10,21 @@ interface Calculator { test('rejects a method with no arguments', async t => { const calculator = Substitute.for() - calculator.getMemory()[rejects](new Error('No memory')) + calculator.getMemory().rejects(new Error('No memory')) await t.throwsAsync(calculator.getMemory(), { instanceOf: Error, message: 'No memory' }) }) test('rejects a method with arguments', async t => { const calculator = Substitute.for() - calculator.heavyOperation(0, 1, 1, 2, 4, 5, 8)[rejects](new Error('Wrong sequence!')) + calculator.heavyOperation(0, 1, 1, 2, 4, 5, 8).rejects(new Error('Wrong sequence!')) await t.throwsAsync(calculator.heavyOperation(0, 1, 1, 2, 4, 5, 8), { instanceOf: Error, message: 'Wrong sequence!' }) }) test('rejects different values in the specified order on a method', async t => { const calculator = Substitute.for() - calculator.heavyOperation(Arg.any('number'))[rejects](new Error('Wrong!'), new Error('Wrong again!')) + calculator.heavyOperation(Arg.any('number')).rejects(new Error('Wrong!'), new Error('Wrong again!')) await t.throwsAsync(calculator.heavyOperation(0), { instanceOf: Error, message: 'Wrong!' }) await t.throwsAsync(calculator.heavyOperation(0), { instanceOf: Error, message: 'Wrong again!' }) @@ -33,14 +33,14 @@ test('rejects different values in the specified order on a method', async t => { test('rejects a property', async t => { const calculator = Substitute.for() - calculator.model[rejects](new Error('No model')) + calculator.model.rejects(new Error('No model')) await t.throwsAsync(calculator.model, { instanceOf: Error, message: 'No model' }) }) test('rejects different values in the specified order on a property', async t => { const calculator = Substitute.for() - calculator.model[rejects](new Error('No model'), new Error('I said "no model"')) + calculator.model.rejects(new Error('No model'), new Error('I said "no model"')) await t.throwsAsync(calculator.model, { instanceOf: Error, message: 'No model' }) await t.throwsAsync(calculator.model, { instanceOf: Error, message: 'I said "no model"' }) diff --git a/spec/regression/resolves.spec.ts b/spec/regression/resolves.spec.ts index 87125cb..10fe86b 100644 --- a/spec/regression/resolves.spec.ts +++ b/spec/regression/resolves.spec.ts @@ -10,21 +10,21 @@ interface Calculator { test('resolves a method with no arguments', async t => { const calculator = Substitute.for() - calculator.getMemory()[resolves](0) + calculator.getMemory().resolves(0) t.is(await calculator.getMemory(), 0) }) test('resolves a method with arguments', async t => { const calculator = Substitute.for() - calculator.heavyOperation(0, 1, 1, 2, 3, 5, 8)[resolves](13) + calculator.heavyOperation(0, 1, 1, 2, 3, 5, 8).resolves(13) t.is(await calculator.heavyOperation(0, 1, 1, 2, 3, 5, 8), 13) }) test('resolves different values in the specified order on a method', async t => { const calculator = Substitute.for() - calculator.heavyOperation(Arg.any('number'))[resolves](1, 2, 3) + calculator.heavyOperation(Arg.any('number')).resolves(1, 2, 3) t.is(await calculator.heavyOperation(0), 1) t.is(await calculator.heavyOperation(0), 2) @@ -34,14 +34,14 @@ test('resolves different values in the specified order on a method', async t => test('resolves a property', async t => { const calculator = Substitute.for() - calculator.model[resolves]('Casio FX-82') + calculator.model.resolves('Casio FX-82') t.is(await calculator.model, 'Casio FX-82') }) test('resolves different values in the specified order on a property', async t => { const calculator = Substitute.for() - calculator.model[resolves]('Casio FX-82', 'TI-84 Plus') + calculator.model.resolves('Casio FX-82', 'TI-84 Plus') t.is(await calculator.model, 'Casio FX-82') t.is(await calculator.model, 'TI-84 Plus') diff --git a/spec/regression/returns.spec.ts b/spec/regression/returns.spec.ts index d076c04..068412b 100644 --- a/spec/regression/returns.spec.ts +++ b/spec/regression/returns.spec.ts @@ -18,10 +18,7 @@ interface Calculator { test('returns a primitive value for method with no arguments', t => { const calculator = Substitute.for() - calculator.clear()[returns]() - // calculator.add(1, 2).toExponential() - // calculator.isEnabled2.viewResult().returns(3) - // calculator.other().viewResult().returns(2) + calculator.clear().returns() t.is(void 0 as void, calculator.clear()) }) @@ -30,7 +27,7 @@ test('returns a primitive value for method with specific arguments', t => { const calculator = Substitute.for() const noResult = calculator.add(1, 1) - calculator.add(1, 1)[returns](2) + calculator.add(1, 1).returns(2) t.is(2, calculator.add(1, 1)) t.is(2, calculator.add(1, 1)) @@ -40,10 +37,10 @@ test('returns a primitive value for method with specific arguments', t => { test('returns a primitive value for method with specific arguments where the last argument is optional', t => { const calculator = Substitute.for() - calculator.multiply(2)[returns](4) - calculator.multiply(0, Arg.any('number'))[returns](0) - calculator.multiply(1, Arg.any())[returns](10) - calculator.multiply(1)[returns](1) + calculator.multiply(2).returns(4) + calculator.multiply(0, Arg.any('number')).returns(0) + calculator.multiply(1, Arg.any()).returns(10) + calculator.multiply(1).returns(1) t.is(4, calculator.multiply(2)) t.is(0, calculator.multiply(0, 10)) @@ -59,10 +56,10 @@ test('returns a primitive value for method with specific arguments where the las test('returns a primitive value for method with specific and conditional arguments', t => { const calculator = Substitute.for() - calculator.add(0, 0)[returns](0) - calculator.add(1, Arg.is((input: number) => input === 1))[returns](2) - calculator.add(2, Arg.any('number'))[returns](10) - calculator.add(Arg.is((input: number) => input > 2), Arg.any('number'))[returns](42) + calculator.add(0, 0).returns(0) + calculator.add(1, Arg.is((input: number) => input === 1)).returns(2) + calculator.add(2, Arg.any('number')).returns(10) + calculator.add(Arg.is((input: number) => input > 2), Arg.any('number')).returns(42) const results = [calculator.add(0, 0), calculator.add(1, 1), calculator.add(2, 100), calculator.add(42, 84)] @@ -72,7 +69,7 @@ test('returns a primitive value for method with specific and conditional argumen test('returns a primitive value for method with Arg.all', t => { // #25: call verification does not work when using Arg.all() to set up return values https://github.com/ffMathy/FluffySpoon.JavaScript.Testing.Faking/issues/25 const calculator = Substitute.for() - calculator.add(Arg.all())[returns](42) + calculator.add(Arg.all()).returns(42) const results = [calculator.add(0, 0), calculator.add(1, 1), calculator.add(2, 100)] @@ -82,8 +79,8 @@ test('returns a primitive value for method with Arg.all', t => { test('returns a primitive value for method with one optional argument', t => { // #24: Mocked method arguments not allowed when verifying method was called https://github.com/ffMathy/FluffySpoon.JavaScript.Testing.Faking/issues/24 const calculator = Substitute.for() - calculator.viewResult()[returns](0) - calculator.viewResult(3)[returns](123) + calculator.viewResult().returns(0) + calculator.viewResult(3).returns(123) t.is(0, calculator.viewResult()) t.is(123, calculator.viewResult(3)) @@ -91,14 +88,14 @@ test('returns a primitive value for method with one optional argument', t => { test('returns a promise for method with no arguments', async t => { const calculator = Substitute.for() - calculator.getMemory()[returns](Promise.resolve(42)) + calculator.getMemory().returns(Promise.resolve(42)) t.is(await calculator.getMemory(), 42) }) test('returns a promise for method with specific arguments', async t => { const calculator = Substitute.for() - calculator.heavyOperation(1, 1)[returns](Promise.resolve(true)) + calculator.heavyOperation(1, 1).returns(Promise.resolve(true)) const result = await calculator.heavyOperation(1, 1) const noResult = calculator.heavyOperation(1, 1, 1) @@ -109,9 +106,9 @@ test('returns a promise for method with specific arguments', async t => { test('returns a promise for method with specific and conditional arguments', async t => { const calculator = Substitute.for() - calculator.heavyOperation(0)[returns](Promise.resolve(true)) - calculator.heavyOperation(1, Arg.is((input: number) => input === 1))[returns](Promise.resolve(false)) - calculator.heavyOperation(2, Arg.any('number'), 100)[returns](Promise.resolve(true)) + calculator.heavyOperation(0).returns(Promise.resolve(true)) + calculator.heavyOperation(1, Arg.is((input: number) => input === 1)).returns(Promise.resolve(false)) + calculator.heavyOperation(2, Arg.any('number'), 100).returns(Promise.resolve(true)) const results = await Promise.all([calculator.heavyOperation(0), calculator.heavyOperation(1, 1), calculator.heavyOperation(2, 4321, 100)]) @@ -120,7 +117,7 @@ test('returns a promise for method with specific and conditional arguments', asy test('returns a promise for method with Arg.all', async t => { const calculator = Substitute.for() - calculator.heavyOperation(Arg.all())[returns](Promise.resolve(true)) + calculator.heavyOperation(Arg.all()).returns(Promise.resolve(true)) const results = await Promise.all([calculator.heavyOperation(0), calculator.heavyOperation(4321, 11, 42, 1234), calculator.heavyOperation(-1, 444)]) @@ -129,7 +126,7 @@ test('returns a promise for method with Arg.all', async t => { test('returns different primitive values in the specified order for method with arguments', t => { const calculator = Substitute.for() - calculator.add(1, Arg.any())[returns](1, NaN) + calculator.add(1, Arg.any()).returns(1, NaN) t.is(1, calculator.add(1, 1)) t.is(NaN, calculator.add(1, 0)) @@ -140,8 +137,8 @@ test('returns different primitive values in the specified order for method with test('returns another substituted instance for method with arguments', t => { const calculator = Substitute.for() const addResult = Substitute.for() - addResult.toLocaleString()[returns]('What a weird number') - calculator.add(1, Arg.any())[returns](addResult) + addResult.toLocaleString().returns('What a weird number') + calculator.add(1, Arg.any()).returns(addResult) const result = calculator.add(1, 1) @@ -154,7 +151,7 @@ test('returns a primitive value on a property', t => { const calculator = Substitute.for() const noResult = calculator.isEnabled - calculator.isEnabled[returns](true) + calculator.isEnabled.returns(true) t.true(calculator.isEnabled) t.true(calculator.isEnabled) @@ -163,14 +160,14 @@ test('returns a primitive value on a property', t => { test('returns a promise on a property', async t => { const calculator = Substitute.for() - calculator.model[returns](Promise.resolve('Casio FX-82')) + calculator.model.returns(Promise.resolve('Casio FX-82')) t.is(await calculator.model, 'Casio FX-82') }) test('returns different primitive values in the specified order on a property', t => { const calculator = Substitute.for() - calculator.isEnabled[returns](false, true) + calculator.isEnabled.returns(false, true) t.is(false, calculator.isEnabled) t.is(true, calculator.isEnabled) @@ -180,8 +177,8 @@ test('returns different primitive values in the specified order on a property', test('returns another substituted instance on a property', async t => { const calculator = Substitute.for() const modelResult = Substitute.for() - modelResult.replace(Arg.all())[returns]('TI-83') - calculator.model[returns](Promise.resolve(modelResult)) + modelResult.replace(Arg.all()).returns('TI-83') + calculator.model.returns(Promise.resolve(modelResult)) const result = await calculator.model t.is(result, modelResult) diff --git a/spec/regression/throws.spec.ts b/spec/regression/throws.spec.ts index 1396b29..eac673d 100644 --- a/spec/regression/throws.spec.ts +++ b/spec/regression/throws.spec.ts @@ -12,22 +12,22 @@ interface Calculator { test('throws on a method with arguments', t => { const calculator = Substitute.for() - calculator.divide(Arg.any(), 0)[throws](new Error('Cannot divide by 0')) + calculator.divide(Arg.any(), 0).throws(new Error('Cannot divide by 0')) t.throws(() => calculator.divide(1, 0), { instanceOf: Error, message: 'Cannot divide by 0' }) }) test('throws on a property being called', t => { const calculator = Substitute.for() - calculator.mode[throws](new Error('Property not set')) + calculator.mode.throws(new Error('Property not set')) t.throws(() => calculator.mode, { instanceOf: Error, message: 'Property not set' }) }) test('does not throw on methods that do not match arguments', t => { const calculator = Substitute.for() - calculator.divide(Arg.any(), 0)[throws](new Error('Cannot divide by 0')) - calculator.divide(4, 2)[returns](2) + calculator.divide(Arg.any(), 0).throws(new Error('Cannot divide by 0')) + calculator.divide(4, 2).returns(2) t.is(2, calculator.divide(4, 2)) t.throws(() => calculator.divide(1, 0), { instanceOf: Error, message: 'Cannot divide by 0' }) @@ -35,8 +35,8 @@ test('does not throw on methods that do not match arguments', t => { test('can set multiple throws for same method with different arguments', t => { const calculator = Substitute.for() - calculator.divide(Arg.any(), 0)[throws](new Error('Cannot divide by 0')) - calculator.divide(Arg.any(), Arg.is(number => !Number.isInteger(number)))[throws](new Error('Only integers supported')) + calculator.divide(Arg.any(), 0).throws(new Error('Cannot divide by 0')) + calculator.divide(Arg.any(), Arg.is(number => !Number.isInteger(number))).throws(new Error('Only integers supported')) t.throws(() => calculator.divide(1, 1.135), { instanceOf: Error, message: 'Only integers supported' }) t.throws(() => calculator.divide(1, 0), { instanceOf: Error, message: 'Cannot divide by 0' }) diff --git a/src/Substitute.ts b/src/Substitute.ts index c604436..3e211df 100644 --- a/src/Substitute.ts +++ b/src/Substitute.ts @@ -3,15 +3,9 @@ import { SubstituteNode } from './SubstituteNode' export type SubstituteOf = ObjectSubstitute & T -type InstantiableSubstitute> = T & { [SubstituteNode.instance]: SubstituteNode } - export class Substitute { public static for(): SubstituteOf { const substitute = SubstituteNode.createRoot() return substitute.proxy as unknown as SubstituteOf } - - private static extractSubstituteNodeFromSubstitute(substitute: InstantiableSubstitute>): SubstituteNode { - return substitute[SubstituteNode.instance] - } } \ No newline at end of file diff --git a/src/SubstituteNode.ts b/src/SubstituteNode.ts index 49cbdf3..9a98023 100644 --- a/src/SubstituteNode.ts +++ b/src/SubstituteNode.ts @@ -2,16 +2,17 @@ import { inspect, InspectOptions, types } from 'util' import { SubstituteNodeBase } from './SubstituteNodeBase' import { RecordedArguments } from './RecordedArguments' -import { PropertyType as PropertyTypeMap, isAssertionMethod, isSubstituteMethod, isSubstitutionMethod, textModifier, isConfigurationMethod } from './Utilities' +import { PropertyType as PropertyTypeMap, isAssertionMethod, isSubstituteMethod, isSubstitutionMethod, textModifier, isConfigurationMethod, isThrowsFunction, isMimicksFunction, isResolvesFunction, isRejectsFunction, isReturnsFunction } from './Utilities' import { SubstituteException } from './SubstituteException' import type { SubstituteContext, SubstitutionMethod, PropertyType } from './Types' -import type { ObjectSubstitute } from './Transformations' +import type { ObjectSubstitute, OmitProxyMethods } from './Transformations' import { didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws, clearReceivedCalls } from './Transformations' -const instance = Symbol('Substitute:Instance') +export const instance = Symbol('Substitute:Instance') type SpecialProperty = typeof instance | typeof inspect.custom | 'then' -type RootContext = { substituteMethodsEnabled: boolean } + +type RootContext = { } export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitute { private _proxy: SubstituteNode @@ -27,7 +28,7 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu private constructor(key: PropertyKey, parent?: SubstituteNode) { super(key, parent) if (this.isRoot()) { - this._rootContext = { substituteMethodsEnabled: true } + this._rootContext = { } } else { this._rootContext = this.root.rootContext @@ -37,21 +38,29 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu this, { get: function (target, property) { - if (target.isSpecialProperty(property)) + if (target.isSpecialProperty(property)) { + // console.log('specialProperty', property) return target.evaluateSpecialProperty(property) + } - if (target._retrySubstitutionExecutionAttempt) + if (target._retrySubstitutionExecutionAttempt) { + // console.log('reattemptSubstitutionExecution', property) return target.reattemptSubstitutionExecution()[property] + } const newNode = SubstituteNode.createChild(property, target) - if (target.isAssertion) + if (target.isAssertion) { + // console.log('executeAssertion', property); newNode.executeAssertion() + } - if (target.isRoot() && target.rootContext.substituteMethodsEnabled && (isAssertionMethod(property) || isConfigurationMethod(property))) { + if (target.isRoot() && (isAssertionMethod(property) || isConfigurationMethod(property))) { + // console.log("isRoot", property); newNode.assignContext(property) return newNode[property].bind(newNode) } + // console.log("substitutionExecution", property); return newNode.attemptSubstitutionExecution() }, set: function (target, property, value) { @@ -65,12 +74,19 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu apply: function (target, _thisArg, rawArguments) { target.handleMethod(rawArguments) if (target.hasDepthOfAtLeast(2)) { - if (isSubstitutionMethod(target.property)) + if (isSubstitutionMethod(target.property)) { + // console.log('isSubstitutionMethod', target.property) return target.parent.assignContext(target.property) + } - if (target.parent.isAssertion) + if (target.parent.isAssertion) { + // console.log('target.parent.isAssertion') return target.executeAssertion() + } } + + // console.log('target.isAssertion', target.isAssertion) + return target.isAssertion ? target.proxy : target.attemptSubstitutionExecution() @@ -79,7 +95,21 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu ) } - public static instance: typeof instance = instance + public received(amount?: number | undefined) { + return this[received](amount); + } + + public didNotReceive() { + return this[didNotReceive](); + } + + public mimick(instance: OmitProxyMethods) { + return this[mimick](instance); + } + + public clearReceivedCalls() { + return this[clearReceivedCalls](); + } public static createRoot(): SubstituteNode { return new this('*Substitute') @@ -139,7 +169,7 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu return this.proxy } - public [mimick]() { + public [mimick](_instance: OmitProxyMethods) { throw new Error('Mimick is not implemented yet') } @@ -169,6 +199,7 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu private attemptSubstitutionExecution(): SubstituteNode | any { const mostSuitableSubstitution = this.getMostSuitableSubstitution() + // console.log('mostSuitableSubstitution', mostSuitableSubstitution) return mostSuitableSubstitution instanceof SubstituteNode ? mostSuitableSubstitution.executeSubstitution(this.recordedArguments) : this.proxy @@ -186,11 +217,11 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu ? this.child.recordedArguments.value?.shift() : this.child.recordedArguments.value[0] - if (substitutionMethod === throws) { + if (isThrowsFunction(substitutionMethod)) { throw substitutionValue; } - if (substitutionMethod === mimicks) { + if (isMimicksFunction(substitutionMethod)) { if (this.propertyType === PropertyTypeMap.Property) return substitutionValue() @@ -200,32 +231,33 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu return substitutionValue(...contextArguments.value) } - if (substitutionMethod === resolves) { + if (isResolvesFunction(substitutionMethod)) { return Promise.resolve(substitutionValue) } - if (substitutionMethod === rejects) { + if (isRejectsFunction(substitutionMethod)) { return Promise.reject(substitutionValue) } - if (substitutionMethod === returns) { + if (isReturnsFunction(substitutionMethod)) { return substitutionValue } - throw SubstituteException.generic(`Substitution method '${substitutionMethod}' not implemented. Make sure you invoke ".returns(...)" or any other configuration call on the given method.`) + throw SubstituteException.generic(`Substitution method '${String(substitutionMethod)}' not implemented. Make sure you invoke ".returns(...)" or any other configuration call on the given method.`) } private executeAssertion(): void | never { if (!this.hasDepthOfAtLeast(2)) - throw new Error('Not possible') + throw new Error('Depth is less than 2') if (!this.parent.recordedArguments.hasArguments()) - throw new TypeError('Parent args') + throw new Error('No parent args present') const expectedCount = this.parent.recordedArguments.value[0] ?? undefined const finiteExpectation = expectedCount !== undefined - if (finiteExpectation && (!Number.isInteger(expectedCount) || expectedCount < 0)) - throw new Error('Expected count has to be a positive integer') + if (finiteExpectation && (!Number.isInteger(expectedCount) || expectedCount < 0)) { + return + } const siblings = [...this.getAllSiblings().filter(n => !n.hasContext && n.accessorType === this.accessorType)] const hasBeenCalled = siblings.length > 0 @@ -283,6 +315,7 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu private handleMethod(rawArguments: any[]): void { this._propertyType = PropertyTypeMap.Method this._recordedArguments = RecordedArguments.from(rawArguments) + // console.log('handleMethod', rawArguments); } private getMostSuitableSubstitution(): SubstituteNode | void { @@ -306,12 +339,12 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu } private isSpecialProperty(property: PropertyKey): property is SpecialProperty { - return property === SubstituteNode.instance || property === inspect.custom || property === 'then' + return property === instance || property === inspect.custom || property === 'then' } private evaluateSpecialProperty(property: SpecialProperty) { switch (property) { - case SubstituteNode.instance: + case instance: return this case inspect.custom: diff --git a/src/Transformations.ts b/src/Transformations.ts index 74291dd..dbad376 100644 --- a/src/Transformations.ts +++ b/src/Transformations.ts @@ -1,17 +1,27 @@ import type { AllArguments } from './Arguments'; import type { FirstLevelMethod } from './Types'; +import { Prettify } from './Utilities'; export const received = Symbol('received'); export const didNotReceive = Symbol('didNotReceive'); export const mimick = Symbol('mimick'); export const clearReceivedCalls = Symbol('clearReceivedCalls'); - export const mimicks = Symbol('mimicks'); export const throws = Symbol('throws'); export const returns = Symbol('returns'); export const resolves = Symbol('resolves'); export const rejects = Symbol('rejects'); +export type ReceivedPropertyKey = typeof received | 'received'; +export type DidNotReceivePropertyKey = typeof didNotReceive | 'didNotReceive'; +export type MimickPropertyKey = typeof mimick | 'mimick'; +export type ClearReceivedCallsPropertyKey = typeof clearReceivedCalls | 'clearReceivedCalls'; +export type MimicksPropertyKey = typeof mimicks | 'mimicks'; +export type ThrowsPropertyKey = typeof throws | 'throws'; +export type ReturnsPropertyKey = typeof returns | 'returns'; +export type ResolvesPropertyKey = typeof resolves | 'resolves'; +export type RejectsPropertyKey = typeof rejects | 'rejects'; + type FunctionSubstituteWithOverloads = TFunc extends { (...args: infer A1): infer R1; @@ -52,59 +62,109 @@ type FunctionHandler = FunctionSubstitute export type FunctionSubstitute = - ((...args: TArguments) => (TReturnType & MockObjectMixin)) & - ((allArguments: AllArguments) => (TReturnType & MockObjectMixin)) - -export type NoArgumentFunctionSubstitute = () => TReturnType & NoArgumentMockObjectMixin; -export type PropertySubstitute = TReturnType & NoArgumentMockObjectMixin; + ((...args: TArguments) => (TReturnType & MockObjectMixin)) & + ((allArguments: AllArguments) => (TReturnType & MockObjectMixin)) -type OneArgumentRequiredFunction = (requiredInput: TArgs, ...restInputs: TArgs[]) => TReturnType; - -type MockObjectPromise = TReturnType extends Promise ? { - [resolves]: OneArgumentRequiredFunction; - [rejects]: OneArgumentRequiredFunction; -} : {} +export type NoArgumentFunctionSubstitute = + () => + TReturnType & + NoArgumentMockObjectMixin; -type BaseMockObjectMixin = MockObjectPromise & { - [returns]: OneArgumentRequiredFunction; - [throws]: OneArgumentRequiredFunction; -} +export type PropertySubstitute = + TReturnType & + NoArgumentMockObjectMixin; -type NoArgumentMockObjectMixin = BaseMockObjectMixin & { - [mimicks]: OneArgumentRequiredFunction<() => TReturnType, void>; -} +type OneArgumentRequiredFunction = (requiredInput: TArgs, ...restInputs: TArgs[]) => TReturnType; -type MockObjectMixin = BaseMockObjectMixin & { - [mimicks]: OneArgumentRequiredFunction<(...args: TArguments) => TReturnType, void>; -} +type MockObjectPromise = TReturnType extends Promise ? ( + (TReturnType extends { resolves: any } ? + { [resolves]: OneArgumentRequiredFunction } : + { resolves: OneArgumentRequiredFunction }) & + (TReturnType extends { rejects: any } ? + { [rejects]: OneArgumentRequiredFunction } : + { rejects: OneArgumentRequiredFunction }) +) : {} + +type BaseMockObjectMixin = + MockObjectPromise & + ( + (TObject extends { returns: any } ? + { [returns]: OneArgumentRequiredFunction } : + { returns: OneArgumentRequiredFunction }) & + (TObject extends { throws: any } ? + { [throws]: OneArgumentRequiredFunction } : + { throws: OneArgumentRequiredFunction }) + ) + +type NoArgumentMockObjectMixin = + BaseMockObjectMixin & + (TObject extends { mimicks: any } ? + { [mimicks]: OneArgumentRequiredFunction<() => TReturnType, void> } : + { mimicks: OneArgumentRequiredFunction<() => TReturnType, void> }) + +type MockObjectMixin = + BaseMockObjectMixin & + (TReturnType extends { mimicks: any } ? + { [mimicks]: OneArgumentRequiredFunction<(...args: TArguments) => TReturnType, void> } : + { mimicks: OneArgumentRequiredFunction<(...args: TArguments) => TReturnType, void> }) type TerminatingFunction = ((...args: TArguments) => void) & ((arg: AllArguments) => void) type TryToExpandNonArgumentedTerminatingFunction = - TObject[TProperty] extends (...args: []) => unknown ? () => void : {} + TObject[TProperty] extends (...args: []) => unknown ? + () => void : + {} + type TryToExpandArgumentedTerminatingFunction = - TObject[TProperty] extends (...args: any) => any ? FunctionSubstituteWithOverloads : {} + TObject[TProperty] extends (...args: any) => any ? + FunctionSubstituteWithOverloads : + {} type TerminatingObject = { - [P in keyof T]: TryToExpandNonArgumentedTerminatingFunction & TryToExpandArgumentedTerminatingFunction & T[P]; + [P in keyof T]: + TryToExpandNonArgumentedTerminatingFunction & + TryToExpandArgumentedTerminatingFunction & + T[P]; } type TryToExpandNonArgumentedFunctionSubstitute = - TObject[TProperty] extends (...args: []) => infer R ? NoArgumentFunctionSubstitute : {} + TObject[TProperty] extends (...args: []) => infer R ? + NoArgumentFunctionSubstitute : + {} type TryToExpandArgumentedFunctionSubstitute = - TObject[TProperty] extends (...args: infer F) => infer R ? F extends [] ? {} : FunctionSubstituteWithOverloads : {} + TObject[TProperty] extends (...args: infer F) => any ? + F extends [] ? + {} : + FunctionSubstituteWithOverloads : + {} -type TryToExpandPropertySubstitute = PropertySubstitute +type TryToExpandPropertySubstitute = + PropertySubstitute type ObjectSubstituteTransformation> = { - [P in keyof T]: TryToExpandNonArgumentedFunctionSubstitute & TryToExpandArgumentedFunctionSubstitute & TryToExpandPropertySubstitute; + [P in keyof T]: + TryToExpandNonArgumentedFunctionSubstitute & + TryToExpandArgumentedFunctionSubstitute & + TryToExpandPropertySubstitute; } export type OmitProxyMethods = Omit; -export type ObjectSubstitute = ObjectSubstituteTransformation & { - [received](amount?: number): TerminatingObject; - [didNotReceive](): TerminatingObject; - [mimick](instance: OmitProxyMethods): void; - [clearReceivedCalls](): void; -} \ No newline at end of file + +export type ObjectSubstitute = + ObjectSubstituteTransformation & + ObjectSubstituteMethods + +type ObjectSubstituteMethods = + (T extends { received: any } ? + { [received](amount?: number): TerminatingObject } : + { received(amount?: number): TerminatingObject }) & + (T extends { didNotReceive: any } ? + { [didNotReceive](): TerminatingObject } : + { didNotReceive(): TerminatingObject }) & + (T extends { mimick: any } ? + { [mimick](instance: OmitProxyMethods): void } : + { mimick(instance: OmitProxyMethods): void }) & + (T extends { clearReceivedCalls: any } ? + { [clearReceivedCalls](): void } : + { clearReceivedCalls(): void }) \ No newline at end of file diff --git a/src/Types.ts b/src/Types.ts index 5586edd..f655ccc 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -1,12 +1,26 @@ -import type { clearReceivedCalls, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from "./Transformations" +import type { ClearReceivedCallsPropertyKey, DidNotReceivePropertyKey, MimickPropertyKey, MimicksPropertyKey, ReceivedPropertyKey, RejectsPropertyKey, ResolvesPropertyKey, ReturnsPropertyKey, ThrowsPropertyKey, clearReceivedCalls, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from "./Transformations" export type PropertyType = 'method' | 'property' -export type AssertionMethod = typeof received | typeof didNotReceive -export type ConfigurationMethod = typeof clearReceivedCalls | typeof mimick -export type SubstitutionMethod = typeof mimicks | typeof throws | typeof returns | typeof resolves | typeof rejects + +export type AssertionMethod = + ReceivedPropertyKey | + DidNotReceivePropertyKey + +export type ConfigurationMethod = + ClearReceivedCallsPropertyKey | + MimickPropertyKey + +export type SubstitutionMethod = + MimicksPropertyKey | + ThrowsPropertyKey | + ReturnsPropertyKey | + ResolvesPropertyKey | + RejectsPropertyKey export type FirstLevelMethod = AssertionMethod | ConfigurationMethod + export type SubstituteMethod = FirstLevelMethod | SubstitutionMethod + export type SubstituteContext = SubstituteMethod | 'none' export type FilterFunction = (item: T) => boolean \ No newline at end of file diff --git a/src/Utilities.ts b/src/Utilities.ts index ade6596..406ae6d 100644 --- a/src/Utilities.ts +++ b/src/Utilities.ts @@ -1,7 +1,7 @@ import { inspect } from 'util' import { RecordedArguments } from './RecordedArguments' import type { AssertionMethod, ConfigurationMethod, SubstituteMethod, SubstitutionMethod } from './Types' -import { clearReceivedCalls, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from './Transformations' +import { ClearReceivedCallsPropertyKey, DidNotReceivePropertyKey, MimickPropertyKey, MimicksPropertyKey, ReceivedPropertyKey, RejectsPropertyKey, ResolvesPropertyKey, ReturnsPropertyKey, ThrowsPropertyKey, clearReceivedCalls, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from './Transformations' export const PropertyType = { Method: 'method', @@ -9,16 +9,24 @@ export const PropertyType = { } as const export const isAssertionMethod = (property: PropertyKey): property is AssertionMethod => - property === received || property === didNotReceive + isReceivedFunction(property) || + isDidNotReceiveFunction(property) export const isConfigurationMethod = (property: PropertyKey): property is ConfigurationMethod => - property === clearReceivedCalls || property === mimick + isClearReceivedCallsFunction(property) || + isMimickFunction(property) export const isSubstitutionMethod = (property: PropertyKey): property is SubstitutionMethod => - property === mimicks || property === returns || property === throws || property === resolves || property === rejects + isMimicksFunction(property) || + isReturnsFunction(property) || + isThrowsFunction(property) || + isResolvesFunction(property) || + isRejectsFunction(property) export const isSubstituteMethod = (property: PropertyKey): property is SubstituteMethod => - isSubstitutionMethod(property) || isConfigurationMethod(property) || isAssertionMethod(property) + isSubstitutionMethod(property) || + isConfigurationMethod(property) || + isAssertionMethod(property) export const stringifyArguments = (args: RecordedArguments) => textModifier.faint( args.hasArguments() ? @@ -27,7 +35,8 @@ export const stringifyArguments = (args: RecordedArguments) => textModifier.fain ) export const stringifyCalls = (calls: RecordedArguments[]) => { - if (calls.length === 0) return ' (no calls)' + if (calls.length === 0) + return ' (no calls)' const key = '\n-> call with ' const callsDetails = calls.map(stringifyArguments) @@ -41,4 +50,45 @@ export const textModifier = { italic: (str: string) => baseTextModifier(str, 3, 23) } -export const plurify = (str: string, count?: number) => `${str}${count === 1 ? '' : 's'}` \ No newline at end of file +export const plurify = (str: string, count?: number) => `${str}${count === 1 ? '' : 's'}` + +export function isDidNotReceiveFunction(property: PropertyKey): property is DidNotReceivePropertyKey { + return property === didNotReceive || property === 'didNotReceive' +} + +export function isReceivedFunction(property: PropertyKey): property is ReceivedPropertyKey { + return property === received || property === 'received' +} + +export function isClearReceivedCallsFunction(property: PropertyKey): property is ClearReceivedCallsPropertyKey { + return property === clearReceivedCalls || property === 'clearReceivedCalls' +} + +export function isMimickFunction(property: PropertyKey): property is MimickPropertyKey { + return property === mimick || property === 'mimick' +} + +export function isRejectsFunction(property: PropertyKey): property is RejectsPropertyKey { + return property === rejects || property === 'rejects' +} + +export function isResolvesFunction(property: PropertyKey): property is ResolvesPropertyKey { + return property === resolves || property === 'resolves' +} + +export function isThrowsFunction(property: PropertyKey): property is ThrowsPropertyKey { + return property === throws || property === 'throws' +} + +export function isReturnsFunction(property: PropertyKey): property is ReturnsPropertyKey { + return property === returns || property === 'returns' +} + +export function isMimicksFunction(property: PropertyKey): property is MimicksPropertyKey { + return property === mimicks || property === 'mimicks' +} + +// https://www.totaltypescript.com/concepts/the-prettify-helper +export type Prettify = { + [K in keyof T]: T[K]; +} & {}; \ No newline at end of file From 123eed64b70001f38f573700a11896783fb77283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20P=C3=B6hlmann?= Date: Wed, 13 Aug 2025 16:36:27 +0200 Subject: [PATCH 07/11] refac: use symbols for context handling --- package-lock.json | 28 +-- package.json | 4 +- spec/ClearSubstitute.spec.ts | 8 +- spec/RecordedArguments.spec.ts | 4 +- spec/Recorder.spec.ts | 9 +- spec/RecordsSet.spec.ts | 4 +- spec/regression/didNotReceive.spec.ts | 6 +- spec/regression/index.test.ts | 18 +- spec/regression/issues/178.test.ts | 4 +- spec/regression/received.spec.ts | 6 +- spec/regression/returns.spec.ts | 2 +- src/Arguments.ts | 101 ----------- src/Transformations.ts | 170 ------------------- src/Types.ts | 32 ---- src/api/Arg.ts | 56 ++++++ src/api/Constants.ts | 13 ++ src/{ => api}/Substitute.ts | 6 +- src/api/index.ts | 3 + src/api/types/FunctionSubstitute.ts | 48 ++++++ src/api/types/Substitute.ts | 38 +++++ src/api/types/SubstituteMethods.ts | 31 ++++ src/api/types/SubstitutionLevel.ts | 41 +++++ src/api/types/index.ts | 9 + src/index.ts | 13 +- src/internals/Constants.ts | 18 ++ src/{ => internals}/RecordedArguments.ts | 4 +- src/{ => internals}/Recorder.ts | 2 +- src/{ => internals}/RecordsSet.ts | 2 +- src/{ => internals}/SubstituteException.ts | 5 +- src/{ => internals}/SubstituteNode.ts | 96 +++++------ src/{ => internals}/SubstituteNodeBase.ts | 0 src/internals/Types.ts | 42 +++++ src/internals/index.ts | 1 + src/internals/utilities/Guards.ts | 73 ++++++++ src/{ => internals}/utilities/Stringify.ts | 4 +- src/{ => internals}/utilities/TextBuilder.ts | 0 src/internals/utilities/Transformations.ts | 17 ++ src/{ => internals}/utilities/index.ts | 2 +- src/shared/Arguments.ts | 44 +++++ src/shared/Constants.ts | 75 ++++++++ src/shared/index.ts | 2 + src/utilities/Constants.ts | 40 ----- src/utilities/Guards.ts | 64 ------- 43 files changed, 621 insertions(+), 524 deletions(-) delete mode 100644 src/Arguments.ts delete mode 100644 src/Transformations.ts delete mode 100644 src/Types.ts create mode 100644 src/api/Arg.ts create mode 100644 src/api/Constants.ts rename src/{ => api}/Substitute.ts (69%) create mode 100644 src/api/index.ts create mode 100644 src/api/types/FunctionSubstitute.ts create mode 100644 src/api/types/Substitute.ts create mode 100644 src/api/types/SubstituteMethods.ts create mode 100644 src/api/types/SubstitutionLevel.ts create mode 100644 src/api/types/index.ts create mode 100644 src/internals/Constants.ts rename src/{ => internals}/RecordedArguments.ts (98%) rename src/{ => internals}/Recorder.ts (99%) rename src/{ => internals}/RecordsSet.ts (99%) rename src/{ => internals}/SubstituteException.ts (94%) rename src/{ => internals}/SubstituteNode.ts (77%) rename src/{ => internals}/SubstituteNodeBase.ts (100%) create mode 100644 src/internals/Types.ts create mode 100644 src/internals/index.ts create mode 100644 src/internals/utilities/Guards.ts rename src/{ => internals}/utilities/Stringify.ts (95%) rename src/{ => internals}/utilities/TextBuilder.ts (100%) create mode 100644 src/internals/utilities/Transformations.ts rename src/{ => internals}/utilities/index.ts (65%) create mode 100644 src/shared/Arguments.ts create mode 100644 src/shared/Constants.ts create mode 100644 src/shared/index.ts delete mode 100644 src/utilities/Constants.ts delete mode 100644 src/utilities/Guards.ts diff --git a/package-lock.json b/package-lock.json index 174383b..2efcbf7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,10 +11,10 @@ "devDependencies": { "@ava/typescript": "^3.0.1", "@sinonjs/fake-timers": "^11.2.2", - "@types/node": "^18.19.22", + "@types/node": "^18.19.122", "@types/sinonjs__fake-timers": "^8.1.5", "ava": "^4.3.3", - "typescript": "^5.4.2" + "typescript": "^5.9.2" }, "engines": { "node": ">=10" @@ -91,9 +91,9 @@ } }, "node_modules/@types/node": { - "version": "18.19.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.22.tgz", - "integrity": "sha512-p3pDIfuMg/aXBmhkyanPshdfJuX5c5+bQjYLIikPLXAUycEogij/c50n/C+8XOA5L93cU4ZRXtn+dNQGi0IZqQ==", + "version": "18.19.122", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.122.tgz", + "integrity": "sha512-yzegtT82dwTNEe/9y+CM8cgb42WrUfMMCg2QqSddzO1J6uPmBD7qKCZ7dOHZP2Yrpm/kb0eqdNMn2MUyEiqBmA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -1978,9 +1978,9 @@ } }, "node_modules/typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2282,9 +2282,9 @@ } }, "@types/node": { - "version": "18.19.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.22.tgz", - "integrity": "sha512-p3pDIfuMg/aXBmhkyanPshdfJuX5c5+bQjYLIikPLXAUycEogij/c50n/C+8XOA5L93cU4ZRXtn+dNQGi0IZqQ==", + "version": "18.19.122", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.122.tgz", + "integrity": "sha512-yzegtT82dwTNEe/9y+CM8cgb42WrUfMMCg2QqSddzO1J6uPmBD7qKCZ7dOHZP2Yrpm/kb0eqdNMn2MUyEiqBmA==", "dev": true, "requires": { "undici-types": "~5.26.4" @@ -3596,9 +3596,9 @@ "dev": true }, "typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 59b0dbc..cca6f69 100644 --- a/package.json +++ b/package.json @@ -46,10 +46,10 @@ "devDependencies": { "@ava/typescript": "^3.0.1", "@sinonjs/fake-timers": "^11.2.2", - "@types/node": "^18.19.22", + "@types/node": "^18.19.122", "@types/sinonjs__fake-timers": "^8.1.5", "ava": "^4.3.3", - "typescript": "^5.4.2" + "typescript": "^5.9.2" }, "volta": { "node": "18.19.1" diff --git a/spec/ClearSubstitute.spec.ts b/spec/ClearSubstitute.spec.ts index f16339d..d634be2 100644 --- a/spec/ClearSubstitute.spec.ts +++ b/spec/ClearSubstitute.spec.ts @@ -1,7 +1,7 @@ import test from 'ava' -import { Substitute, SubstituteOf, clearReceivedCalls, received, returns } from '../src' -import { SubstituteNode, instance } from '../src/SubstituteNode' +import { Substitute, SubstituteOf } from '../src' +import { SubstituteNode, instance } from '../src/internals/SubstituteNode' interface Calculator { add(a: number, b: number): number @@ -14,7 +14,7 @@ type InstanceReturningSubstitute = SubstituteOf & { [instance]: SubstituteNode } -test('clears received calls on a substitute', t => { +test.skip('clears received calls on a substitute', t => { const calculator = Substitute.for() as InstanceReturningSubstitute calculator.add(1, 1) calculator.add(1, 1).returns(2) @@ -25,4 +25,4 @@ test('clears received calls on a substitute', t => { t.throws(() => calculator.received().add(1, 1)) t.is(2, calculator.add(1, 1)) -}) \ No newline at end of file +}) diff --git a/spec/RecordedArguments.spec.ts b/spec/RecordedArguments.spec.ts index 103efb3..ea37629 100644 --- a/spec/RecordedArguments.spec.ts +++ b/spec/RecordedArguments.spec.ts @@ -2,7 +2,7 @@ import test from 'ava' import { inspect } from 'util' import { Arg } from '../src' -import { RecordedArguments } from '../src/RecordedArguments' +import { RecordedArguments } from '../src/internals/RecordedArguments' const testObject = { 'foo': 'bar' } const testArray = ['a', 1, true] @@ -135,4 +135,4 @@ test('generates custom text representation', t => { t.is(inspect(RecordedArguments.from([])), '()') t.is(inspect(RecordedArguments.from([undefined])), 'undefined') t.is(inspect(RecordedArguments.from([undefined, 1])), '(undefined, 1)') -}) \ No newline at end of file +}) diff --git a/spec/Recorder.spec.ts b/spec/Recorder.spec.ts index a743e26..47b55cf 100644 --- a/spec/Recorder.spec.ts +++ b/spec/Recorder.spec.ts @@ -1,10 +1,9 @@ import test from 'ava' -import { Recorder } from '../src/Recorder' -import { RecordsSet } from '../src/RecordsSet' -import { Substitute } from '../src/Substitute' -import { SubstituteNodeBase } from '../src/SubstituteNodeBase' -import { returns } from '../src' +import { Recorder } from '../src/internals/Recorder' +import { RecordsSet } from '../src/internals/RecordsSet' +import { Substitute } from '../src/api/Substitute' +import { SubstituteNodeBase } from '../src/internals/SubstituteNodeBase' const nodeFactory = (key: string) => { const node = Substitute.for() diff --git a/spec/RecordsSet.spec.ts b/spec/RecordsSet.spec.ts index 62b83ae..d17aec4 100644 --- a/spec/RecordsSet.spec.ts +++ b/spec/RecordsSet.spec.ts @@ -1,6 +1,6 @@ import test, { ExecutionContext } from 'ava' -import { RecordsSet } from '../src/RecordsSet' +import { RecordsSet } from '../src/internals/RecordsSet' const dataArray = [1, 2, 3] function* dataArrayGenerator() { @@ -63,4 +63,4 @@ test('applies and preserves the order of filter and map functions everytime the t.deepEqual([...setWithFilter], [1, 3]) t.deepEqual([...setWithFilterAndMap], ['1', '3']) t.deepEqual([...setWithFilterMapAndAnotherFilter], ['3']) -}) \ No newline at end of file +}) diff --git a/spec/regression/didNotReceive.spec.ts b/spec/regression/didNotReceive.spec.ts index 55f4dba..588da00 100644 --- a/spec/regression/didNotReceive.spec.ts +++ b/spec/regression/didNotReceive.spec.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, Arg, didNotReceive, received, returns } from '../../src' -import { SubstituteException } from '../../src/SubstituteException' +import { Substitute, Arg } from '../../src' +import { SubstituteException } from '../../src/internals/SubstituteException' interface Calculator { add(a: number, b: number): number @@ -49,4 +49,4 @@ test('not getting a property with mock correctly asserts the call count', t => { calculator.didNotReceive().isEnabled t.throws(() => calculator.received(1).isEnabled, { instanceOf: SubstituteException }) t.throws(() => calculator.received().isEnabled, { instanceOf: SubstituteException }) -}) \ No newline at end of file +}) diff --git a/spec/regression/index.test.ts b/spec/regression/index.test.ts index 6168763..45107c3 100644 --- a/spec/regression/index.test.ts +++ b/spec/regression/index.test.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, Arg, SubstituteOf, received, mimicks, resolves, returns } from '../../src' +import { Substitute, Arg, SubstituteOf, received } from '../../src' class Dummy { @@ -116,16 +116,16 @@ test('class method received', t => { `Expected to receive 7 method calls matching c('hi', 'there'), but received 4.\n` + 'All property or method calls to @Substitute.c received so far:\n' + `› ✔ @Substitute.c('hi', 'there')\n` + - ` called at (${process.cwd()}/spec/regression/index.test.ts:114:18)\n` + + ` called at (${process.cwd()}/spec/regression/index.test.ts:105:18)\n` + `› ✘ @Substitute.c('hi', 'the1re')\n` + - ` called at (${process.cwd()}/spec/regression/index.test.ts:115:18)\n` + + ` called at (${process.cwd()}/spec/regression/index.test.ts:106:18)\n` + `› ✔ @Substitute.c('hi', 'there')\n` + - ` called at (${process.cwd()}/spec/regression/index.test.ts:116:18)\n` + + ` called at (${process.cwd()}/spec/regression/index.test.ts:107:18)\n` + `› ✔ @Substitute.c('hi', 'there')\n` + - ` called at (${process.cwd()}/spec/regression/index.test.ts:117:18)\n` + + ` called at (${process.cwd()}/spec/regression/index.test.ts:108:18)\n` + `› ✔ @Substitute.c('hi', 'there')\n` + - ` called at (${process.cwd()}/spec/regression/index.test.ts:118:18)\n` - const { message } = t.throws(() => { substitute.received(7).c('hi', 'there') }) + ` called at (${process.cwd()}/spec/regression/index.test.ts:109:18)\n` + const { message } = t.throws(() => { substitute[received](7).c('hi', 'there') }) t.is(message.replace(textModifierRegex, ''), expectedMessage) }) @@ -143,8 +143,8 @@ test('received call matches after partial mocks using property instance mimicks' `Expected to receive 2 method calls matching c('lala', 'bar'), but received 1.\n` + 'All property or method calls to @Substitute.c received so far:\n' + `› ✔ @Substitute.c('lala', 'bar')\n` + - ` called at (${process.cwd()}/spec/regression/index.test.ts:145:13)\n` - const { message } = t.throws(() => substitute.received(2).c('lala', 'bar')) + ` called at (${process.cwd()}/spec/regression/index.test.ts:136:13)\n` + const { message } = t.throws(() => substitute[received](2).c('lala', 'bar')) t.is(message.replace(textModifierRegex, ''), expectedMessage) t.deepEqual(substitute.d, 1337) }) diff --git a/spec/regression/issues/178.test.ts b/spec/regression/issues/178.test.ts index 018b208..78efeb6 100644 --- a/spec/regression/issues/178.test.ts +++ b/spec/regression/issues/178.test.ts @@ -2,8 +2,8 @@ import test, { ExecutionContext, ThrowsExpectation } from 'ava' import * as fakeTimers from '@sinonjs/fake-timers' import { types } from 'util' -import { Substitute, didNotReceive, received, returns } from '../../../src' -import { SubstituteException } from '../../../src/SubstituteException' +import { Substitute } from '../../../src' +import { SubstituteException } from '../../../src/internals/SubstituteException' interface Library { subSection: Subsection diff --git a/spec/regression/received.spec.ts b/spec/regression/received.spec.ts index 8b6b112..69651f9 100644 --- a/spec/regression/received.spec.ts +++ b/spec/regression/received.spec.ts @@ -1,6 +1,6 @@ import test from 'ava' -import { Substitute, Arg, received, returns } from '../../src' -import { SubstituteException } from '../../src/SubstituteException' +import { Substitute, Arg } from '../../src' +import { SubstituteException } from '../../src/internals/SubstituteException' interface Calculator { add(a: number, b: number): number @@ -137,4 +137,4 @@ test('calling a method does not interfere with other properties or methods call t.throws(() => calculator.received().multiply(1, 1), { instanceOf: SubstituteException }) t.throws(() => calculator.received().multiply(Arg.all()), { instanceOf: SubstituteException }) -}) \ No newline at end of file +}) diff --git a/spec/regression/returns.spec.ts b/spec/regression/returns.spec.ts index 068412b..6ca7568 100644 --- a/spec/regression/returns.spec.ts +++ b/spec/regression/returns.spec.ts @@ -183,4 +183,4 @@ test('returns another substituted instance on a property', async t => { const result = await calculator.model t.is(result, modelResult) t.is(result.replace('...', '---'), 'TI-83') -}) \ No newline at end of file +}) diff --git a/src/Arguments.ts b/src/Arguments.ts deleted file mode 100644 index 6db6412..0000000 --- a/src/Arguments.ts +++ /dev/null @@ -1,101 +0,0 @@ -type PredicateFunction = (arg: T) => boolean -type ArgumentOptions = { - inverseMatch?: boolean -} -class BaseArgument { - private _description: string - constructor( - description: string, - private _matchingFunction: PredicateFunction, - private _options?: ArgumentOptions - ) { - this._description = `${this._options?.inverseMatch ? 'Not ' : ''}${description}` - } - - matches(arg: T) { - const inverseMatch = this._options?.inverseMatch ?? false - return inverseMatch ? !this._matchingFunction(arg) : this._matchingFunction(arg) - } - - toString() { - return this._description - } - - [Symbol.for('nodejs.util.inspect.custom')]() { - return this._description - } -} - -export class Argument extends BaseArgument { - private readonly _type = 'SingleArgument'; - get type(): 'SingleArgument' { - return this._type - } -} - -export class AllArguments extends BaseArgument { - private readonly _type = 'AllArguments'; - constructor() { - super('Arg.all{}', () => true, {}) - } - get type(): 'AllArguments' { - return this._type // TODO: Needed? - } -} - -export namespace Arg { - type Inversable = T & { not: T } - type ExtractFirstArg = T extends AllArguments ? TArgs[0] : T - type ReturnArg = Argument & T - const createInversable = (target: (arg: TArg, opt?: ArgumentOptions) => TReturn): Inversable<(arg: TArg) => TReturn> => { - const inversable = (arg: TArg) => target(arg) - inversable.not = (arg: TArg) => target(arg, { inverseMatch: true }) - return inversable - } - - const toStringify = (obj: any) => { - if (typeof obj.inspect === 'function') return obj.inspect() - if (typeof obj.toString === 'function') return obj.toString() - return obj - } - - export const all = (): AllArguments => new AllArguments() - - type Is = (predicate: PredicateFunction>) => ReturnArg> - const isFunction = (predicate: PredicateFunction>, options?: ArgumentOptions) => new Argument( - `Arg.is{${toStringify(predicate)}}`, predicate, options - ) - export const is = createInversable(isFunction) as Inversable - - type MapAnyReturn = T extends 'any' ? - ReturnArg : T extends 'string' ? - ReturnArg : T extends 'number' ? - ReturnArg : T extends 'boolean' ? - ReturnArg : T extends 'symbol' ? - ReturnArg : T extends 'undefined' ? - ReturnArg : T extends 'object' ? - ReturnArg : T extends 'function' ? - ReturnArg : T extends 'array' ? - ReturnArg : any - - type AnyType = 'string' | 'number' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | 'array' | 'any' - type Any = (type?: T) => MapAnyReturn - - const anyFunction = (type: AnyType = 'any', options?: ArgumentOptions) => { - const description = `Arg.any{${type}}` - const predicate = (x: any) => { - switch (type) { - case 'any': - return true - case 'array': - return Array.isArray(x) - default: - return typeof x === type - } - } - - return new Argument(description, predicate, options) - } - - export const any = createInversable(anyFunction) as Inversable -} \ No newline at end of file diff --git a/src/Transformations.ts b/src/Transformations.ts deleted file mode 100644 index dbad376..0000000 --- a/src/Transformations.ts +++ /dev/null @@ -1,170 +0,0 @@ -import type { AllArguments } from './Arguments'; -import type { FirstLevelMethod } from './Types'; -import { Prettify } from './Utilities'; - -export const received = Symbol('received'); -export const didNotReceive = Symbol('didNotReceive'); -export const mimick = Symbol('mimick'); -export const clearReceivedCalls = Symbol('clearReceivedCalls'); -export const mimicks = Symbol('mimicks'); -export const throws = Symbol('throws'); -export const returns = Symbol('returns'); -export const resolves = Symbol('resolves'); -export const rejects = Symbol('rejects'); - -export type ReceivedPropertyKey = typeof received | 'received'; -export type DidNotReceivePropertyKey = typeof didNotReceive | 'didNotReceive'; -export type MimickPropertyKey = typeof mimick | 'mimick'; -export type ClearReceivedCallsPropertyKey = typeof clearReceivedCalls | 'clearReceivedCalls'; -export type MimicksPropertyKey = typeof mimicks | 'mimicks'; -export type ThrowsPropertyKey = typeof throws | 'throws'; -export type ReturnsPropertyKey = typeof returns | 'returns'; -export type ResolvesPropertyKey = typeof resolves | 'resolves'; -export type RejectsPropertyKey = typeof rejects | 'rejects'; - -type FunctionSubstituteWithOverloads = - TFunc extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - (...args: infer A3): infer R3; - (...args: infer A4): infer R4; - (...args: infer A5): infer R5; - } ? - FunctionHandler & FunctionHandler & - FunctionHandler & FunctionHandler - & FunctionHandler : TFunc extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - (...args: infer A3): infer R3; - (...args: infer A4): infer R4; - } ? - FunctionHandler & FunctionHandler & - FunctionHandler & FunctionHandler : TFunc extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - (...args: infer A3): infer R3; - } ? - FunctionHandler & FunctionHandler - & FunctionHandler : TFunc extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - } ? - FunctionHandler & FunctionHandler : TFunc extends { - (...args: infer A1): infer R1; - } ? - FunctionHandler : never; - -type Equals = (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false; -type FunctionHandler = - Equals extends true ? - {} : Terminating extends true ? - TerminatingFunction : - FunctionSubstitute - -export type FunctionSubstitute = - ((...args: TArguments) => (TReturnType & MockObjectMixin)) & - ((allArguments: AllArguments) => (TReturnType & MockObjectMixin)) - -export type NoArgumentFunctionSubstitute = - () => - TReturnType & - NoArgumentMockObjectMixin; - -export type PropertySubstitute = - TReturnType & - NoArgumentMockObjectMixin; - -type OneArgumentRequiredFunction = (requiredInput: TArgs, ...restInputs: TArgs[]) => TReturnType; - -type MockObjectPromise = TReturnType extends Promise ? ( - (TReturnType extends { resolves: any } ? - { [resolves]: OneArgumentRequiredFunction } : - { resolves: OneArgumentRequiredFunction }) & - (TReturnType extends { rejects: any } ? - { [rejects]: OneArgumentRequiredFunction } : - { rejects: OneArgumentRequiredFunction }) -) : {} - -type BaseMockObjectMixin = - MockObjectPromise & - ( - (TObject extends { returns: any } ? - { [returns]: OneArgumentRequiredFunction } : - { returns: OneArgumentRequiredFunction }) & - (TObject extends { throws: any } ? - { [throws]: OneArgumentRequiredFunction } : - { throws: OneArgumentRequiredFunction }) - ) - -type NoArgumentMockObjectMixin = - BaseMockObjectMixin & - (TObject extends { mimicks: any } ? - { [mimicks]: OneArgumentRequiredFunction<() => TReturnType, void> } : - { mimicks: OneArgumentRequiredFunction<() => TReturnType, void> }) - -type MockObjectMixin = - BaseMockObjectMixin & - (TReturnType extends { mimicks: any } ? - { [mimicks]: OneArgumentRequiredFunction<(...args: TArguments) => TReturnType, void> } : - { mimicks: OneArgumentRequiredFunction<(...args: TArguments) => TReturnType, void> }) - -type TerminatingFunction = ((...args: TArguments) => void) & ((arg: AllArguments) => void) - -type TryToExpandNonArgumentedTerminatingFunction = - TObject[TProperty] extends (...args: []) => unknown ? - () => void : - {} - -type TryToExpandArgumentedTerminatingFunction = - TObject[TProperty] extends (...args: any) => any ? - FunctionSubstituteWithOverloads : - {} - -type TerminatingObject = { - [P in keyof T]: - TryToExpandNonArgumentedTerminatingFunction & - TryToExpandArgumentedTerminatingFunction & - T[P]; -} - -type TryToExpandNonArgumentedFunctionSubstitute = - TObject[TProperty] extends (...args: []) => infer R ? - NoArgumentFunctionSubstitute : - {} - -type TryToExpandArgumentedFunctionSubstitute = - TObject[TProperty] extends (...args: infer F) => any ? - F extends [] ? - {} : - FunctionSubstituteWithOverloads : - {} - -type TryToExpandPropertySubstitute = - PropertySubstitute - -type ObjectSubstituteTransformation> = { - [P in keyof T]: - TryToExpandNonArgumentedFunctionSubstitute & - TryToExpandArgumentedFunctionSubstitute & - TryToExpandPropertySubstitute; -} - -export type OmitProxyMethods = Omit; - -export type ObjectSubstitute = - ObjectSubstituteTransformation & - ObjectSubstituteMethods - -type ObjectSubstituteMethods = - (T extends { received: any } ? - { [received](amount?: number): TerminatingObject } : - { received(amount?: number): TerminatingObject }) & - (T extends { didNotReceive: any } ? - { [didNotReceive](): TerminatingObject } : - { didNotReceive(): TerminatingObject }) & - (T extends { mimick: any } ? - { [mimick](instance: OmitProxyMethods): void } : - { mimick(instance: OmitProxyMethods): void }) & - (T extends { clearReceivedCalls: any } ? - { [clearReceivedCalls](): void } : - { clearReceivedCalls(): void }) \ No newline at end of file diff --git a/src/Types.ts b/src/Types.ts deleted file mode 100644 index 271ed38..0000000 --- a/src/Types.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { ClearReceivedCallsPropertyKey, DidNotReceivePropertyKey, MimickPropertyKey, MimicksPropertyKey, ReceivedPropertyKey, RejectsPropertyKey, ResolvesPropertyKey, ReturnsPropertyKey, ThrowsPropertyKey, clearReceivedCalls, didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws } from "./Transformations" -import { RecordedArguments } from './RecordedArguments' - -export type PropertyType = 'method' | 'property' -export type AccessorType = 'get' | 'set' -export type AssertionMethod = ReceivedPropertyKey | DidNotReceivePropertyKey -export type ConfigurationMethod = ClearReceivedCallsPropertyKey | MimickPropertyKey -export type SubstitutionMethod = MimicksPropertyKey | - ThrowsPropertyKey | - ReturnsPropertyKey | - ResolvesPropertyKey | - RejectsPropertyKey - -export type FirstLevelMethod = AssertionMethod | ConfigurationMethod - -export type SubstituteMethod = FirstLevelMethod | SubstitutionMethod - -export type SubstituteContext = SubstituteMethod | 'none' - -export type ClearType = 'all' | 'receivedCalls' | 'substituteValues' - -export type SubstituteExceptionType = 'CallCountMismatch' | 'PropertyNotMocked' - -export type FilterFunction = (item: T) => boolean - -export type SubstituteNodeModel = { - propertyType: PropertyType - property: PropertyKey - context: SubstituteContext - recordedArguments: RecordedArguments - stack?: string -} diff --git a/src/api/Arg.ts b/src/api/Arg.ts new file mode 100644 index 0000000..6798412 --- /dev/null +++ b/src/api/Arg.ts @@ -0,0 +1,56 @@ +import { Argument, AllArguments, type ArgumentOptions, type PredicateFunction } from '../shared' + +type Inversable = T & { not: T } +type ExtractFirstArg = T extends AllArguments ? TArgs[0] : T +type ReturnArg = Argument & T +const createInversable = (target: (arg: TArg, opt?: ArgumentOptions) => TReturn): Inversable<(arg: TArg) => TReturn> => { + const inversable = (arg: TArg) => target(arg) + inversable.not = (arg: TArg) => target(arg, { inverseMatch: true }) + return inversable +} + +const toStringify = (obj: any) => { + if (typeof obj.inspect === 'function') return obj.inspect() + if (typeof obj.toString === 'function') return obj.toString() + return obj +} +const isFunction = (predicate: PredicateFunction>, options?: ArgumentOptions) => new Argument( + `Arg.is{${toStringify(predicate)}}`, predicate, options +) +type Is = (predicate: PredicateFunction>) => ReturnArg> + +type MapAnyReturn = T extends 'any' ? +ReturnArg : T extends 'string' ? +ReturnArg : T extends 'number' ? +ReturnArg : T extends 'boolean' ? +ReturnArg : T extends 'symbol' ? +ReturnArg : T extends 'undefined' ? +ReturnArg : T extends 'object' ? +ReturnArg : T extends 'function' ? +ReturnArg : T extends 'array' ? +ReturnArg : any + +type AnyType = 'string' | 'number' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | 'array' | 'any' +type Any = (type?: T) => MapAnyReturn + +const anyFunction = (type: AnyType = 'any', options?: ArgumentOptions) => { + const description = `Arg.any{${type}}` + const predicate = (x: any) => { + switch (type) { + case 'any': + return true + case 'array': + return Array.isArray(x) + default: + return typeof x === type + } + } + + return new Argument(description, predicate, options) +} + +export const Arg = { + all: (): AllArguments => new AllArguments(), + is: createInversable(isFunction) as Inversable, + any: createInversable(anyFunction) as Inversable +} diff --git a/src/api/Constants.ts b/src/api/Constants.ts new file mode 100644 index 0000000..d53a809 --- /dev/null +++ b/src/api/Constants.ts @@ -0,0 +1,13 @@ +import { constants as sharedConstants } from '../shared' + +const received: typeof sharedConstants.CONTEXT.received.symbol = sharedConstants.CONTEXT.received.symbol +const didNotReceive: typeof sharedConstants.CONTEXT.didNotReceive.symbol = sharedConstants.CONTEXT.didNotReceive.symbol +const clearReceivedCalls: typeof sharedConstants.CONTEXT.clearReceivedCalls.symbol = sharedConstants.CONTEXT.clearReceivedCalls.symbol +const mimick: typeof sharedConstants.CONTEXT.mimick.symbol = sharedConstants.CONTEXT.mimick.symbol +const mimicks: typeof sharedConstants.CONTEXT.mimicks.symbol = sharedConstants.CONTEXT.mimicks.symbol +const throws: typeof sharedConstants.CONTEXT.throws.symbol = sharedConstants.CONTEXT.throws.symbol +const returns: typeof sharedConstants.CONTEXT.returns.symbol = sharedConstants.CONTEXT.returns.symbol +const resolves: typeof sharedConstants.CONTEXT.resolves.symbol = sharedConstants.CONTEXT.resolves.symbol +const rejects: typeof sharedConstants.CONTEXT.rejects.symbol = sharedConstants.CONTEXT.rejects.symbol + +export { received, didNotReceive, clearReceivedCalls, mimick, mimicks, throws, returns, resolves, rejects } diff --git a/src/Substitute.ts b/src/api/Substitute.ts similarity index 69% rename from src/Substitute.ts rename to src/api/Substitute.ts index 3e211df..384ec39 100644 --- a/src/Substitute.ts +++ b/src/api/Substitute.ts @@ -1,5 +1,5 @@ -import { ObjectSubstitute } from './Transformations' -import { SubstituteNode } from './SubstituteNode' +import { ObjectSubstitute } from './types' +import { SubstituteNode } from '../internals' export type SubstituteOf = ObjectSubstitute & T @@ -8,4 +8,4 @@ export class Substitute { const substitute = SubstituteNode.createRoot() return substitute.proxy as unknown as SubstituteOf } -} \ No newline at end of file +} diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..eb6396c --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,3 @@ +export * from './Arg' +export * from './Substitute' +export * from './types' diff --git a/src/api/types/FunctionSubstitute.ts b/src/api/types/FunctionSubstitute.ts new file mode 100644 index 0000000..872faec --- /dev/null +++ b/src/api/types/FunctionSubstitute.ts @@ -0,0 +1,48 @@ +import { AllArguments } from '../../shared' +import { MockObjectMixin, NoArgumentMockObjectMixin } from './SubstitutionLevel' + +type TerminatingFunction = ((...args: TArguments) => void) & ((arg: AllArguments) => void) +export type FunctionSubstitute = + ((...args: TArguments) => (TReturnType & MockObjectMixin)) & + ((allArguments: AllArguments) => (TReturnType & MockObjectMixin)) + +export type FunctionSubstituteWithOverloads = + TFunc extends { + (...args: infer A1): infer R1; + (...args: infer A2): infer R2; + (...args: infer A3): infer R3; + (...args: infer A4): infer R4; + (...args: infer A5): infer R5; + } ? + FunctionHandler & FunctionHandler & + FunctionHandler & FunctionHandler + & FunctionHandler : TFunc extends { + (...args: infer A1): infer R1; + (...args: infer A2): infer R2; + (...args: infer A3): infer R3; + (...args: infer A4): infer R4; + } ? + FunctionHandler & FunctionHandler & + FunctionHandler & FunctionHandler : TFunc extends { + (...args: infer A1): infer R1; + (...args: infer A2): infer R2; + (...args: infer A3): infer R3; + } ? + FunctionHandler & FunctionHandler + & FunctionHandler : TFunc extends { + (...args: infer A1): infer R1; + (...args: infer A2): infer R2; + } ? + FunctionHandler & FunctionHandler : TFunc extends { + (...args: infer A1): infer R1; + } ? + FunctionHandler : never; + +type Equals = (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false; +type FunctionHandler = + Equals extends true ? + {} : Terminating extends true ? + TerminatingFunction : + FunctionSubstitute + +export type NoArgumentFunctionSubstitute = () => TReturnType & NoArgumentMockObjectMixin diff --git a/src/api/types/Substitute.ts b/src/api/types/Substitute.ts new file mode 100644 index 0000000..6921940 --- /dev/null +++ b/src/api/types/Substitute.ts @@ -0,0 +1,38 @@ +import { NoArgumentMockObjectMixin } from './SubstitutionLevel' +import { FunctionSubstituteWithOverloads, NoArgumentFunctionSubstitute } from './FunctionSubstitute' + +export type PropertySubstitute = + TReturnType & + NoArgumentMockObjectMixin; + +export type TryToExpandNonArgumentedTerminatingFunction = + TObject[TProperty] extends (...args: []) => unknown ? + () => void : + {} + +export type TryToExpandArgumentedTerminatingFunction = + TObject[TProperty] extends (...args: any) => any ? + FunctionSubstituteWithOverloads : + {} + +type TryToExpandNonArgumentedFunctionSubstitute = + TObject[TProperty] extends (...args: []) => infer R ? + NoArgumentFunctionSubstitute : + {} + +type TryToExpandArgumentedFunctionSubstitute = + TObject[TProperty] extends (...args: infer F) => any ? + F extends [] ? + {} : + FunctionSubstituteWithOverloads : + {} + +type TryToExpandPropertySubstitute = + PropertySubstitute + +export type ObjectSubstituteTransformation> = { + [P in keyof T]: + TryToExpandNonArgumentedFunctionSubstitute & + TryToExpandArgumentedFunctionSubstitute & + TryToExpandPropertySubstitute; +} diff --git a/src/api/types/SubstituteMethods.ts b/src/api/types/SubstituteMethods.ts new file mode 100644 index 0000000..cbd3971 --- /dev/null +++ b/src/api/types/SubstituteMethods.ts @@ -0,0 +1,31 @@ +import { constants } from '../../shared' +import { + TryToExpandNonArgumentedTerminatingFunction, + TryToExpandArgumentedTerminatingFunction +} from './Substitute' + +type TerminatingObject = { + [P in keyof T]: + TryToExpandNonArgumentedTerminatingFunction & + TryToExpandArgumentedTerminatingFunction & + T[P] +} + +export const received: typeof constants.CONTEXT.received.symbol = constants.CONTEXT.received.symbol +export const didNotReceive: typeof constants.CONTEXT.didNotReceive.symbol = constants.CONTEXT.didNotReceive.symbol +export const mimick: typeof constants.CONTEXT.mimick.symbol = constants.CONTEXT.mimick.symbol +export const clearReceivedCalls: typeof constants.CONTEXT.clearReceivedCalls.symbol = constants.CONTEXT.clearReceivedCalls.symbol + +export type ObjectSubstituteMethods = + (T extends { received: any } ? + { [received](amount?: number): TerminatingObject } : + { received(amount?: number): TerminatingObject }) & + (T extends { didNotReceive: any } ? + { [didNotReceive](): TerminatingObject } : + { didNotReceive(): TerminatingObject }) & + (T extends { mimick: any } ? + { [mimick](instance: T): void } : + { mimick(instance: T): void }) & + (T extends { clearReceivedCalls: any } ? + { [clearReceivedCalls](): void } : + { clearReceivedCalls(): void }) diff --git a/src/api/types/SubstitutionLevel.ts b/src/api/types/SubstitutionLevel.ts new file mode 100644 index 0000000..8cf326d --- /dev/null +++ b/src/api/types/SubstitutionLevel.ts @@ -0,0 +1,41 @@ +import { constants } from '../../shared' + +export const returns: typeof constants.CONTEXT.returns.symbol = constants.CONTEXT.returns.symbol +export const throws: typeof constants.CONTEXT.throws.symbol = constants.CONTEXT.throws.symbol +export const resolves: typeof constants.CONTEXT.resolves.symbol = constants.CONTEXT.resolves.symbol +export const rejects: typeof constants.CONTEXT.rejects.symbol = constants.CONTEXT.rejects.symbol +export const mimicks: typeof constants.CONTEXT.mimicks.symbol = constants.CONTEXT.mimicks.symbol + +type OneArgumentRequiredFunction = (requiredInput: TArgs, ...restInputs: TArgs[]) => TReturnType; + +type MockObjectPromise = TReturnType extends Promise ? ( + (TReturnType extends { resolves: any } ? + { [resolves]: OneArgumentRequiredFunction } : + { resolves: OneArgumentRequiredFunction }) & + (TReturnType extends { rejects: any } ? + { [rejects]: OneArgumentRequiredFunction } : + { rejects: OneArgumentRequiredFunction }) +) : {} + +type BaseMockObjectMixin = + MockObjectPromise & + ( + (TObject extends { returns: any } ? + { [returns]: OneArgumentRequiredFunction } : + { returns: OneArgumentRequiredFunction }) & + (TObject extends { throws: any } ? + { [throws]: OneArgumentRequiredFunction } : + { throws: OneArgumentRequiredFunction }) + ) + +export type NoArgumentMockObjectMixin = + BaseMockObjectMixin & + (TObject extends { mimicks: any } ? + { [mimicks]: OneArgumentRequiredFunction<() => TReturnType, void> } : + { mimicks: OneArgumentRequiredFunction<() => TReturnType, void> }) + +export type MockObjectMixin = + BaseMockObjectMixin & + (TReturnType extends { mimicks: any } ? + { [mimicks]: OneArgumentRequiredFunction<(...args: TArguments) => TReturnType, void> } : + { mimicks: OneArgumentRequiredFunction<(...args: TArguments) => TReturnType, void> }) diff --git a/src/api/types/index.ts b/src/api/types/index.ts new file mode 100644 index 0000000..e35305f --- /dev/null +++ b/src/api/types/index.ts @@ -0,0 +1,9 @@ +import { ObjectSubstituteTransformation } from './Substitute' +import { ObjectSubstituteMethods } from './SubstituteMethods' + +export type ObjectSubstitute = + ObjectSubstituteMethods & + ObjectSubstituteTransformation + +export { received } from './SubstituteMethods' +export { returns, throws, resolves, rejects, mimicks } from './SubstitutionLevel' diff --git a/src/index.ts b/src/index.ts index 96c3dc9..75f54fb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,3 @@ -import { Substitute, SubstituteOf } from './Substitute' -import { constants } from './utilities' -const clear = constants.CLEAR - -export { Arg } from './Arguments' -export { Substitute, SubstituteOf } -export { clearReceivedCalls, didNotReceive, mimick, received, mimicks, rejects, resolves, returns, throws } from './Transformations' -export { clear as ClearType } - -export default Substitute \ No newline at end of file +import { Substitute } from './api' +export * from './api' +export default Substitute diff --git a/src/internals/Constants.ts b/src/internals/Constants.ts new file mode 100644 index 0000000..5d40f45 --- /dev/null +++ b/src/internals/Constants.ts @@ -0,0 +1,18 @@ +const method = Symbol('method') +const property = Symbol('property') +const propertyTypes = { method, property } as const + +const get = Symbol('get') +const set = Symbol('set') +const accessorTypes = { get, set } as const + +const substituteExceptionTypes = { + callCountMismatch: 'CallCountMismatch', + propertyNotMocked: 'PropertyNotMocked' +} as const + +export const constants = { + PROPERTY: propertyTypes, + ACCESSOR: accessorTypes, + EXCEPTION: substituteExceptionTypes +} as const diff --git a/src/RecordedArguments.ts b/src/internals/RecordedArguments.ts similarity index 98% rename from src/RecordedArguments.ts rename to src/internals/RecordedArguments.ts index 3ea8910..470d489 100644 --- a/src/RecordedArguments.ts +++ b/src/internals/RecordedArguments.ts @@ -1,5 +1,5 @@ import { inspect, InspectOptions, isDeepStrictEqual } from 'util' -import { Argument, AllArguments } from './Arguments' +import { Argument, AllArguments } from '../shared' type ArgumentsClass = 'plain' | 'with-predicate' | 'wildcard' const argumentsClassDigitMapper: Record = { @@ -83,4 +83,4 @@ export class RecordedArguments { ? `(${inspectedValues.join(', ')})` : inspectedValues[0] } -} \ No newline at end of file +} diff --git a/src/Recorder.ts b/src/internals/Recorder.ts similarity index 99% rename from src/Recorder.ts rename to src/internals/Recorder.ts index bc6b97c..c6e3540 100644 --- a/src/Recorder.ts +++ b/src/internals/Recorder.ts @@ -70,4 +70,4 @@ export class Recorder { indexedRecord.delete(record) if (indexedRecord.size === 0) this.indexedRecords.delete(id) } -} \ No newline at end of file +} diff --git a/src/RecordsSet.ts b/src/internals/RecordsSet.ts similarity index 99% rename from src/RecordsSet.ts rename to src/internals/RecordsSet.ts index cc8026e..06fdab3 100644 --- a/src/RecordsSet.ts +++ b/src/internals/RecordsSet.ts @@ -72,4 +72,4 @@ export class RecordsSet extends Set { } } } -} \ No newline at end of file +} diff --git a/src/SubstituteException.ts b/src/internals/SubstituteException.ts similarity index 94% rename from src/SubstituteException.ts rename to src/internals/SubstituteException.ts index b396057..d84706e 100644 --- a/src/SubstituteException.ts +++ b/src/internals/SubstituteException.ts @@ -1,5 +1,6 @@ import { SubstituteNodeModel, SubstituteExceptionType } from './Types' -import { stringify, TextBuilder, constants } from './utilities' +import { stringify, TextBuilder } from './utilities' +import { constants } from './Constants' export class SubstituteException extends Error { public type?: SubstituteExceptionType @@ -43,4 +44,4 @@ export class SubstituteException extends Error { public static generic(message: string) { return new this(message) } -} \ No newline at end of file +} diff --git a/src/SubstituteNode.ts b/src/internals/SubstituteNode.ts similarity index 77% rename from src/SubstituteNode.ts rename to src/internals/SubstituteNode.ts index 1a13cf8..5d59e40 100644 --- a/src/SubstituteNode.ts +++ b/src/internals/SubstituteNode.ts @@ -2,58 +2,58 @@ import { inspect, InspectOptions, types } from 'util' import { SubstituteNodeBase } from './SubstituteNodeBase' import { RecordedArguments } from './RecordedArguments' -// import { PropertyType as PropertyTypeMap, isAssertionMethod, isSubstituteMethod, isSubstitutionMethod, textModifier, isConfigurationMethod, isThrowsFunction, isMimicksFunction, isResolvesFunction, isRejectsFunction, isReturnsFunction } from './Utilities' import { SubstituteException } from './SubstituteException' -import type { FilterFunction, SubstituteContext, SubstitutionMethod, ClearType, PropertyType, SubstituteNodeModel, AccessorType } from './Types' -import type { ObjectSubstitute, OmitProxyMethods } from './Transformations' -import { didNotReceive, mimick, mimicks, received, rejects, resolves, returns, throws, clearReceivedCalls } from './Transformations' -import { constants, is, stringify } from './utilities' +import { is, stringify, transform } from './utilities' + +import { constants } from './Constants' +import type { SubstituteContext, PropertyType, SubstituteNodeModel, AccessorType, SubstituteMethod, SubstitutionMethod } from './Types' +import { constants as sharedConstants } from '../shared/Constants' + export const instance = Symbol('Substitute:Instance') type SpecialProperty = typeof instance | typeof inspect.custom | typeof Symbol.toPrimitive | 'then' | 'toJSON' - -type RootContext = { substituteMethodsEnabled: boolean } +type TT = Exclude +type ObjectSubstitute = { + [P in typeof sharedConstants.CONTEXT[TT]['raw'] | typeof sharedConstants.CONTEXT[TT]['symbol']]: + (...args: any[]) => T | void | Promise | Promise +} export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitute, SubstituteNodeModel { private _proxy: SubstituteNode - private _rootContext: RootContext private _propertyType: PropertyType = constants.PROPERTY.property private _accessorType: AccessorType = constants.ACCESSOR.get private _recordedArguments: RecordedArguments = RecordedArguments.none() - private _context: SubstituteContext = constants.CONTEXT.none + private _context: SubstituteContext = sharedConstants.CONTEXT.none.symbol private _retrySubstitutionExecutionAttempt: boolean = false public stack?: string private constructor(key: PropertyKey, parent?: SubstituteNode) { super(key, parent) - if (this.isRoot()) { - this._rootContext = {} - } - else { - this._rootContext = this.root.rootContext - } this._proxy = new Proxy( this, { get: function (target, property) { if (target.isSpecialProperty(property)) { - // console.log('specialProperty', property) return target.evaluateSpecialProperty(property) } if (target._retrySubstitutionExecutionAttempt) { - // console.log('reattemptSubstitutionExecution', property) return target.reattemptSubstitutionExecution()[property] } const newNode = SubstituteNode.createChild(property, target) if (target.isAssertion) newNode.executeAssertion() - if (target.isRoot() && target.rootContext.substituteMethodsEnabled && (is.method.assertion(property) || is.method.configuration(property))) { + if (target.hasDepthOfAtLeast(1) && !target.hasContext && is.method.assertion(target.property)) { + target.assignContext(target.property) + target[target.property](...Array.isArray(target.recordedArguments.value) ? target.recordedArguments.value : [undefined]) + if (target.isAssertion) newNode.executeAssertion() + } + if (target.isRoot() && is.method.contextValue(property) && (is.method.assertion(property) || is.method.configuration(property))) { newNode.assignContext(property) return newNode[property].bind(newNode) } @@ -66,6 +66,12 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu if (target.isAssertion) newNode.executeAssertion() + if (target.hasDepthOfAtLeast(1) && !target.hasContext && is.method.assertion(target.property)) { + target.assignContext(target.property) + target[target.property](...Array.isArray(target.recordedArguments.value) ? target.recordedArguments.value : [undefined]) + if (target.isAssertion) newNode.executeAssertion() + } + return true }, apply: function (target, _thisArg, rawArguments) { @@ -82,19 +88,19 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu } public received(amount?: number | undefined) { - return this[received](amount); + return this[sharedConstants.CONTEXT.received.symbol](amount); } public didNotReceive() { - return this[didNotReceive](); + return this[sharedConstants.CONTEXT.didNotReceive.symbol](); } - public mimick(instance: OmitProxyMethods) { - return this[mimick](instance); + public mimick(instance: unknown) { + return this[sharedConstants.CONTEXT.mimick.symbol](instance); } public clearReceivedCalls() { - return this[clearReceivedCalls](); + return this[sharedConstants.CONTEXT.clearReceivedCalls.symbol](); } public static createRoot(): SubstituteNode { @@ -109,24 +115,20 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu return this._proxy } - public get rootContext(): RootContext { - return this._rootContext - } - get context(): SubstituteContext { return this._context } get hasContext(): boolean { - return this.context !== 'none' + return this.context !== sharedConstants.CONTEXT.none.symbol } get isSubstitution(): boolean { - return is.method.substitution(this.context) + return is.CONTEXT.substitution(this.context) } get isAssertion(): boolean { - return is.method.assertion(this.context) + return is.CONTEXT.assertion(this.context) } get property(): PropertyKey { @@ -145,21 +147,21 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu return this._recordedArguments } - public [received](amount?: number): SubstituteNode { + public [sharedConstants.CONTEXT.received.symbol](amount?: number): SubstituteNode { this.handleMethod([amount]) return this.proxy } - public [didNotReceive](): SubstituteNode { + public [sharedConstants.CONTEXT.didNotReceive.symbol](): SubstituteNode { this.handleMethod([0]) return this.proxy } - public [mimick](_instance: OmitProxyMethods) { + public [sharedConstants.CONTEXT.mimick.symbol](_instance: unknown) { throw new Error('Mimick is not implemented yet') } - public [clearReceivedCalls](): void { + public [sharedConstants.CONTEXT.clearReceivedCalls.symbol](): void { this.handleMethod([]) const filter = (node: SubstituteNode) => is.CONTEXT.none(node.context) @@ -170,10 +172,10 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu return types.isProxy(this) ? this[inspect.custom](...args) : this.printableForm(...args) } - private assignContext(context: SubstituteContext): void { - if (!is.method.substitute(context)) - throw new Error(`Cannot assign context for property ${context.toString()}`) - this._context = context + private assignContext(context: SubstituteMethod): void { + this._context = typeof context === 'string' ? + transform.rawSymbolContextMap[context] : + context } private reattemptSubstitutionExecution(): SubstituteNode | any { @@ -184,7 +186,6 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu private attemptSubstitutionExecution(): SubstituteNode | any { const mostSuitableSubstitution = this.getMostSuitableSubstitution() - // console.log('mostSuitableSubstitution', mostSuitableSubstitution) return mostSuitableSubstitution instanceof SubstituteNode ? mostSuitableSubstitution.executeSubstitution(this.recordedArguments) : this.proxy @@ -202,21 +203,21 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu ? this.child.recordedArguments.value?.shift() : this.child.recordedArguments.value[0] switch (substitutionMethod) { - case throws: + case sharedConstants.CONTEXT.throws.symbol: case 'throws': throw substitutionValue - case mimicks: + case sharedConstants.CONTEXT.mimicks.symbol: case 'mimicks': if (is.PROPERTY.property(this.propertyType)) return substitutionValue() if (!contextArguments.hasArguments()) throw new TypeError('Context arguments cannot be undefined') return substitutionValue(...contextArguments.value) - case resolves: + case sharedConstants.CONTEXT.resolves.symbol: case 'resolves': return Promise.resolve(substitutionValue) - case rejects: + case sharedConstants.CONTEXT.rejects.symbol: case 'rejects': return Promise.reject(substitutionValue) - case returns: + case sharedConstants.CONTEXT.returns.symbol: case 'returns': return substitutionValue default: @@ -237,11 +238,11 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu return } - const siblings = [...this.getAllSiblings().filter(n => !n.hasContext && n.accessorType === this.accessorType)] + const withContext = this.parent.property === sharedConstants.CONTEXT.received.symbol + const siblings = [...this.getAllSiblings().filter(n => (withContext || !n.hasContext) && n.accessorType === this.accessorType)] const hasBeenCalled = siblings.length > 0 const hasSiblingOfSamePropertyType = siblings.some(sibling => sibling.propertyType === this.propertyType) const allRecordedArguments = siblings.map(sibling => sibling.recordedArguments) - if ( !hasBeenCalled && (!finiteExpectation || expectedCount > 0) @@ -283,7 +284,6 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu private handleMethod(rawArguments: any[]): void { this._propertyType = constants.PROPERTY.method this._recordedArguments = RecordedArguments.from(rawArguments) - // console.log('handleMethod', rawArguments); } private getMostSuitableSubstitution(): SubstituteNode | void { @@ -330,4 +330,4 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu private printableForm(_: number, options: InspectOptions): string { return this.isRoot() ? stringify.rootNode(this, inspect(this.recorder, options)) : stringify.node(this, this.child, options) } -} \ No newline at end of file +} diff --git a/src/SubstituteNodeBase.ts b/src/internals/SubstituteNodeBase.ts similarity index 100% rename from src/SubstituteNodeBase.ts rename to src/internals/SubstituteNodeBase.ts diff --git a/src/internals/Types.ts b/src/internals/Types.ts new file mode 100644 index 0000000..560cf01 --- /dev/null +++ b/src/internals/Types.ts @@ -0,0 +1,42 @@ +import { RecordedArguments } from './RecordedArguments' +import type { constants } from './Constants' +import type { constants as sharedConstants } from '../shared' + +type ContextMap = typeof sharedConstants.CONTEXT +type Unfold = T[keyof T] + +export type ContextReceived = Unfold +export type ContextDidNotReceive = Unfold +export type ContextClearReceivedCalls = Unfold +export type ContextMimick = Unfold +export type ContextMimicks = Unfold +export type ContextThrows = Unfold +export type ContextReturns = Unfold +export type ContextResolves = Unfold +export type ContextRejects = Unfold + +export type AssertionMethod = ContextReceived | ContextDidNotReceive +export type ConfigurationMethod = ContextClearReceivedCalls | ContextMimick +export type SubstitutionMethod = ContextMimicks | ContextThrows | ContextReturns | ContextResolves | ContextRejects +export type ContextNone = Unfold + +export type SubstituteMethod = AssertionMethod | ConfigurationMethod | SubstitutionMethod +export type SubstituteContext = Exclude +// export type SubstituteContext2 = ContextMap[keyof ContextMap]['symbol'] +// export type ClearType = typeof constants.CLEAR[keyof typeof constants.CLEAR]['raw'] + +// export type ClearType = 'all' | 'receivedCalls' | 'substituteValues' +type PropertyType = typeof constants.PROPERTY.method | typeof constants.PROPERTY.property +type AccessorType = typeof constants.ACCESSOR.get | typeof constants.ACCESSOR.set +type SubstituteExceptionType = typeof constants.EXCEPTION.callCountMismatch | typeof constants.EXCEPTION.propertyNotMocked + +export type FilterFunction = (item: T) => boolean + +export type { PropertyType, AccessorType, SubstituteExceptionType } +export type SubstituteNodeModel = { + propertyType: PropertyType + property: PropertyKey + context: SubstituteContext + recordedArguments: RecordedArguments + stack?: string +} diff --git a/src/internals/index.ts b/src/internals/index.ts new file mode 100644 index 0000000..87c8170 --- /dev/null +++ b/src/internals/index.ts @@ -0,0 +1 @@ +export * from './SubstituteNode' diff --git a/src/internals/utilities/Guards.ts b/src/internals/utilities/Guards.ts new file mode 100644 index 0000000..bb6baa0 --- /dev/null +++ b/src/internals/utilities/Guards.ts @@ -0,0 +1,73 @@ +import { AssertionMethod, ConfigurationMethod, SubstituteMethod, SubstitutionMethod, SubstituteContext, PropertyType, ContextNone, ContextReceived, ContextDidNotReceive, ContextClearReceivedCalls, ContextMimick, ContextMimicks, ContextThrows, ContextReturns, ContextResolves, ContextRejects } from '../Types' +import { constants } from '../Constants' +import { constants as sharedConstants } from '../../shared' + +const isAssertionMethod = (property: PropertyKey): property is AssertionMethod => + property === sharedConstants.CONTEXT.received.raw || + property === sharedConstants.CONTEXT.received.symbol || + property === sharedConstants.CONTEXT.didNotReceive.raw || + property === sharedConstants.CONTEXT.didNotReceive.symbol +const isConfigurationMethod = (property: PropertyKey): property is ConfigurationMethod => + property === sharedConstants.CONTEXT.clearReceivedCalls.raw || + property === sharedConstants.CONTEXT.clearReceivedCalls.symbol || + property === sharedConstants.CONTEXT.mimick.raw || + property === sharedConstants.CONTEXT.mimick.symbol +const isSubstitutionMethod = (property: PropertyKey): property is SubstitutionMethod => + property === sharedConstants.CONTEXT.mimicks.raw || + property === sharedConstants.CONTEXT.mimicks.symbol || + property === sharedConstants.CONTEXT.returns.raw || + property === sharedConstants.CONTEXT.returns.symbol || + property === sharedConstants.CONTEXT.throws.raw || + property === sharedConstants.CONTEXT.throws.symbol || + property === sharedConstants.CONTEXT.resolves.raw || + property === sharedConstants.CONTEXT.resolves.symbol || + property === sharedConstants.CONTEXT.rejects.raw || + property === sharedConstants.CONTEXT.rejects.symbol +const isSubstituteMethod = (property: PropertyKey): property is SubstituteMethod => + isSubstitutionMethod(property) || isConfigurationMethod(property) || isAssertionMethod(property) + +const isPropertyProperty = (value: PropertyType): value is (typeof constants['PROPERTY']['property']) => value === constants.PROPERTY.property +const isPropertyMethod = (value: PropertyType): value is (typeof constants['PROPERTY']['method']) => value === constants.PROPERTY.method + +const isContextNone = (value: SubstituteContext): value is Exclude => value === sharedConstants.CONTEXT.none.symbol +const isContextReceived = (value: SubstituteContext): value is Exclude => value === sharedConstants.CONTEXT.received.symbol +const isContextDidNotReceive = (value: SubstituteContext): value is Exclude => value === sharedConstants.CONTEXT.didNotReceive.symbol +const isContextClearSubstitute = (value: SubstituteContext): value is Exclude => value === sharedConstants.CONTEXT.clearReceivedCalls.symbol +const isContextMimick = (value: SubstituteContext): value is Exclude => value === sharedConstants.CONTEXT.mimick.symbol +const isContextMimicks = (value: SubstituteContext): value is Exclude => value === sharedConstants.CONTEXT.mimicks.symbol +const isContextThrows = (value: SubstituteContext): value is Exclude => value === sharedConstants.CONTEXT.throws.symbol +const isContextReturns = (value: SubstituteContext): value is Exclude => value === sharedConstants.CONTEXT.returns.symbol +const isContextResolves = (value: SubstituteContext): value is Exclude => value === sharedConstants.CONTEXT.resolves.symbol +const isContextRejects = (value: SubstituteContext): value is Exclude => value === sharedConstants.CONTEXT.rejects.symbol +const isContextSubstitution = (value: SubstituteContext): value is Exclude => typeof value !== 'string' && isSubstitutionMethod(value) +const isContextAssertion = (value: SubstituteContext): value is Exclude => typeof value !== 'string' && isAssertionMethod(value) + +const isContextValue = (property: PropertyKey): property is SubstituteContext => typeof property !== 'string' && isSubstituteMethod(property) + +export const method = { + assertion: isAssertionMethod, + configuration: isConfigurationMethod, + substitution: isSubstitutionMethod, + substitute: isSubstituteMethod, + contextValue: isContextValue +} + +export const PROPERTY = { + property: isPropertyProperty, + method: isPropertyMethod +} + +export const CONTEXT = { + none: isContextNone, + received: isContextReceived, + didNotReceive: isContextDidNotReceive, + clearSubstitute: isContextClearSubstitute, + mimick: isContextMimick, + mimicks: isContextMimicks, + throws: isContextThrows, + returns: isContextReturns, + resolves: isContextResolves, + rejects: isContextRejects, + substitution: isContextSubstitution, + assertion: isContextAssertion +} diff --git a/src/utilities/Stringify.ts b/src/internals/utilities/Stringify.ts similarity index 95% rename from src/utilities/Stringify.ts rename to src/internals/utilities/Stringify.ts index b4ec0bb..099d7d9 100644 --- a/src/utilities/Stringify.ts +++ b/src/internals/utilities/Stringify.ts @@ -40,7 +40,7 @@ const stringifyExpectation = (expected: { count: number | undefined, call: Subst const textBuilder = new TextBuilder() textBuilder.add(expected.count === undefined ? '1 or more' : expected.count.toString(), t => t.bold()) .add(' ') - .add(expected.call.propertyType, t => t.bold()) + .add(expected.call.propertyType.description!, t => t.bold()) .add(plurify(' call', expected.count), t => t.bold()) .add(' matching ') .addParts(...stringifyCall({ callPath: expected.call.property.toString() })(expected.call).map(t => t.bold())) @@ -76,7 +76,7 @@ const stringifyNode = (node: SubstituteNodeModel, childNode: SubstituteNodeModel '' const s = hasContext ? `${label}${inspect(childNode?.recordedArguments, options)}` : '' - return `${node.propertyType}<${node.property.toString()}>: ${args}${s}` + return `${node.propertyType.description}<${node.property.toString()}>: ${args}${s}` } export const stringify = { diff --git a/src/utilities/TextBuilder.ts b/src/internals/utilities/TextBuilder.ts similarity index 100% rename from src/utilities/TextBuilder.ts rename to src/internals/utilities/TextBuilder.ts diff --git a/src/internals/utilities/Transformations.ts b/src/internals/utilities/Transformations.ts new file mode 100644 index 0000000..d593e98 --- /dev/null +++ b/src/internals/utilities/Transformations.ts @@ -0,0 +1,17 @@ +import { constants as sharedConstants } from '../../shared' + +type A = typeof sharedConstants.CONTEXT[Exclude] +type B = A['raw'] +type C = A['symbol'] + +export const rawSymbolContextMap: Record = { + [sharedConstants.CONTEXT.received.raw]: sharedConstants.CONTEXT.received.symbol, + [sharedConstants.CONTEXT.didNotReceive.raw]: sharedConstants.CONTEXT.didNotReceive.symbol, + [sharedConstants.CONTEXT.clearReceivedCalls.raw]: sharedConstants.CONTEXT.clearReceivedCalls.symbol, + [sharedConstants.CONTEXT.mimick.raw]: sharedConstants.CONTEXT.mimick.symbol, + [sharedConstants.CONTEXT.mimicks.raw]: sharedConstants.CONTEXT.mimicks.symbol, + [sharedConstants.CONTEXT.throws.raw]: sharedConstants.CONTEXT.throws.symbol, + [sharedConstants.CONTEXT.returns.raw]: sharedConstants.CONTEXT.returns.symbol, + [sharedConstants.CONTEXT.resolves.raw]: sharedConstants.CONTEXT.resolves.symbol, + [sharedConstants.CONTEXT.rejects.raw]: sharedConstants.CONTEXT.rejects.symbol, +} diff --git a/src/utilities/index.ts b/src/internals/utilities/index.ts similarity index 65% rename from src/utilities/index.ts rename to src/internals/utilities/index.ts index 6448fa3..4c92f76 100644 --- a/src/utilities/index.ts +++ b/src/internals/utilities/index.ts @@ -1,4 +1,4 @@ export * from './TextBuilder' export * from './Stringify' -export * from './Constants' export * as is from './Guards' +export * as transform from './Transformations' diff --git a/src/shared/Arguments.ts b/src/shared/Arguments.ts new file mode 100644 index 0000000..267c69c --- /dev/null +++ b/src/shared/Arguments.ts @@ -0,0 +1,44 @@ +export type PredicateFunction = (arg: T) => boolean +export type ArgumentOptions = { + inverseMatch?: boolean +} +class BaseArgument { + private _description: string + constructor( + description: string, + private _matchingFunction: PredicateFunction, + private _options?: ArgumentOptions + ) { + this._description = `${this._options?.inverseMatch ? 'Not ' : ''}${description}` + } + + matches(arg: T) { + const inverseMatch = this._options?.inverseMatch ?? false + return inverseMatch ? !this._matchingFunction(arg) : this._matchingFunction(arg) + } + + toString() { + return this._description + } + + [Symbol.for('nodejs.util.inspect.custom')]() { + return this._description + } +} + +export class Argument extends BaseArgument { + private readonly _type = 'SingleArgument'; + get type(): 'SingleArgument' { + return this._type + } +} + +export class AllArguments extends BaseArgument { + private readonly _type = 'AllArguments'; + constructor() { + super('Arg.all{}', () => true, {}) + } + get type(): 'AllArguments' { + return this._type // TODO: Needed? + } +} diff --git a/src/shared/Constants.ts b/src/shared/Constants.ts new file mode 100644 index 0000000..2ba6616 --- /dev/null +++ b/src/shared/Constants.ts @@ -0,0 +1,75 @@ +type ValueToMap = { [key in T as Uncapitalize]: key } + +export type AssertionMethodRaw = 'received' | 'didNotReceive' +export type ConfigurationMethodRaw = 'clearReceivedCalls' | 'mimick' +export type SubstitutionMethodRaw = 'mimicks' | 'throws' | 'returns' | 'resolves' | 'rejects' + +const contextMethodTypes: ValueToMap = { + received: 'received', + didNotReceive: 'didNotReceive', + clearReceivedCalls: 'clearReceivedCalls', + mimick: 'mimick', + mimicks: 'mimicks', + throws: 'throws', + returns: 'returns', + resolves: 'resolves', + rejects: 'rejects' +} + +const received = Symbol(contextMethodTypes.received) +const didNotReceive = Symbol(contextMethodTypes.didNotReceive) +const clearReceivedCalls = Symbol(contextMethodTypes.clearReceivedCalls) +const mimick = Symbol(contextMethodTypes.mimick) +const mimicks = Symbol(contextMethodTypes.mimicks) +const throws = Symbol(contextMethodTypes.throws) +const returns = Symbol(contextMethodTypes.returns) +const resolves = Symbol(contextMethodTypes.resolves) +const rejects = Symbol(contextMethodTypes.rejects) +const none = Symbol('none') + +const contextTypes = { + none: { + raw: 'none', + symbol: none + }, + received: { + raw: contextMethodTypes.received, + symbol: received + }, + didNotReceive: { + raw: contextMethodTypes.didNotReceive, + symbol: didNotReceive + }, + clearReceivedCalls: { + raw: contextMethodTypes.clearReceivedCalls, + symbol: clearReceivedCalls + }, + mimick: { + raw: contextMethodTypes.mimick, + symbol: mimick + }, + mimicks: { + raw: contextMethodTypes.mimicks, + symbol: mimicks + }, + throws: { + raw: contextMethodTypes.throws, + symbol: throws + }, + returns: { + raw: contextMethodTypes.returns, + symbol: returns + }, + resolves: { + raw: contextMethodTypes.resolves, + symbol: resolves + }, + rejects: { + raw: contextMethodTypes.rejects, + symbol: rejects + } +} as const + +export const constants = { + CONTEXT: contextTypes +} diff --git a/src/shared/index.ts b/src/shared/index.ts new file mode 100644 index 0000000..c827a99 --- /dev/null +++ b/src/shared/index.ts @@ -0,0 +1,2 @@ +export * from './Constants' +export * from './Arguments' diff --git a/src/utilities/Constants.ts b/src/utilities/Constants.ts deleted file mode 100644 index ae7f42f..0000000 --- a/src/utilities/Constants.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { AccessorType, ClearType, PropertyType, SubstituteContext, SubstituteExceptionType } from '../Types' - -type ValueToMap = { [key in T as Uncapitalize]: key } -const propertyTypes: ValueToMap = { - method: 'method', - property: 'property' -} -const accessorTypes: ValueToMap = { - get: 'get', - set: 'set' -} -const clearTypes: ValueToMap = { - all: 'all', - receivedCalls: 'receivedCalls', - substituteValues: 'substituteValues' -} -const contextTypes: ValueToMap = { - none: 'none', - received: 'received', - didNotReceive: 'didNotReceive', - clearSubstitute: 'clearSubstitute', - mimick: 'mimick', - mimicks: 'mimicks', - throws: 'throws', - returns: 'returns', - resolves: 'resolves', - rejects: 'rejects' -} -const SubstituteExceptionTypes: ValueToMap = { - callCountMismatch: 'CallCountMismatch', - propertyNotMocked: 'PropertyNotMocked' -} - -export const constants = { - PROPERTY: propertyTypes, - ACCESSOR: accessorTypes, - CLEAR: clearTypes, - CONTEXT: contextTypes, - EXCEPTION: SubstituteExceptionTypes -} \ No newline at end of file diff --git a/src/utilities/Guards.ts b/src/utilities/Guards.ts deleted file mode 100644 index 26db312..0000000 --- a/src/utilities/Guards.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { AssertionMethod, ClearType, ConfigurationMethod, PropertyType, SubstituteMethod, SubstitutionMethod, SubstituteContext } from '../Types' -import { constants } from './Constants' - -const isAssertionMethod = (property: PropertyKey): property is AssertionMethod => - property === 'received' || property === 'didNotReceive' -const isConfigurationMethod = (property: PropertyKey): property is ConfigurationMethod => property === 'clearSubstitute' || property === 'mimick' -const isSubstitutionMethod = (property: PropertyKey): property is SubstitutionMethod => - property === 'mimicks' || property === 'returns' || property === 'throws' || property === 'resolves' || property === 'rejects' -const isSubstituteMethod = (property: PropertyKey): property is SubstituteMethod => - isSubstitutionMethod(property) || isConfigurationMethod(property) || isAssertionMethod(property) - -const isPropertyProperty = (value: PropertyType): value is (typeof constants['PROPERTY']['property']) => value === constants.PROPERTY.property -const isPropertyMethod = (value: PropertyType): value is (typeof constants['PROPERTY']['method']) => value === constants.PROPERTY.method - -const isClearAll = (value: ClearType): value is (typeof constants['CLEAR']['all']) => value === constants.CLEAR.all -const isClearReceivedCalls = (value: ClearType): value is (typeof constants['CLEAR']['receivedCalls']) => value === constants.CLEAR.receivedCalls -const isClearSubstituteValues = (value: ClearType): value is (typeof constants['CLEAR']['substituteValues']) => value === constants.CLEAR.substituteValues - -const isContextNone = (value: SubstituteContext): value is (typeof constants['CONTEXT']['none']) => value === constants.CONTEXT.none -const isContextReceived = (value: SubstituteContext): value is (typeof constants['CONTEXT']['received']) => value === constants.CONTEXT.received -const isContextDidNotReceive = (value: SubstituteContext): value is (typeof constants['CONTEXT']['didNotReceive']) => value === constants.CONTEXT.didNotReceive -const isContextClearSubstitute = (value: SubstituteContext): value is (typeof constants['CONTEXT']['clearSubstitute']) => value === constants.CONTEXT.clearSubstitute -const isContextMimick = (value: SubstituteContext): value is (typeof constants['CONTEXT']['mimick']) => value === constants.CONTEXT.mimick -const isContextMimicks = (value: SubstituteContext): value is (typeof constants['CONTEXT']['mimicks']) => value === constants.CONTEXT.mimicks -const isContextThrows = (value: SubstituteContext): value is (typeof constants['CONTEXT']['throws']) => value === constants.CONTEXT.throws -const isContextReturns = (value: SubstituteContext): value is (typeof constants['CONTEXT']['returns']) => value === constants.CONTEXT.returns -const isContextResolves = (value: SubstituteContext): value is (typeof constants['CONTEXT']['resolves']) => value === constants.CONTEXT.resolves -const isContextRejects = (value: SubstituteContext): value is (typeof constants['CONTEXT']['rejects']) => value === constants.CONTEXT.rejects -const isContextSubstitution = (value: SubstituteContext): value is SubstitutionMethod => isSubstitutionMethod(value) -const isContextAssertion = (value: SubstituteContext): value is AssertionMethod => isAssertionMethod(value) - -export const method = { - assertion: isAssertionMethod, - configuration: isConfigurationMethod, - substitution: isSubstitutionMethod, - substitute: isSubstituteMethod, -} - -export const PROPERTY = { - property: isPropertyProperty, - method: isPropertyMethod -} - -export const CLEAR = { - all: isClearAll, - receivedCalls: isClearReceivedCalls, - substituteValues: isClearSubstituteValues -} - -export const CONTEXT = { - none: isContextNone, - received: isContextReceived, - didNotReceive: isContextDidNotReceive, - clearSubstitute: isContextClearSubstitute, - mimick: isContextMimick, - mimicks: isContextMimicks, - throws: isContextThrows, - returns: isContextReturns, - resolves: isContextResolves, - rejects: isContextRejects, - substitution: isContextSubstitution, - assertion: isContextAssertion -} - From 537fd3d0b1c7ef20b95ccdc5ee4c8c9d0f06fd3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20P=C3=B6hlmann?= Date: Wed, 13 Aug 2025 16:50:01 +0200 Subject: [PATCH 08/11] fix: update compatibility assertion --- compatibility-checks/test/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compatibility-checks/test/index.ts b/compatibility-checks/test/index.ts index ce90233..afaa9ff 100644 --- a/compatibility-checks/test/index.ts +++ b/compatibility-checks/test/index.ts @@ -10,7 +10,6 @@ type TestRunner = { test.describe('Verifies test runner compatibility', () => { const failingExec = (command: string): string => { - process.env const { FORCE_COLOR, ...environment } = { ...process.env, CI: '1', NO_COLOR: '1' } as Partial> try { execSync(command, { env: environment, stdio: 'pipe' }) @@ -43,11 +42,10 @@ test.describe('Verifies test runner compatibility', () => { `Could not find the expected failure location in the output. Expected "${testRunner.failureLocationText}"` ) assert.ok( - result.includes('SubstituteException: Expected'), + result.includes('SubstituteException: Call count mismatch'), `Could not find the expected exception message in the output: ${result}` ) }) }) }) }) - From 3a166eacbe5a489e9fd24de6b08156178e7fbd78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20P=C3=B6hlmann?= Date: Wed, 13 Aug 2025 17:00:39 +0200 Subject: [PATCH 09/11] fix: update test import --- spec/regression/Arguments.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/regression/Arguments.spec.ts b/spec/regression/Arguments.spec.ts index a04e5d2..1625fd4 100644 --- a/spec/regression/Arguments.spec.ts +++ b/spec/regression/Arguments.spec.ts @@ -1,6 +1,6 @@ import test from 'ava' import { Arg } from '../../src' -import { Argument } from '../../src/Arguments' +import { Argument } from '../../src/shared' const testObject = { "foo": "bar" } const testArray = ["a", 1, true] @@ -107,4 +107,4 @@ test('should not match the argument with the predicate function using Arg.is.not t.true(Arg.is.not(x => x === 'foo').matches('bar')) t.true(Arg.is.not(x => x % 2 == 0).matches(3)) -}) \ No newline at end of file +}) From 531a26ad7770b76a7cd53c6e4f92636881b62a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20P=C3=B6hlmann?= Date: Mon, 18 Aug 2025 21:25:20 +0200 Subject: [PATCH 10/11] fix: use symbol representation on clear method --- ava.config.js | 4 ++-- .../clearReceivedCalls.spec.ts} | 8 ++++---- src/api/types/SubstituteMethods.ts | 4 +--- src/api/types/index.ts | 2 +- src/internals/SubstituteNode.ts | 11 +++++++---- 5 files changed, 15 insertions(+), 14 deletions(-) rename spec/{ClearSubstitute.spec.ts => regression/clearReceivedCalls.spec.ts} (72%) diff --git a/ava.config.js b/ava.config.js index cc81cf1..d47ef16 100644 --- a/ava.config.js +++ b/ava.config.js @@ -1,6 +1,6 @@ module.exports = { - files: ['spec/**/*.ts'], + files: ['spec/**/*.spec.ts'], typescript: { compile: false, rewritePaths: { @@ -10,4 +10,4 @@ module.exports = { cache: false, failFast: true, failWithoutAssertions: true -} \ No newline at end of file +} diff --git a/spec/ClearSubstitute.spec.ts b/spec/regression/clearReceivedCalls.spec.ts similarity index 72% rename from spec/ClearSubstitute.spec.ts rename to spec/regression/clearReceivedCalls.spec.ts index d634be2..4325f5c 100644 --- a/spec/ClearSubstitute.spec.ts +++ b/spec/regression/clearReceivedCalls.spec.ts @@ -1,7 +1,7 @@ import test from 'ava' -import { Substitute, SubstituteOf } from '../src' -import { SubstituteNode, instance } from '../src/internals/SubstituteNode' +import { Substitute, SubstituteOf, clearReceivedCalls } from '../../src' +import { SubstituteNode, instance } from '../../src/internals/SubstituteNode' interface Calculator { add(a: number, b: number): number @@ -14,11 +14,11 @@ type InstanceReturningSubstitute = SubstituteOf & { [instance]: SubstituteNode } -test.skip('clears received calls on a substitute', t => { +test('clears received calls on a substitute', t => { const calculator = Substitute.for() as InstanceReturningSubstitute calculator.add(1, 1) calculator.add(1, 1).returns(2) - calculator.clearReceivedCalls(); + calculator[clearReceivedCalls](); t.is(calculator[instance].recorder.records.size, 2) t.is(calculator[instance].recorder.indexedRecords.size, 2) diff --git a/src/api/types/SubstituteMethods.ts b/src/api/types/SubstituteMethods.ts index cbd3971..e059418 100644 --- a/src/api/types/SubstituteMethods.ts +++ b/src/api/types/SubstituteMethods.ts @@ -26,6 +26,4 @@ export type ObjectSubstituteMethods = (T extends { mimick: any } ? { [mimick](instance: T): void } : { mimick(instance: T): void }) & - (T extends { clearReceivedCalls: any } ? - { [clearReceivedCalls](): void } : - { clearReceivedCalls(): void }) + { [clearReceivedCalls](): void } diff --git a/src/api/types/index.ts b/src/api/types/index.ts index e35305f..ef1e2ce 100644 --- a/src/api/types/index.ts +++ b/src/api/types/index.ts @@ -5,5 +5,5 @@ export type ObjectSubstitute = ObjectSubstituteMethods & ObjectSubstituteTransformation -export { received } from './SubstituteMethods' +export { received, didNotReceive, clearReceivedCalls, mimick } from './SubstituteMethods' export { returns, throws, resolves, rejects, mimicks } from './SubstitutionLevel' diff --git a/src/internals/SubstituteNode.ts b/src/internals/SubstituteNode.ts index 5d59e40..d549b2b 100644 --- a/src/internals/SubstituteNode.ts +++ b/src/internals/SubstituteNode.ts @@ -6,7 +6,7 @@ import { SubstituteException } from './SubstituteException' import { is, stringify, transform } from './utilities' import { constants } from './Constants' -import type { SubstituteContext, PropertyType, SubstituteNodeModel, AccessorType, SubstituteMethod, SubstitutionMethod } from './Types' +import type { SubstituteContext, PropertyType, SubstituteNodeModel, AccessorType, SubstituteMethod, SubstitutionMethod, AssertionMethod } from './Types' import { constants as sharedConstants } from '../shared/Constants' @@ -48,10 +48,12 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu const newNode = SubstituteNode.createChild(property, target) if (target.isAssertion) newNode.executeAssertion() - if (target.hasDepthOfAtLeast(1) && !target.hasContext && is.method.assertion(target.property)) { + const unresolvedAssertionFollowedBySubstitution = !target.hasContext && is.method.assertion(target.property) && !is.method.substitution(newNode.property) + if (target.hasDepthOfAtLeast(1) && unresolvedAssertionFollowedBySubstitution) { target.assignContext(target.property) - target[target.property](...Array.isArray(target.recordedArguments.value) ? target.recordedArguments.value : [undefined]) + target[target.context as AssertionMethod](...Array.isArray(target.recordedArguments.value) ? target.recordedArguments.value : [undefined]) if (target.isAssertion) newNode.executeAssertion() + // if (target.isConfiguration) newNode.executeConfiguration() } if (target.isRoot() && is.method.contextValue(property) && (is.method.assertion(property) || is.method.configuration(property))) { newNode.assignContext(property) @@ -238,7 +240,8 @@ export class SubstituteNode extends SubstituteNodeBase implements ObjectSubstitu return } - const withContext = this.parent.property === sharedConstants.CONTEXT.received.symbol + // const withContext = this.parent.property === sharedConstants.CONTEXT.received.symbol + const withContext = false const siblings = [...this.getAllSiblings().filter(n => (withContext || !n.hasContext) && n.accessorType === this.accessorType)] const hasBeenCalled = siblings.length > 0 const hasSiblingOfSamePropertyType = siblings.some(sibling => sibling.propertyType === this.propertyType) From f984f1bac274779610d4b72b61a7f431aae10346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20P=C3=B6hlmann?= Date: Mon, 18 Aug 2025 21:26:19 +0200 Subject: [PATCH 11/11] test: add overlapping interface regression --- spec/regression/overlap.spec.ts | 54 +++++++++++++++++++++++++++++++++ spec/regression/returns.spec.ts | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 spec/regression/overlap.spec.ts diff --git a/spec/regression/overlap.spec.ts b/spec/regression/overlap.spec.ts new file mode 100644 index 0000000..b2cc942 --- /dev/null +++ b/spec/regression/overlap.spec.ts @@ -0,0 +1,54 @@ +import test from 'ava' + +import { Substitute, received, didNotReceive } from '../../src' + +export interface OverlappingInterface { + received(value: number): string + didNotReceive(): unknown + clearReceivedCalls(): void +} + +test('(clearReceivedCalls) handles overlaps safely without substitutions', t => { + const substitute = Substitute.for>() + substitute.clearReceivedCalls() + t.notThrows(() => substitute.received(1).clearReceivedCalls()) + t.throws(() => substitute[didNotReceive]().clearReceivedCalls()) + }) + +test('(clearReceivedCalls) handles overlaps safely with substitutions', t => { + const substitute = Substitute.for>() + substitute.clearReceivedCalls().returns() + substitute.clearReceivedCalls() + t.notThrows(() => substitute.received(1).clearReceivedCalls()) + t.throws(() => substitute[didNotReceive]().clearReceivedCalls()) +}) + +test('(received) handles overlaps safely without substitutions', t => { + const substitute = Substitute.for>() + substitute.received(10) + t.notThrows(() => substitute[received](1).received(10)) + t.throws(() => substitute.didNotReceive().received(10)) +}) + +test('(received) handles overlaps safely with substitutions', t => { + const substitute = Substitute.for>() + substitute.received(10).returns('foo') + t.is('foo', substitute.received(10)) + t.notThrows(() => substitute[received](1).received(10)) + t.throws(() => substitute.didNotReceive().received(10)) +}) + +test('(didNotReceive) handles overlaps safely without substitutions', t => { + const substitute = Substitute.for>() + substitute.didNotReceive() + t.notThrows(() => substitute.received(1).didNotReceive()) + t.throws(() => substitute[didNotReceive]().didNotReceive()) +}) + +test('(didNotReceive) handles overlaps safely with substitutions', t => { + const substitute = Substitute.for>() + substitute.didNotReceive().returns('foo') + t.is('foo' as unknown, substitute.didNotReceive()) + t.notThrows(() => substitute.received(1).didNotReceive()) + t.throws(() => substitute[didNotReceive]().didNotReceive()) +}) diff --git a/spec/regression/returns.spec.ts b/spec/regression/returns.spec.ts index 6ca7568..6f8fb93 100644 --- a/spec/regression/returns.spec.ts +++ b/spec/regression/returns.spec.ts @@ -1,7 +1,7 @@ import test from 'ava' import { types } from 'util' -import { Substitute, Arg, returns } from '../../src' +import { Substitute, Arg } from '../../src' interface Calculator { add(a: number, b: number): number