diff --git a/CHANGELOG.md b/CHANGELOG.md index d62f2cc..b5754aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +# [2.0.0-beta.3](https://github.com/kwonoj/rx-sandbox/compare/v2.0.0-beta.2...v2.0.0-beta.3) (2020-11-06) + + +### Bug Fixes + +* **sandboxinstance:** expose scheduler instance ([6ada7a5](https://github.com/kwonoj/rx-sandbox/commit/6ada7a52c7e9bd25ae83b41eba1e786b194ffc93)) + + +### Features + +* **creattestscheduler:** support flush with native async tick ([93a99a4](https://github.com/kwonoj/rx-sandbox/commit/93a99a45bf63698f4e721a75f85fbf22f74eea68)) +* **marbleassert:** jasmine style toEqual() matcher ([6dcc7f8](https://github.com/kwonoj/rx-sandbox/commit/6dcc7f89936f971da66e0e4d0b286203b0fa68b9)) +* **sandbox:** accept config object ([3464710](https://github.com/kwonoj/rx-sandbox/commit/346471048564667c3a41df256467299fcb43eaa0)) +* **sandbox:** expose interface to create async flush scheduler ([f20fb40](https://github.com/kwonoj/rx-sandbox/commit/f20fb4088ae3fb2d3ee8501fa38b392685a67063)) +* **sandbox:** support to create async flush instance ([b9c5e71](https://github.com/kwonoj/rx-sandbox/commit/b9c5e71281cc3fd7def2c39ea94ab21c767585a8)) + + +### BREAKING CHANGES + +* **sandboxinstance:** scheduler no longer expose `maxFrame` property. use +property returned by `sandbox.create()`. +* **sandbox:** no longer directly export signatures for utilities, +such as `getMessages` due to overloaded signature behaviors. Use +`RxSandboxInstance['name']` or `RxAsyncSandboxInstance['name']` instead +to pick up signatures. + + + ## [1.0.4](https://github.com/kwonoj/rx-sandbox/compare/v1.0.3...v1.0.4) (2020-11-02) diff --git a/README.md b/README.md index 109c171..1927668 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,13 @@ it('testcase', () => { ```typescript rxSandbox.create(autoFlush?: boolean, frameTimeFactor?: number, maxFrameValue?: number): RxSandboxInstance + +rxSandbox.create({ + autoFlush?: boolean, + frameTimeFactor?: number, + maxFrameValue?: boolean, + flushWithAsyncTick?: boolean, +}): RxSandboxInstance | RxAsyncSandboxInstance ``` `frameTimeFactor` allows to override default frame passage `1` to given value. @@ -232,9 +239,33 @@ expect(messages).to.deep.equal(expected); //subsequent attempt will throw expect(() => getMessages(e1.mapTo('y'))).to.throw(); +``` + +### Scheduling flush into native async tick (Experimental) +If you create sandbox instance with `flushWithAsyncTick` option, sandbox will return instance of `RxAsyncSandboxInstance` which all of flush interfaces need to be asynchronously awaited: + +``` +interface RxAsyncSandboxInstance { + ..., + advanceTo(toFrame: number) => Promise; + flush: () => Promise; + getMessages: (observable: Observable, unsubscriptionMarbles?: string | null) => Promise; +} ``` +It is not uncommon practices chaining native async function or promise inside of observables, especially for inner observables. Let's say if there's a redux-observable epic like below + +``` +const epic = (actionObservable) => actionObservable.ofType(...).pipe((mergeMap) => { + return new Promise.resolve(...); +}) +``` + +Testing this epic via rxSandbox won't work. Once sandbox flush all internal actions synchronously, promises are still scheduled into next tick so there's no inner observable subscription value collected by flush. `RxAsyncSandboxInstance` in opposite no longer flush actions synchronously but schedule each individual action into promise tick to try to collect values from async functions. + +**NOTE: this is beta feature and likely have some issues. Also Until stablized internal implementation can change without semver breaking.** + #### Custom frame time factor Each timeframe `-` is predefined to `1`, can be overridden. diff --git a/jest.config.js b/jest.config.js index 5796d58..9bfcfa4 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,9 +11,6 @@ module.exports = { } }, "bail": true, - "resetMocks": true, - "clearMocks": true, - "resetModules": true, "testEnvironment": "node", "moduleFileExtensions": [ "js", diff --git a/package-lock.json b/package-lock.json index 98a06d2..6d02367 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "rx-sandbox", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -51,12 +51,12 @@ } }, "@babel/generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", - "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", "dev": true, "requires": { - "@babel/types": "^7.12.1", + "@babel/types": "^7.12.5", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -99,12 +99,12 @@ } }, "@babel/helper-module-imports": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz", - "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.5" } }, "@babel/helper-module-transforms": { @@ -140,15 +140,15 @@ "dev": true }, "@babel/helper-replace-supers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.1.tgz", - "integrity": "sha512-zJjTvtNJnCFsCXVi5rUInstLd/EIVNmIKA1Q9ynESmMBWPWd+7sdR+G4/wdu+Mppfep0XLyG2m7EBPvjCeFyrw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", "dev": true, "requires": { "@babel/helper-member-expression-to-functions": "^7.12.1", "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1" + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, "@babel/helper-simple-access": { @@ -175,14 +175,14 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/helpers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.1.tgz", - "integrity": "sha512-9JoDSBGoWtmbay98efmT2+mySkwjzeFeAL9BuWNoVQpkPFQF8SIIFUfY5os9u8wVzglzoiPRSW7cuJmBDUt43g==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", "dev": true, "requires": { "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1" + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, "@babel/highlight": { @@ -242,9 +242,9 @@ } }, "@babel/parser": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", - "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -376,26 +376,26 @@ } }, "@babel/traverse": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.1.tgz", - "integrity": "sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", + "@babel/generator": "^7.12.5", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.1", - "@babel/types": "^7.12.1", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -673,85 +673,13 @@ "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } - }, - "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" - } - }, - "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" - } - }, - "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 - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "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" - } } } }, "@jest/core": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.2.tgz", - "integrity": "sha512-x0v0LVlEslGYGYk4StT90NUp7vbFBrh0K7KDyAg3hMhG0drrxOIQHsY05uC7XVlKHXFgGI+HdnU35qewMZOLFQ==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", "dev": true, "requires": { "@jest/console": "^26.6.2", @@ -765,14 +693,14 @@ "exit": "^0.1.2", "graceful-fs": "^4.2.4", "jest-changed-files": "^26.6.2", - "jest-config": "^26.6.2", + "jest-config": "^26.6.3", "jest-haste-map": "^26.6.2", "jest-message-util": "^26.6.2", "jest-regex-util": "^26.0.0", "jest-resolve": "^26.6.2", - "jest-resolve-dependencies": "^26.6.2", - "jest-runner": "^26.6.2", - "jest-runtime": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", "jest-snapshot": "^26.6.2", "jest-util": "^26.6.2", "jest-validate": "^26.6.2", @@ -830,23 +758,6 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, "micromatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", @@ -857,18 +768,6 @@ "picomatch": "^2.0.5" } }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -972,78 +871,6 @@ "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } - }, - "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" - } - }, - "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" - } - }, - "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 - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "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" - } } } }, @@ -1070,200 +897,84 @@ "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + } + } + }, + "@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" } - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "expect": { + } + } + }, + "@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "dependencies": { + "@jest/types": { "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.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" - } - }, - "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 - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "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" - } - } - } - }, - "@jest/reporters": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", - "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.3", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.6.2", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "node-notifier": "^8.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^7.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - } - } - }, - "@jest/source-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", - "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -1276,16 +987,16 @@ } }, "@jest/test-sequencer": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.2.tgz", - "integrity": "sha512-iHiEXLMP69Ohe6kFMOVz6geADRxwK+OkLGg0VIGfZrUdkJGiCpghkMb2946FLh7jvzOwwZGyQoMi+kaHiOdM5g==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", "dev": true, "requires": { "@jest/test-result": "^26.6.2", "graceful-fs": "^4.2.4", "jest-haste-map": "^26.6.2", - "jest-runner": "^26.6.2", - "jest-runtime": "^26.6.2" + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" } }, "@jest/transform": { @@ -1738,9 +1449,9 @@ "dev": true }, "babel-jest": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.2.tgz", - "integrity": "sha512-pysyz/mZ7T5sozKnvSa1n7QEf22W9yc+dUmn2zNuQTN0saG51q8A/8k9wbED9X4YNxmwjuhIwf4JRXXQGzui3Q==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", "dev": true, "requires": { "@jest/transform": "^26.6.2", @@ -2354,22 +2065,34 @@ "dev": true }, "conventional-changelog": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.23.tgz", - "integrity": "sha512-sScUu2NHusjRC1dPc5p8/b3kT78OYr95/Bx7Vl8CPB8tF2mG1xei5iylDTRjONV5hTlzt+Cn/tBWrKdd299b7A==", - "dev": true, - "requires": { - "conventional-changelog-angular": "^5.0.11", - "conventional-changelog-atom": "^2.0.7", - "conventional-changelog-codemirror": "^2.0.7", - "conventional-changelog-conventionalcommits": "^4.4.0", - "conventional-changelog-core": "^4.2.0", - "conventional-changelog-ember": "^2.0.8", - "conventional-changelog-eslint": "^3.0.8", - "conventional-changelog-express": "^2.0.5", - "conventional-changelog-jquery": "^3.0.10", - "conventional-changelog-jshint": "^2.0.8", + "version": "3.1.24", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.24.tgz", + "integrity": "sha512-ed6k8PO00UVvhExYohroVPXcOJ/K1N0/drJHx/faTH37OIZthlecuLIRX/T6uOp682CAoVoFpu+sSEaeuH6Asg==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.12", + "conventional-changelog-atom": "^2.0.8", + "conventional-changelog-codemirror": "^2.0.8", + "conventional-changelog-conventionalcommits": "^4.5.0", + "conventional-changelog-core": "^4.2.1", + "conventional-changelog-ember": "^2.0.9", + "conventional-changelog-eslint": "^3.0.9", + "conventional-changelog-express": "^2.0.6", + "conventional-changelog-jquery": "^3.0.11", + "conventional-changelog-jshint": "^2.0.9", "conventional-changelog-preset-loader": "^2.3.4" + }, + "dependencies": { + "conventional-changelog-angular": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", + "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + } + } } }, "conventional-changelog-angular": { @@ -2383,40 +2106,194 @@ } }, "conventional-changelog-atom": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.7.tgz", - "integrity": "sha512-7dOREZwzB+tCEMjRTDfen0OHwd7vPUdmU0llTy1eloZgtOP4iSLVzYIQqfmdRZEty+3w5Jz+AbhfTJKoKw1JeQ==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", + "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-cli": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.1.0.tgz", - "integrity": "sha512-hZ8EcpxV4LcGOZwH+U5LJQDnyA4o/uyUdmIGzmFZMB4caujavvDBo/iTgVihk0m1QKkEhJgulagrILSm1JCakA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.1.1.tgz", + "integrity": "sha512-xMGQdKJ+4XFDDgfX5aK7UNFduvJMbvF5BB+g0OdVhA3rYdYyhctrIE2Al+WYdZeKTdg9YzMWF2iFPT8MupIwng==", "dev": true, "requires": { "add-stream": "^1.0.0", - "conventional-changelog": "^3.1.23", + "conventional-changelog": "^3.1.24", "lodash": "^4.17.15", - "meow": "^7.0.0", + "meow": "^8.0.0", "tempfile": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "hosted-git-info": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", + "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "meow": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.0.0.tgz", + "integrity": "sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", + "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", + "dev": true, + "requires": { + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.1" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "type-fest": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.0.tgz", + "integrity": "sha512-fbDukFPnJBdn2eZ3RR+5mK2slHLFd6gYHY7jna1KWWy4Yr4XysHuCdXRzy+RiG/HwG4WJat00vdC2UHky5eKiQ==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.3.tgz", + "integrity": "sha512-emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww==", + "dev": true + } } }, "conventional-changelog-codemirror": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.7.tgz", - "integrity": "sha512-Oralk1kiagn3Gb5cR5BffenWjVu59t/viE6UMD/mQa1hISMPkMYhJIqX+CMeA1zXgVBO+YHQhhokEj99GP5xcg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", + "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-conventionalcommits": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.4.0.tgz", - "integrity": "sha512-ybvx76jTh08tpaYrYn/yd0uJNLt5yMrb1BphDe4WBredMlvPisvMghfpnJb6RmRNcqXeuhR6LfGZGewbkRm9yA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.5.0.tgz", + "integrity": "sha512-buge9xDvjjOxJlyxUnar/+6i/aVEVGA7EEh4OafBCXPlLUQPGbRUBhBUveWRxzvR8TEjhKEP4BdepnpG2FSZXw==", "dev": true, "requires": { "compare-func": "^2.0.0", @@ -2425,26 +2302,26 @@ } }, "conventional-changelog-core": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.0.tgz", - "integrity": "sha512-8+xMvN6JvdDtPbGBqA7oRNyZD4od1h/SIzrWqHcKZjitbVXrFpozEeyn4iI4af1UwdrabQpiZMaV07fPUTGd4w==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.1.tgz", + "integrity": "sha512-8cH8/DEoD3e5Q6aeogdR5oaaKs0+mG6+f+Om0ZYt3PNv7Zo0sQhu4bMDRsqAF+UTekTAtP1W/C41jH/fkm8Jtw==", "dev": true, "requires": { "add-stream": "^1.0.0", - "conventional-changelog-writer": "^4.0.17", - "conventional-commits-parser": "^3.1.0", + "conventional-changelog-writer": "^4.0.18", + "conventional-commits-parser": "^3.2.0", "dateformat": "^3.0.0", "get-pkg-repo": "^1.0.0", "git-raw-commits": "2.0.0", "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^4.1.0", + "git-semver-tags": "^4.1.1", "lodash": "^4.17.15", - "normalize-package-data": "^2.3.5", + "normalize-package-data": "^3.0.0", "q": "^1.5.1", "read-pkg": "^3.0.0", "read-pkg-up": "^3.0.0", "shelljs": "^0.8.3", - "through2": "^3.0.0" + "through2": "^4.0.0" }, "dependencies": { "camelcase": { @@ -2453,15 +2330,19 @@ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "conventional-commits-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.0.tgz", + "integrity": "sha512-XmJiXPxsF0JhAKyfA2Nn+rZwYKJ60nanlbSWwwkGwLQFbugsc0gv1rzc7VbbUWAzJfR1qR87/pNgv9NgmxtBMQ==", "dev": true, "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^2.0.0", + "through2": "^4.0.0", + "trim-off-newlines": "^1.0.0" } }, "dargs": { @@ -2473,6 +2354,16 @@ "number-is-nan": "^1.0.0" } }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, "git-raw-commits": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz", @@ -2486,6 +2377,93 @@ "through2": "^2.0.0" }, "dependencies": { + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" + } + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -2495,15 +2473,39 @@ "readable-stream": "~2.3.6", "xtend": "~4.0.1" } + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true } } }, + "hosted-git-info": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", + "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "indent-string": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", "dev": true }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, "map-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", @@ -2511,63 +2513,125 @@ "dev": true }, "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", - "dev": true, - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.0.0.tgz", + "integrity": "sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, - "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "normalize-package-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", + "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", "dev": true, "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.1" } }, - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", - "dev": true - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "p-try": "^2.0.0" } }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" + "p-limit": "^2.2.0" } }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2583,54 +2647,69 @@ "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", "dev": true }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + }, + "type-fest": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.0.tgz", + "integrity": "sha512-fbDukFPnJBdn2eZ3RR+5mK2slHLFd6gYHY7jna1KWWy4Yr4XysHuCdXRzy+RiG/HwG4WJat00vdC2UHky5eKiQ==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.3.tgz", + "integrity": "sha512-emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww==", "dev": true } } }, "conventional-changelog-ember": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.8.tgz", - "integrity": "sha512-JEMEcUAMg4Q9yxD341OgWlESQ4gLqMWMXIWWUqoQU8yvTJlKnrvcui3wk9JvnZQyONwM2g1MKRZuAjKxr8hAXA==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", + "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-eslint": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.8.tgz", - "integrity": "sha512-5rTRltgWG7TpU1PqgKHMA/2ivjhrB+E+S7OCTvj0zM/QGg4vmnVH67Vq/EzvSNYtejhWC+OwzvDrLk3tqPry8A==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", + "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-express": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.5.tgz", - "integrity": "sha512-pW2hsjKG+xNx/Qjof8wYlAX/P61hT5gQ/2rZ2NsTpG+PgV7Rc8RCfITvC/zN9K8fj0QmV6dWmUefCteD9baEAw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", + "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-jquery": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.10.tgz", - "integrity": "sha512-QCW6wF8QgPkq2ruPaxc83jZxoWQxLkt/pNxIDn/oYjMiVgrtqNdd7lWe3vsl0hw5ENHNf/ejXuzDHk6suKsRpg==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", + "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-jshint": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.8.tgz", - "integrity": "sha512-hB/iI0IiZwnZ+seYI+qEQ4b+EMQSEC8jGIvhO2Vpz1E5p8FgLz75OX8oB1xJWl+s4xBMB6f8zJr0tC/BL7YOjw==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", + "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", "dev": true, "requires": { "compare-func": "^2.0.0", @@ -2644,28 +2723,197 @@ "dev": true }, "conventional-changelog-writer": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.17.tgz", - "integrity": "sha512-IKQuK3bib/n032KWaSb8YlBFds+aLmzENtnKtxJy3+HqDq5kohu3g/UdNbIHeJWygfnEbZjnCKFxAW0y7ArZAw==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.18.tgz", + "integrity": "sha512-mAQDCKyB9HsE8Ko5cCM1Jn1AWxXPYV0v8dFPabZRkvsiWUul2YyAqbIaoMKF88Zf2ffnOPSvKhboLf3fnjo5/A==", "dev": true, "requires": { "compare-func": "^2.0.0", - "conventional-commits-filter": "^2.0.6", + "conventional-commits-filter": "^2.0.7", "dateformat": "^3.0.0", "handlebars": "^4.7.6", "json-stringify-safe": "^5.0.1", "lodash": "^4.17.15", - "meow": "^7.0.0", + "meow": "^8.0.0", "semver": "^6.0.0", "split": "^1.0.0", - "through2": "^3.0.0" + "through2": "^4.0.0" }, "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "hosted-git-info": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", + "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "meow": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.0.0.tgz", + "integrity": "sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", + "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", + "dev": true, + "requires": { + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + }, + "type-fest": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.0.tgz", + "integrity": "sha512-fbDukFPnJBdn2eZ3RR+5mK2slHLFd6gYHY7jna1KWWy4Yr4XysHuCdXRzy+RiG/HwG4WJat00vdC2UHky5eKiQ==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.3.tgz", + "integrity": "sha512-emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww==", + "dev": true } } }, @@ -2676,9 +2924,9 @@ "dev": true }, "conventional-commits-filter": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.6.tgz", - "integrity": "sha512-4g+sw8+KA50/Qwzfr0hL5k5NWxqtrOVw4DDk3/h6L85a9Gz0/Eqp3oP+CWCNfesBvZZZEFHF7OTEbRe+yYSyKw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", "dev": true, "requires": { "lodash.ismatch": "^4.4.0", @@ -3556,9 +3804,9 @@ "dev": true }, "fsevents": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.0.tgz", - "integrity": "sha512-pKnaUh2TNvk+/egJdBw1h46LwyLx8BzEq+MGCf/RMCVfEHHsGOCWG00dqk91kUPPArIIwMBg9T/virxwzP03cA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", + "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", "dev": true, "optional": true }, @@ -3857,20 +4105,180 @@ } }, "git-semver-tags": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.0.tgz", - "integrity": "sha512-TcxAGeo03HdErzKzi4fDD+xEL7gi8r2Y5YSxH6N2XYdVSV5UkBwfrt7Gqo1b+uSHCjy/sa9Y6BBBxxFLxfbhTg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", + "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", "dev": true, "requires": { - "meow": "^7.0.0", + "meow": "^8.0.0", "semver": "^6.0.0" }, "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "hosted-git-info": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", + "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "meow": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.0.0.tgz", + "integrity": "sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", + "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", + "dev": true, + "requires": { + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true + }, + "type-fest": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.0.tgz", + "integrity": "sha512-fbDukFPnJBdn2eZ3RR+5mK2slHLFd6gYHY7jna1KWWy4Yr4XysHuCdXRzy+RiG/HwG4WJat00vdC2UHky5eKiQ==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.3.tgz", + "integrity": "sha512-emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww==", + "dev": true } } }, @@ -4711,14 +5119,14 @@ } }, "jest": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.2.tgz", - "integrity": "sha512-lL0hW7mh/2hhQmpo/1fDWQji/BUB3Xcxxj7r0fAOa3t56OAnwbE0HEl2bZ7XjAwV5TXOt8UpCgaa/WBJBB0CYw==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", "dev": true, "requires": { - "@jest/core": "^26.6.2", + "@jest/core": "^26.6.3", "import-local": "^3.0.2", - "jest-cli": "^26.6.2" + "jest-cli": "^26.6.3" }, "dependencies": { "@jest/types": { @@ -4735,12 +5143,12 @@ } }, "jest-cli": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.2.tgz", - "integrity": "sha512-5SBxa0bXc43fTHgxMfonDFDWTmQTiC6RSS4GpKhVekWkwpaeMHWt/FvGIy5GlTHMbCpzULWV++N3v93OdlFfQA==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", "dev": true, "requires": { - "@jest/core": "^26.6.2", + "@jest/core": "^26.6.3", "@jest/test-result": "^26.6.2", "@jest/types": "^26.6.2", "chalk": "^4.0.0", @@ -4748,7 +5156,7 @@ "graceful-fs": "^4.2.4", "import-local": "^3.0.2", "is-ci": "^2.0.0", - "jest-config": "^26.6.2", + "jest-config": "^26.6.3", "jest-util": "^26.6.2", "jest-validate": "^26.6.2", "prompts": "^2.0.1", @@ -4881,15 +5289,15 @@ } }, "jest-config": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.2.tgz", - "integrity": "sha512-0ApZqPd+L/BUWvNj1GHcptb5jwF23lo+BskjgJV/Blht1hgpu6eIwaYRgHPrS6I6HrxwRfJvlGbzoZZVb3VHTA==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.6.2", + "@jest/test-sequencer": "^26.6.3", "@jest/types": "^26.6.2", - "babel-jest": "^26.6.2", + "babel-jest": "^26.6.3", "chalk": "^4.0.0", "deepmerge": "^4.2.2", "glob": "^7.1.1", @@ -4897,7 +5305,7 @@ "jest-environment-jsdom": "^26.6.2", "jest-environment-node": "^26.6.2", "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.6.2", + "jest-jasmine2": "^26.6.3", "jest-regex-util": "^26.0.0", "jest-resolve": "^26.6.2", "jest-util": "^26.6.2", @@ -5182,9 +5590,9 @@ } }, "jest-jasmine2": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.2.tgz", - "integrity": "sha512-Om6q632kogggOBGjSr34jErXGOQy0+IkxouGUbyzB0lQmufu8nm1AcxLIKpB/FN36I43f2T3YajeNlxwJZ94PQ==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", @@ -5198,121 +5606,26 @@ "expect": "^26.6.2", "is-generator-fn": "^2.0.0", "jest-each": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-runtime": "^26.6.2", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2", - "throat": "^5.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "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" - } - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.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" - } - }, - "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 - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-message-util": { + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + }, + "dependencies": { + "@jest/types": { "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" } }, "pretty-format": { @@ -5326,15 +5639,6 @@ "ansi-styles": "^4.0.0", "react-is": "^17.0.1" } - }, - "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" - } } } }, @@ -5648,9 +5952,9 @@ } }, "jest-resolve-dependencies": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.2.tgz", - "integrity": "sha512-lXXQqBLlKlnOPyCfJZnrYydd7lZzWux9sMwKJxOmjsuVmoSlnmTOJ8kW1FYxotTyMzqoNtBuSF6qE+iXuAr6qQ==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", "dev": true, "requires": { "@jest/types": "^26.6.2", @@ -5674,9 +5978,9 @@ } }, "jest-runner": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.2.tgz", - "integrity": "sha512-OsWTIGx/MHSuPqjYwap1LAxT0qvlqmwTYSFOwc+G14AtyZlL7ngrrDes7moLRqFkDVpCHL2RT0i317jogyw81Q==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", "dev": true, "requires": { "@jest/console": "^26.6.2", @@ -5688,13 +5992,13 @@ "emittery": "^0.7.1", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-config": "^26.6.2", + "jest-config": "^26.6.3", "jest-docblock": "^26.0.0", "jest-haste-map": "^26.6.2", "jest-leak-detector": "^26.6.2", "jest-message-util": "^26.6.2", "jest-resolve": "^26.6.2", - "jest-runtime": "^26.6.2", + "jest-runtime": "^26.6.3", "jest-util": "^26.6.2", "jest-worker": "^26.6.2", "source-map-support": "^0.5.6", @@ -5713,85 +6017,13 @@ "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } - }, - "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" - } - }, - "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" - } - }, - "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 - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "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" - } } } }, "jest-runtime": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.2.tgz", - "integrity": "sha512-VEjfoim4tkvq8Gh8z7wMXlKva3DnIlgvmGR1AajiRK1nEHuXtuaR17jnVYOi+wW0i1dS3NH4jVdUQl08GodgZQ==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", "dev": true, "requires": { "@jest/console": "^26.6.2", @@ -5809,7 +6041,7 @@ "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.4", - "jest-config": "^26.6.2", + "jest-config": "^26.6.3", "jest-haste-map": "^26.6.2", "jest-message-util": "^26.6.2", "jest-mock": "^26.6.2", @@ -5835,78 +6067,6 @@ "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } - }, - "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" - } - }, - "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" - } - }, - "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 - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "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" - } } } }, @@ -5957,50 +6117,12 @@ "chalk": "^4.0.0" } }, - "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" - } - }, "diff-sequences": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", "dev": true }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.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" - } - }, - "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 - }, "jest-diff": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", @@ -6013,45 +6135,6 @@ "pretty-format": "^26.6.2" } }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, "pretty-format": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", @@ -6063,15 +6146,6 @@ "ansi-styles": "^4.0.0", "react-is": "^17.0.1" } - }, - "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" - } } } }, @@ -6818,6 +6892,15 @@ "signal-exit": "^3.0.0" } }, + "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" + } + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -7158,6 +7241,13 @@ "which": "^2.0.2" }, "dependencies": { + "uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "dev": true, + "optional": true + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7988,12 +8078,6 @@ "psl": "^1.1.28", "punycode": "^2.1.1" } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true } } }, @@ -9007,14 +9091,6 @@ "requires": { "temp-dir": "^2.0.0", "uuid": "^3.3.2" - }, - "dependencies": { - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } } }, "terminal-link": { @@ -9367,9 +9443,9 @@ "dev": true }, "uglify-js": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.3.tgz", - "integrity": "sha512-wDRziHG94mNj2n3R864CvYw/+pc9y/RNImiTyrrf8BzgWn75JgFSwYvXrtZQMnMnOp/4UTrf3iCSQxSStPiByA==", + "version": "3.11.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.5.tgz", + "integrity": "sha512-btvv/baMqe7HxP7zJSF7Uc16h1mSfuuSplT0/qdjxseesDU+yYzH33eHBH+eMdeRXwujXspaCTooWHQVVBh09w==", "dev": true, "optional": true }, @@ -9467,11 +9543,10 @@ "dev": true }, "uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", - "dev": true, - "optional": true + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true }, "v8-to-istanbul": { "version": "7.0.0", @@ -9680,6 +9755,12 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "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 + }, "yaml": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", diff --git a/package.json b/package.json index 16b20f3..b4c0298 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rx-sandbox", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "description": "Marble diagram DSL based test suite for RxJS 6", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -51,10 +51,10 @@ "@commitlint/config-angular": "^11.0.0", "@types/jest": "^26.0.15", "@types/node": "^14.14.6", - "conventional-changelog-cli": "^2.1.0", + "conventional-changelog-cli": "^2.1.1", "cz-conventional-changelog": "^3.3.0", "husky": "^3.1.0", - "jest": "^26.6.2", + "jest": "^26.6.3", "jest-spin-reporter": "^2.0.0", "lint-staged": "^10.5.1", "npm-run-all": "^4.1.5", diff --git a/spec/assert/constructObservableMarble-spec.ts b/spec/assert/constructObservableMarble-spec.ts index 0767974..d97957f 100644 --- a/spec/assert/constructObservableMarble-spec.ts +++ b/spec/assert/constructObservableMarble-spec.ts @@ -1,6 +1,6 @@ import { constructObservableMarble } from '../../src/assert/constructObservableMarble'; import { parseObservableMarble as p } from '../../src/marbles/parseObservableMarble'; -import { TestScheduler } from '../../src/scheduler/TestScheduler'; +import { createTestScheduler } from '../../src/scheduler/createTestScheduler'; describe('constructObservableMarble', () => { it('should create empty marble', () => { @@ -28,7 +28,7 @@ describe('constructObservableMarble', () => { a: 1, b: { x: 'meh' }, d: 4, - f: ['value'] + f: ['value'], }; const s = '--a--b--c--d--e--f--|'; @@ -153,11 +153,11 @@ describe('constructObservableMarble', () => { }); it('should create marble with higher order obsrevables', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { hot } = createTestScheduler(false, 1, 1000, false); const s = '--a--b--|'; - const a = scheduler.createHotObservable('--1--2--|'); - const b = scheduler.createHotObservable('--3--4--|'); + const a = hot('--1--2--|'); + const b = hot('--3--4--|'); const e = '--ä--ḅ--|'; const source = p(s, { a, b }, null, true); diff --git a/spec/assert/marbleAssert-spec.ts b/spec/assert/marbleAssert-spec.ts index 9b992b6..40f0d82 100644 --- a/spec/assert/marbleAssert-spec.ts +++ b/spec/assert/marbleAssert-spec.ts @@ -7,6 +7,11 @@ describe('marbleAssert', () => { expect(() => marbleAssert(1 as any)).toThrow(); }); + it('should export jasmine matcher style interface', () => { + const assert = marbleAssert([]); + expect(assert.to.equal).toBe(assert.toEqual); + }); + describe('TestMessage', () => { it('shoud pass empty', () => { marbleAssert(p('------')).to.equal(p('------')); diff --git a/spec/index-spec.ts b/spec/index-spec.ts index 9a9a123..9113828 100644 --- a/spec/index-spec.ts +++ b/spec/index-spec.ts @@ -15,6 +15,17 @@ describe('rxSandbox', () => { expect(first).not.toEqual(second); }); + it('should able to create async tick flush instance', async () => { + expect.assertions(1); + + const sandbox = idx.rxSandbox; + + const instance = sandbox.create({flushWithAsyncTick: true}); + + // verify instance returns promise based interfaces + await instance.flush().then(() => expect(true).toBeTruthy()); + }); + it('should able to create instance with autoflush', () => { const sandbox = idx.rxSandbox; diff --git a/spec/marbles/parseObservableMarble-spec.ts b/spec/marbles/parseObservableMarble-spec.ts index 7ecd3ae..acc3330 100644 --- a/spec/marbles/parseObservableMarble-spec.ts +++ b/spec/marbles/parseObservableMarble-spec.ts @@ -1,6 +1,6 @@ import { parseObservableMarble } from '../../src/marbles/parseObservableMarble'; import { complete, error, next } from '../../src/message/TestMessage'; -import { TestScheduler } from '../../src/scheduler/TestScheduler'; +import { createTestScheduler } from '../../src/scheduler/createTestScheduler'; import { ColdObservable } from '../../src/utils/coreInternalImport'; describe('parseObservableMarble', () => { @@ -205,10 +205,10 @@ describe('parseObservableMarble', () => { }); it('should able to flatten inner observable', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { cold } = createTestScheduler(false, 1, 1000, false); const marble = ' --a--|'; - const inner = scheduler.createColdObservable('---1--'); + const inner = cold('---1--'); const messages = parseObservableMarble(marble, { a: inner }, null, true); const expected = [next(2, [next(3, '1')]), complete(5)]; diff --git a/spec/scheduler/calculateSubscriptionFrame-spec.ts b/spec/scheduler/calculateSubscriptionFrame-spec.ts index 6b86632..a329a42 100644 --- a/spec/scheduler/calculateSubscriptionFrame-spec.ts +++ b/spec/scheduler/calculateSubscriptionFrame-spec.ts @@ -1,6 +1,6 @@ import { of } from 'rxjs'; import { calculateSubscriptionFrame } from '../../src/scheduler/calculateSubscriptionFrame'; -import { TestScheduler } from '../../src/scheduler/TestScheduler'; +import { createTestScheduler } from '../../src/scheduler/createTestScheduler'; describe('calculateSubscriptionFrame', () => { it('should return immediate subscription without subscription token', () => { @@ -11,28 +11,28 @@ describe('calculateSubscriptionFrame', () => { }); it('should preserve subscription frame with hot observable', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const hot = scheduler.createHotObservable(''); + const { hot } = createTestScheduler(false, 1, 1000, false); + const value = hot(''); - const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame(hot, '--^---!', 1); + const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame(value, '--^---!', 1); expect(subscribedFrame).toEqual(2); expect(unsubscribedFrame).toEqual(6); }); it('should allow custom frameTimeFactor', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const hot = scheduler.createHotObservable(''); + const { hot } = createTestScheduler(false, 1, 1000, false); + const value = hot(''); - const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame(hot, '--^---!', 10); + const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame(value, '--^---!', 10); expect(subscribedFrame).toEqual(20); expect(unsubscribedFrame).toEqual(60); }); it('should return adjusted subscription frame with cold observable', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const hot = scheduler.createColdObservable(''); + const { cold } = createTestScheduler(false, 1, 1000, false); + const hot = cold(''); const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame(hot, '--^---!', 1); @@ -41,8 +41,8 @@ describe('calculateSubscriptionFrame', () => { }); it('should preserve immediate subscription with cold observable', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const hot = scheduler.createColdObservable(''); + const { cold } = createTestScheduler(false, 1, 1000, false); + const hot = cold(''); const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame(hot, '^---!', 1); @@ -51,18 +51,18 @@ describe('calculateSubscriptionFrame', () => { }); it('should return subscription only frame with hot obsrevable', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const hot = scheduler.createHotObservable(''); + const { hot } = createTestScheduler(false, 1, 1000, false); + const value = hot(''); - const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame(hot, '--^---', 1); + const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame(value, '--^---', 1); expect(subscribedFrame).toEqual(2); expect(unsubscribedFrame).toEqual(Number.POSITIVE_INFINITY); }); it('should return adjusted subscription only frame with cold obsrevable', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const hot = scheduler.createColdObservable(''); + const { cold } = createTestScheduler(false, 1, 1000, false); + const hot = cold(''); const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame(hot, '--^---', 1); diff --git a/spec/scheduler/createAsyncTestScheduler-spec.ts b/spec/scheduler/createAsyncTestScheduler-spec.ts new file mode 100644 index 0000000..29f1efe --- /dev/null +++ b/spec/scheduler/createAsyncTestScheduler-spec.ts @@ -0,0 +1,447 @@ +import { mapTo, mergeMap, windowCount } from 'rxjs/operators'; +import { parseObservableMarble } from '../../src/marbles/parseObservableMarble'; +import { complete, error, next, subscribe, TestMessage } from '../../src/message/TestMessage'; +import { createTestScheduler } from '../../src/scheduler/createTestScheduler'; +import { AsyncAction, ColdObservable, HotObservable } from '../../src/utils/coreInternalImport'; + +describe('createTestSchedulerWithAsyncTickFlush', () => { + describe('hotObservable', () => { + it('should create hot observable via TestMessage', () => { + const { hot } = createTestScheduler(false, 1, 1000, true); + const messages: Array> = [next(10, 'meh'), complete(20)]; + + const value = hot(messages); + + expect(value).toBeInstanceOf(HotObservable); + expect(value.messages).toEqual(messages); + }); + + it('should create hot observable via marble diagram', () => { + const { hot } = createTestScheduler(false, 1, 1000, true); + const value = hot('---a---|'); + + const expected: Array> = [next(3, 'a'), complete(7)]; + + expect(value).toBeInstanceOf(HotObservable); + expect(value.messages).toEqual(expected); + }); + + it('should create hot observale via marble with custom value', () => { + const { hot } = createTestScheduler(false, 1, 1000, true); + const value = hot('---a---|', { + a: 'meh', + }); + + const expected: Array> = [next(3, 'meh'), complete(7)]; + + expect(value).toBeInstanceOf(HotObservable); + expect(value.messages).toEqual(expected); + }); + + it('should create hot observable via marble with error', () => { + const { hot } = createTestScheduler(false, 1, 1000, true); + const value = hot('---a---#'); + + const expected: Array> = [next(3, 'a'), error(7, '#')]; + + expect(value).toBeInstanceOf(HotObservable); + expect(value.messages).toEqual(expected); + }); + + it('should create hot observable via marble with custom error', () => { + const { hot } = createTestScheduler(false, 1, 1000, true); + const value = hot('---a---#', null, 'meh'); + + const expected: Array> = [next(3, 'a'), error(7, 'meh')]; + + expect(value).toBeInstanceOf(HotObservable); + expect(value.messages).toEqual(expected); + }); + + it('should honor custom frameTimeFactor', () => { + const { hot } = createTestScheduler(false, 10, 1000, true); + + const value = hot('---a---|'); + + const expected: Array> = [next(30, 'a'), complete(70)]; + + expect(value).toBeInstanceOf(HotObservable); + expect(value.messages).toEqual(expected); + }); + }); + + describe('coldObservable', () => { + it('should create cold observable via TestMessage', () => { + const { cold } = createTestScheduler(false, 1, 1000, true); + const messages: Array> = [next(10, 'meh'), complete(20)]; + + const value = cold(messages); + + expect(value).toBeInstanceOf(ColdObservable); + expect(value.messages).toEqual(messages); + }); + + it('should create cold observable via marble diagram', () => { + const { cold } = createTestScheduler(false, 1, 1000, true); + const value = cold('---a---|'); + + const expected: Array> = [next(3, 'a'), complete(7)]; + + expect(value).toBeInstanceOf(ColdObservable); + expect(value.messages).toEqual(expected); + }); + + it('should create cold observale via marble with custom value', () => { + const { cold } = createTestScheduler(false, 1, 1000, true); + const value = cold('---a---|', { + a: 'meh', + }); + + const expected: Array> = [next(3, 'meh'), complete(7)]; + + expect(value).toBeInstanceOf(ColdObservable); + expect(value.messages).toEqual(expected); + }); + + it('should create cold observable via marble with error', () => { + const { cold } = createTestScheduler(false, 1, 1000, true); + const value = cold('---a---#'); + + const expected: Array> = [next(3, 'a'), error(7, '#')]; + + expect(value).toBeInstanceOf(ColdObservable); + expect(value.messages).toEqual(expected); + }); + + it('should create cold observable via marble with custom error', () => { + const { cold } = createTestScheduler(false, 1, 1000, true); + const value = cold('---a---#', null, 'meh'); + + const expected: Array> = [next(3, 'a'), error(7, 'meh')]; + + expect(value).toBeInstanceOf(ColdObservable); + expect(value.messages).toEqual(expected); + }); + + it('should honor custom frameTimeFactor', () => { + const { cold } = createTestScheduler(false, 10, 1000, true); + + const value = cold('---a---|'); + + const expected: Array> = [next(30, 'a'), complete(70)]; + + expect(value).toBeInstanceOf(ColdObservable); + expect(value.messages).toEqual(expected); + }); + + it('should throw when marble includes unsupported token', () => { + const { cold } = createTestScheduler(false, 1, 1000, true); + + expect(() => cold('----^')).toThrow(); + }); + }); + + describe('getMessages', () => { + it('should generate messages from observable having hot observable source', async () => { + const expected: Array> = [next(3, 'x'), next(7, 'x'), complete(11)]; + const { flush, getMessages, hot } = createTestScheduler(false, 1, 1000, true); + + const e1 = hot('---1---2---|'); + const messages = await getMessages(e1.pipe(mapTo('x'))); + + expect(messages).toHaveLength(0); + await flush(); + + expect(messages).toEqual(expected); + expect(e1.subscriptions).toEqual([subscribe(0, 11)]); + }); + + it('should able to autoflush with hot observable source', async () => { + const expected: Array> = [next(3, 'x'), next(7, 'x'), complete(11)]; + const { hot, getMessages } = createTestScheduler(true, 1, 1000, true); + + const e1 = hot('---1---2---|'); + const messages = await getMessages(e1.pipe(mapTo('x'))); + + expect(messages).toEqual(expected); + expect(e1.subscriptions).toEqual([subscribe(0, 11)]); + }); + + it('should throw when autoflush attempted multiple times', async () => { + const { hot, getMessages } = createTestScheduler(true, 1, 1000, true); + + const e1 = hot('---1---2---|'); + await getMessages(e1.pipe(mapTo('x'))); + + expect(() => getMessages(e1.pipe(mapTo('x')))).toThrow(); + }); + + it('should ignore values over max frames', async () => { + const expected: Array> = [next(3, 'x')]; + const { hot, getMessages } = createTestScheduler(true, 1, 5, true); + + const e1 = hot('---1---2---|'); + const messages = await getMessages(e1.pipe(mapTo('x'))); + + expect(messages).toEqual(expected); + }); + + it('should generate messages from observable having hot observable source with error', async () => { + const expected: Array> = [next(3, 'x'), next(7, 'x'), error(11, '#')]; + const { flush, getMessages, hot } = createTestScheduler(false, 1, 1000, true); + + const e1 = hot('---1---2---#'); + const messages = await getMessages(e1.pipe(mapTo('x'))); + + expect(messages).toHaveLength(0); + await flush(); + + expect(messages).toEqual(expected); + expect(e1.subscriptions).toEqual([subscribe(0, 11)]); + }); + + it('should generate messages from observable having cold observable source', async () => { + const expected: Array> = [next(3, 'x'), next(7, 'x'), complete(11)]; + const { flush, getMessages, cold } = createTestScheduler(false, 1, 1000, true); + + const e1 = cold('---1---2---|'); + const messages = await getMessages(e1.pipe(mapTo('x'))); + + expect(messages).toHaveLength(0); + await flush(); + + expect(messages).toEqual(expected); + expect(e1.subscriptions).toEqual([subscribe(0, 11)]); + }); + + it('should able to autoflush with cold observable source', async () => { + const expected: Array> = [next(3, 'x'), next(7, 'x'), complete(11)]; + const { cold, getMessages } = createTestScheduler(true, 1, 1000, true); + + const e1 = cold('---1---2---|'); + const messages = await getMessages(e1.pipe(mapTo('x'))); + + expect(messages).toEqual(expected); + expect(e1.subscriptions).toEqual([subscribe(0, 11)]); + }); + + it('should generate messages from observable having cold observable source with error', async () => { + const expected: Array> = [next(3, 'x'), next(7, 'x'), error(11, '#')]; + const { flush, getMessages, cold } = createTestScheduler(false, 1, 1000, true); + + const e1 = cold('---1---2---#'); + const messages = await getMessages(e1.pipe(mapTo('x'))); + + expect(messages).toHaveLength(0); + await flush(); + + expect(messages).toEqual(expected); + expect(e1.subscriptions).toEqual([subscribe(0, 11)]); + }); + + it('should materialize inner observable with cold observable source', async () => { + const { flush, getMessages, cold } = createTestScheduler(false, 1, 1000, true); + const expected: Array[]>[]>>> = [ + next(0, parseObservableMarble(' ---a---b---(c|)')), + next(11, parseObservableMarble(' ----d---|')), + complete(19), + ]; + + const source = cold('---a---b---c---d---|'); + + const messages = await getMessages(source.pipe(windowCount(3))); + expect(messages).toHaveLength(0); + + await flush(); + expect(messages).toEqual(expected); + }); + + it('should materialize inner observable with cold observable source with error', async () => { + const { flush, getMessages, cold } = createTestScheduler(false, 1, 1000, true); + const expected: Array[]>[]>>> = [ + next(0, parseObservableMarble(' ---a---b---(c|)')), + next(11, parseObservableMarble(' ----#')), + error(15, '#'), + ]; + + const source = cold('---a---b---c---#'); + + const messages = await getMessages(source.pipe(windowCount(3))); + expect(messages).toHaveLength(0); + + await flush(); + expect(messages).toEqual(expected); + }); + + it('should materialize inner observable with hot observable source', async () => { + const { flush, getMessages, hot } = createTestScheduler(false, 1, 1000, true); + const expected: Array[]>[]>>> = [ + next(0, parseObservableMarble(' ---a---b---(c|)')), + next(11, parseObservableMarble(' ----d---|')), + complete(19), + ]; + + const source = hot('---a---b---c---d---|'); + + const messages = await getMessages(source.pipe(windowCount(3))); + expect(messages).toHaveLength(0); + + await flush(); + expect(messages).toEqual(expected); + }); + + it('should materialize inner observable with hot observable source with error', async () => { + const { flush, getMessages, hot } = createTestScheduler(false, 1, 1000, true); + const expected: Array[]>[]>>> = [ + next(0, parseObservableMarble(' ---a---b---(c|)')), + next(11, parseObservableMarble(' ----#')), + error(15, '#'), + ]; + + const source = hot('---a---b---c---#'); + + const messages = await getMessages(source.pipe(windowCount(3))); + expect(messages).toHaveLength(0); + + await flush(); + expect(messages).toEqual(expected); + }); + + it('should support subscription with cold observable source', async () => { + const expected: Array> = [next(3, 'x')]; + const { flush, getMessages, cold } = createTestScheduler(false, 1, 1000, true); + + const e1 = cold('---1---2---|'); + const sub = ' ------^---!'; + //actual subscription: ---1- + + const messages = await getMessages(e1.pipe(mapTo('x')), sub); + + expect(messages).toHaveLength(0); + await flush(); + + expect(messages).toEqual(expected); + expect(e1.subscriptions).toEqual([subscribe(0, 4)]); + }); + + it('should support subscription with hot observable source', async () => { + const expected: Array> = [next(7, 'x')]; + const { flush, getMessages, hot } = createTestScheduler(false, 1, 1000, true); + + const e1 = hot('---1---2---|'); + const sub = ' ------^---!'; + const messages = await getMessages(e1.pipe(mapTo('x')), sub); + + expect(messages).toHaveLength(0); + await flush(); + + expect(messages).toEqual(expected); + expect(e1.subscriptions).toEqual([subscribe(6, 10)]); + }); + }); + + describe('flush', () => { + it(`should not flush while it's already flushing`, async () => { + const { scheduler, getMessages, hot, flush } = createTestScheduler(false, 1, 1000, true); + + const expected = [next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]; + + scheduler.schedule(async (_x: typeof scheduler | undefined) => await flush(), 50, scheduler); + + const source = hot([next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]); + const messages = await getMessages(source); + + await flush(); + + expect(messages).toEqual(expected); + }); + }); + + describe('advanceTo', () => { + it('should throw when autoflush set', async () => { + const { advanceTo } = createTestScheduler(true, 1, 1000, true); + + await expect(advanceTo(10)).rejects.toThrow(); + }); + + it('should able to advance to absolute time', async () => { + const { scheduler, advanceTo, getMessages, hot } = createTestScheduler(false, 1, 1000, true); + const toFrame = 50; + + const expected = [next(20, 'a'), next(40, 'b')]; + + const source = hot([next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]); + + const messages = await getMessages(source); + + await advanceTo(toFrame); + + expect(messages).toEqual(expected); + expect((scheduler as any).frame).toEqual(toFrame); + expect(scheduler.now()).toEqual(toFrame); + }); + + it('should not do anything with zero absolute time', async () => { + const { hot, advanceTo, getMessages } = createTestScheduler(false, 1, 1000, true); + + const source = hot([next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]); + + const messages = await getMessages(source); + + await advanceTo(0); + + expect(messages).toHaveLength(0); + }); + + it('should able to advance without any actions setup', async () => { + const { advanceTo } = createTestScheduler(false, 1, 1000, true); + await expect(advanceTo(100)).resolves.not.toThrow(); + }); + + it('should not allow backward', async () => { + const { advanceTo } = createTestScheduler(false, 1, 1000, true); + await advanceTo(100); + + await expect(advanceTo(80)).rejects.toThrow(); + await expect(advanceTo(-1)).rejects.toThrow(); + }); + + it('should unsubscribe the rest of the scheduled actions if an action throws an error', async () => { + const { scheduler, advanceTo } = createTestScheduler(false, 1, 1000, true); + + const error = new Error('meh'); + let normalActionExecuted = false; + + const errorAction = new AsyncAction(scheduler as any, () => { + throw error; + }); + const normalAction = new AsyncAction(scheduler as any, () => { + normalActionExecuted = true; + }); + + errorAction.schedule({}, 20); + normalAction.schedule({}, 40); + + (scheduler as any).actions.push(errorAction, normalAction); + + await expect(advanceTo(100)).rejects.toThrowError(error); + + expect(errorAction.closed).toBeTruthy(); + expect(normalAction.closed).toBeTruthy(); + expect(normalActionExecuted).toBeFalsy(); + }); + }); + + it('should support innerobservable from promise ', async () => { + const { hot, getMessages, flush } = createTestScheduler(false, 1, 1000, true); + + const sourceMessage = [next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]; + + const source = hot(sourceMessage).pipe(mergeMap((v) => new Promise((res) => res(v)))); + const messages = await getMessages(source); + + await flush(); + + expect(messages).toEqual(sourceMessage); + }); +}); \ No newline at end of file diff --git a/spec/scheduler/TestScheduler-spec.ts b/spec/scheduler/createTestScheduler-spec.ts similarity index 59% rename from spec/scheduler/TestScheduler-spec.ts rename to spec/scheduler/createTestScheduler-spec.ts index c35c940..8391234 100644 --- a/spec/scheduler/TestScheduler-spec.ts +++ b/spec/scheduler/createTestScheduler-spec.ts @@ -1,24 +1,24 @@ -import { mapTo, windowCount } from 'rxjs/operators'; +import { mapTo, mergeMap, windowCount } from 'rxjs/operators'; import { parseObservableMarble } from '../../src/marbles/parseObservableMarble'; import { complete, error, next, subscribe, TestMessage } from '../../src/message/TestMessage'; -import { TestScheduler } from '../../src/scheduler/TestScheduler'; +import { createTestScheduler } from '../../src/scheduler/createTestScheduler'; import { AsyncAction, ColdObservable, HotObservable } from '../../src/utils/coreInternalImport'; -describe('TestScheduler', () => { +describe('createTestScheduler', () => { describe('hotObservable', () => { it('should create hot observable via TestMessage', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { hot } = createTestScheduler(false, 1, 1000, false); const messages: Array> = [next(10, 'meh'), complete(20)]; - const value = scheduler.createHotObservable(messages); + const value = hot(messages); expect(value).toBeInstanceOf(HotObservable); expect(value.messages).toEqual(messages); }); it('should create hot observable via marble diagram', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const value = scheduler.createHotObservable('---a---|'); + const { hot } = createTestScheduler(false, 1, 1000, false); + const value = hot('---a---|'); const expected: Array> = [next(3, 'a'), complete(7)]; @@ -27,8 +27,8 @@ describe('TestScheduler', () => { }); it('should create hot observale via marble with custom value', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const value = scheduler.createHotObservable('---a---|', { + const { hot } = createTestScheduler(false, 1, 1000, false); + const value = hot('---a---|', { a: 'meh', }); @@ -39,8 +39,8 @@ describe('TestScheduler', () => { }); it('should create hot observable via marble with error', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const value = scheduler.createHotObservable('---a---#'); + const { hot } = createTestScheduler(false, 1, 1000, false); + const value = hot('---a---#'); const expected: Array> = [next(3, 'a'), error(7, '#')]; @@ -49,8 +49,8 @@ describe('TestScheduler', () => { }); it('should create hot observable via marble with custom error', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const value = scheduler.createHotObservable('---a---#', null, 'meh'); + const { hot } = createTestScheduler(false, 1, 1000, false); + const value = hot('---a---#', null, 'meh'); const expected: Array> = [next(3, 'a'), error(7, 'meh')]; @@ -59,9 +59,9 @@ describe('TestScheduler', () => { }); it('should honor custom frameTimeFactor', () => { - const scheduler = new TestScheduler(false, 10, 1000); + const { hot } = createTestScheduler(false, 10, 1000, false); - const value = scheduler.createHotObservable('---a---|'); + const value = hot('---a---|'); const expected: Array> = [next(30, 'a'), complete(70)]; @@ -72,18 +72,18 @@ describe('TestScheduler', () => { describe('coldObservable', () => { it('should create cold observable via TestMessage', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { cold } = createTestScheduler(false, 1, 1000, false); const messages: Array> = [next(10, 'meh'), complete(20)]; - const value = scheduler.createColdObservable(messages); + const value = cold(messages); expect(value).toBeInstanceOf(ColdObservable); expect(value.messages).toEqual(messages); }); it('should create cold observable via marble diagram', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const value = scheduler.createColdObservable('---a---|'); + const { cold } = createTestScheduler(false, 1, 1000, false); + const value = cold('---a---|'); const expected: Array> = [next(3, 'a'), complete(7)]; @@ -92,8 +92,8 @@ describe('TestScheduler', () => { }); it('should create cold observale via marble with custom value', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const value = scheduler.createColdObservable('---a---|', { + const { cold } = createTestScheduler(false, 1, 1000, false); + const value = cold('---a---|', { a: 'meh', }); @@ -104,8 +104,8 @@ describe('TestScheduler', () => { }); it('should create cold observable via marble with error', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const value = scheduler.createColdObservable('---a---#'); + const { cold } = createTestScheduler(false, 1, 1000, false); + const value = cold('---a---#'); const expected: Array> = [next(3, 'a'), error(7, '#')]; @@ -114,8 +114,8 @@ describe('TestScheduler', () => { }); it('should create cold observable via marble with custom error', () => { - const scheduler = new TestScheduler(false, 1, 1000); - const value = scheduler.createColdObservable('---a---#', null, 'meh'); + const { cold } = createTestScheduler(false, 1, 1000, false); + const value = cold('---a---#', null, 'meh'); const expected: Array> = [next(3, 'a'), error(7, 'meh')]; @@ -124,9 +124,9 @@ describe('TestScheduler', () => { }); it('should honor custom frameTimeFactor', () => { - const scheduler = new TestScheduler(false, 10, 1000); + const { cold } = createTestScheduler(false, 10, 1000, false); - const value = scheduler.createColdObservable('---a---|'); + const value = cold('---a---|'); const expected: Array> = [next(30, 'a'), complete(70)]; @@ -135,22 +135,22 @@ describe('TestScheduler', () => { }); it('should throw when marble includes unsupported token', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { cold } = createTestScheduler(false, 1, 1000, false); - expect(() => scheduler.createColdObservable('----^')).toThrow(); + expect(() => cold('----^')).toThrow(); }); }); describe('getMessages', () => { it('should generate messages from observable having hot observable source', () => { const expected: Array> = [next(3, 'x'), next(7, 'x'), complete(11)]; - const scheduler = new TestScheduler(false, 1, 1000); + const { flush, getMessages, hot } = createTestScheduler(false, 1, 1000, false); - const e1 = scheduler.createHotObservable('---1---2---|'); - const messages = scheduler.getMessages(e1.pipe(mapTo('x'))); + const e1 = hot('---1---2---|'); + const messages = getMessages(e1.pipe(mapTo('x'))); expect(messages).toHaveLength(0); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); expect(e1.subscriptions).toEqual([subscribe(0, 11)]); @@ -158,43 +158,43 @@ describe('TestScheduler', () => { it('should able to autoflush with hot observable source', () => { const expected: Array> = [next(3, 'x'), next(7, 'x'), complete(11)]; - const scheduler = new TestScheduler(true, 1, 1000); + const { hot, getMessages } = createTestScheduler(true, 1, 1000, false); - const e1 = scheduler.createHotObservable('---1---2---|'); - const messages = scheduler.getMessages(e1.pipe(mapTo('x'))); + const e1 = hot('---1---2---|'); + const messages = getMessages(e1.pipe(mapTo('x'))); expect(messages).toEqual(expected); expect(e1.subscriptions).toEqual([subscribe(0, 11)]); }); it('should throw when autoflush attempted multiple times', () => { - const scheduler = new TestScheduler(true, 1, 1000); + const { hot, getMessages } = createTestScheduler(true, 1, 1000, false); - const e1 = scheduler.createHotObservable('---1---2---|'); - scheduler.getMessages(e1.pipe(mapTo('x'))); + const e1 = hot('---1---2---|'); + getMessages(e1.pipe(mapTo('x'))); - expect(() => scheduler.getMessages(e1.pipe(mapTo('x')))).toThrow(); + expect(() => getMessages(e1.pipe(mapTo('x')))).toThrow(); }); it('should ignore values over max frames', () => { const expected: Array> = [next(3, 'x')]; - const scheduler = new TestScheduler(true, 1, 5); + const { hot, getMessages } = createTestScheduler(true, 1, 5, false); - const e1 = scheduler.createHotObservable('---1---2---|'); - const messages = scheduler.getMessages(e1.pipe(mapTo('x'))); + const e1 = hot('---1---2---|'); + const messages = getMessages(e1.pipe(mapTo('x'))); expect(messages).toEqual(expected); }); it('should generate messages from observable having hot observable source with error', () => { const expected: Array> = [next(3, 'x'), next(7, 'x'), error(11, '#')]; - const scheduler = new TestScheduler(false, 1, 1000); + const { flush, getMessages, hot } = createTestScheduler(false, 1, 1000, false); - const e1 = scheduler.createHotObservable('---1---2---#'); - const messages = scheduler.getMessages(e1.pipe(mapTo('x'))); + const e1 = hot('---1---2---#'); + const messages = getMessages(e1.pipe(mapTo('x'))); expect(messages).toHaveLength(0); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); expect(e1.subscriptions).toEqual([subscribe(0, 11)]); @@ -202,13 +202,13 @@ describe('TestScheduler', () => { it('should generate messages from observable having cold observable source', () => { const expected: Array> = [next(3, 'x'), next(7, 'x'), complete(11)]; - const scheduler = new TestScheduler(false, 1, 1000); + const { flush, getMessages, cold } = createTestScheduler(false, 1, 1000, false); - const e1 = scheduler.createColdObservable('---1---2---|'); - const messages = scheduler.getMessages(e1.pipe(mapTo('x'))); + const e1 = cold('---1---2---|'); + const messages = getMessages(e1.pipe(mapTo('x'))); expect(messages).toHaveLength(0); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); expect(e1.subscriptions).toEqual([subscribe(0, 11)]); @@ -216,10 +216,10 @@ describe('TestScheduler', () => { it('should able to autoflush with cold observable source', () => { const expected: Array> = [next(3, 'x'), next(7, 'x'), complete(11)]; - const scheduler = new TestScheduler(true, 1, 1000); + const { cold, getMessages } = createTestScheduler(true, 1, 1000, false); - const e1 = scheduler.createColdObservable('---1---2---|'); - const messages = scheduler.getMessages(e1.pipe(mapTo('x'))); + const e1 = cold('---1---2---|'); + const messages = getMessages(e1.pipe(mapTo('x'))); expect(messages).toEqual(expected); expect(e1.subscriptions).toEqual([subscribe(0, 11)]); @@ -227,98 +227,98 @@ describe('TestScheduler', () => { it('should generate messages from observable having cold observable source with error', () => { const expected: Array> = [next(3, 'x'), next(7, 'x'), error(11, '#')]; - const scheduler = new TestScheduler(false, 1, 1000); + const { flush, getMessages, cold } = createTestScheduler(false, 1, 1000, false); - const e1 = scheduler.createColdObservable('---1---2---#'); - const messages = scheduler.getMessages(e1.pipe(mapTo('x'))); + const e1 = cold('---1---2---#'); + const messages = getMessages(e1.pipe(mapTo('x'))); expect(messages).toHaveLength(0); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); expect(e1.subscriptions).toEqual([subscribe(0, 11)]); }); it('should materialize inner observable with cold observable source', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { flush, getMessages, cold } = createTestScheduler(false, 1, 1000, false); const expected: Array[]>[]>>> = [ next(0, parseObservableMarble(' ---a---b---(c|)')), next(11, parseObservableMarble(' ----d---|')), complete(19), ]; - const source = scheduler.createColdObservable('---a---b---c---d---|'); + const source = cold('---a---b---c---d---|'); - const messages = scheduler.getMessages(source.pipe(windowCount(3))); + const messages = getMessages(source.pipe(windowCount(3))); expect(messages).toHaveLength(0); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); }); it('should materialize inner observable with cold observable source with error', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { flush, getMessages, cold } = createTestScheduler(false, 1, 1000, false); const expected: Array[]>[]>>> = [ next(0, parseObservableMarble(' ---a---b---(c|)')), next(11, parseObservableMarble(' ----#')), error(15, '#'), ]; - const source = scheduler.createColdObservable('---a---b---c---#'); + const source = cold('---a---b---c---#'); - const messages = scheduler.getMessages(source.pipe(windowCount(3))); + const messages = getMessages(source.pipe(windowCount(3))); expect(messages).toHaveLength(0); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); }); it('should materialize inner observable with hot observable source', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { flush, getMessages, hot } = createTestScheduler(false, 1, 1000, false); const expected: Array[]>[]>>> = [ next(0, parseObservableMarble(' ---a---b---(c|)')), next(11, parseObservableMarble(' ----d---|')), complete(19), ]; - const source = scheduler.createHotObservable('---a---b---c---d---|'); + const source = hot('---a---b---c---d---|'); - const messages = scheduler.getMessages(source.pipe(windowCount(3))); + const messages = getMessages(source.pipe(windowCount(3))); expect(messages).toHaveLength(0); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); }); it('should materialize inner observable with hot observable source with error', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { flush, getMessages, hot } = createTestScheduler(false, 1, 1000, false); const expected: Array[]>[]>>> = [ next(0, parseObservableMarble(' ---a---b---(c|)')), next(11, parseObservableMarble(' ----#')), error(15, '#'), ]; - const source = scheduler.createHotObservable('---a---b---c---#'); + const source = hot('---a---b---c---#'); - const messages = scheduler.getMessages(source.pipe(windowCount(3))); + const messages = getMessages(source.pipe(windowCount(3))); expect(messages).toHaveLength(0); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); }); it('should support subscription with cold observable source', () => { const expected: Array> = [next(3, 'x')]; - const scheduler = new TestScheduler(false, 1, 1000); + const { flush, getMessages, cold } = createTestScheduler(false, 1, 1000, false); - const e1 = scheduler.createColdObservable('---1---2---|'); + const e1 = cold('---1---2---|'); const sub = ' ------^---!'; //actual subscription: ---1- - const messages = scheduler.getMessages(e1.pipe(mapTo('x')), sub); + const messages = getMessages(e1.pipe(mapTo('x')), sub); expect(messages).toHaveLength(0); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); expect(e1.subscriptions).toEqual([subscribe(0, 4)]); @@ -326,14 +326,14 @@ describe('TestScheduler', () => { it('should support subscription with hot observable source', () => { const expected: Array> = [next(7, 'x')]; - const scheduler = new TestScheduler(false, 1, 1000); + const { flush, getMessages, hot } = createTestScheduler(false, 1, 1000, false); - const e1 = scheduler.createHotObservable('---1---2---|'); + const e1 = hot('---1---2---|'); const sub = ' ------^---!'; - const messages = scheduler.getMessages(e1.pipe(mapTo('x')), sub); + const messages = getMessages(e1.pipe(mapTo('x')), sub); expect(messages).toHaveLength(0); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); expect(e1.subscriptions).toEqual([subscribe(6, 10)]); @@ -342,16 +342,16 @@ describe('TestScheduler', () => { describe('flush', () => { it(`should not flush while it's already flushing`, () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { scheduler, getMessages, hot, flush } = createTestScheduler(false, 1, 1000, false); const expected = [next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]; - scheduler.schedule((x: TestScheduler | undefined) => x?.flush(), 50, scheduler); + scheduler.schedule((_x: typeof scheduler | undefined) => flush(), 50, scheduler); - const source = scheduler.createHotObservable([next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]); - const messages = scheduler.getMessages(source); + const source = hot([next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]); + const messages = getMessages(source); - scheduler.flush(); + flush(); expect(messages).toEqual(expected); }); @@ -359,76 +359,90 @@ describe('TestScheduler', () => { describe('advanceTo', () => { it('should throw when autoflush set', () => { - const scheduler = new TestScheduler(true, 1, 1000); + const { advanceTo } = createTestScheduler(true, 1, 1000, false); - expect(() => scheduler.advanceTo(10)).toThrow(); + expect(() => advanceTo(10)).toThrow(); }); it('should able to advance to absolute time', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { scheduler, advanceTo, getMessages, hot } = createTestScheduler(false, 1, 1000, false); const toFrame = 50; const expected = [next(20, 'a'), next(40, 'b')]; - const source = scheduler.createHotObservable([next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]); + const source = hot([next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]); - const messages = scheduler.getMessages(source); + const messages = getMessages(source); - scheduler.advanceTo(toFrame); + advanceTo(toFrame); expect(messages).toEqual(expected); - expect(scheduler.frame).toEqual(toFrame); + expect((scheduler as any).frame).toEqual(toFrame); expect(scheduler.now()).toEqual(toFrame); }); it('should not do anything with zero absolute time', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { hot, advanceTo, getMessages } = createTestScheduler(false, 1, 1000, false); - const source = scheduler.createHotObservable([next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]); + const source = hot([next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]); - const messages = scheduler.getMessages(source); + const messages = getMessages(source); - scheduler.advanceTo(0); + advanceTo(0); expect(messages).toHaveLength(0); }); it('should able to advance without any actions setup', () => { - const scheduler = new TestScheduler(false, 1, 1000); - expect(() => scheduler.advanceTo(100)).not.toThrow(); + const { advanceTo } = createTestScheduler(false, 1, 1000, false); + expect(() => advanceTo(100)).not.toThrow(); }); it('should not allow backward', () => { - const scheduler = new TestScheduler(false, 1, 1000); - scheduler.advanceTo(100); + const { advanceTo } = createTestScheduler(false, 1, 1000, false); + advanceTo(100); - expect(() => scheduler.advanceTo(80)).toThrow(); - expect(() => scheduler.advanceTo(-1)).toThrow(); + expect(() => advanceTo(80)).toThrow(); + expect(() => advanceTo(-1)).toThrow(); }); it('should unsubscribe the rest of the scheduled actions if an action throws an error', () => { - const scheduler = new TestScheduler(false, 1, 1000); + const { scheduler, advanceTo } = createTestScheduler(false, 1, 1000, false); const error = new Error('meh'); let normalActionExecuted = false; - const errorAction = new AsyncAction(scheduler, () => { + const errorAction = new AsyncAction(scheduler as any, () => { throw error; }); - const normalAction = new AsyncAction(scheduler, () => { + const normalAction = new AsyncAction(scheduler as any, () => { normalActionExecuted = true; }); errorAction.schedule({}, 20); normalAction.schedule({}, 40); - scheduler.actions.push(errorAction, normalAction); + (scheduler as any).actions.push(errorAction, normalAction); - expect(() => scheduler.advanceTo(100)).toThrowError(error); + expect(() => advanceTo(100)).toThrowError(error); expect(errorAction.closed).toBeTruthy(); expect(normalAction.closed).toBeTruthy(); expect(normalActionExecuted).toBeFalsy(); }); }); -}); + + it('does not support innerobservable from promise', () => { + const { hot, getMessages, flush } = createTestScheduler(false, 1, 1000, false); + + const sourceMessage = [next(20, 'a'), next(40, 'b'), next(60, 'c'), complete(80)]; + + const source = hot(sourceMessage).pipe(mergeMap((v) => new Promise((res) => res(v)))); + const messages = getMessages(source); + + flush(); + + //scheduler flushes synchronously, so inner promise does not resolve while flush actions + expect(messages).toEqual([]); + }); +}); \ No newline at end of file diff --git a/src/assert/marbleAssert.ts b/src/assert/marbleAssert.ts index 75d2bc1..8301caf 100644 --- a/src/assert/marbleAssert.ts +++ b/src/assert/marbleAssert.ts @@ -84,27 +84,37 @@ function marbleAssert( expected: TestMessage>> | Readonly>>>> ): void; }; + toEqual( + expected: TestMessage>> | Readonly>>>> + ): void; }; function marbleAssert( source: Array -): { to: { equal(expected: Array): void } }; +): { + to: { equal(expected: Array): void }; + toEqual(expected: Array): void; +}; function marbleAssert( source: | Array | Array>>> | Readonly>>>> -): { to: { equal(expected: object): void } } { +): { + to: { equal(expected: object): void }; + toEqual(expected: object): void; +} { const isSourceArray = Array.isArray(source); if (!isSourceArray) { throw new Error('Cannot assert non array'); } const isSourceSubscription = source.length > 0 && (source as Array).every((v) => v instanceof SubscriptionLog); - + const equal = isSourceSubscription ? subscriptionMarbleAssert(source as any) : observableMarbleAssert(source as any); return { to: { - equal: isSourceSubscription ? subscriptionMarbleAssert(source as any) : observableMarbleAssert(source as any), + equal, }, + toEqual: equal, }; } diff --git a/src/index.ts b/src/index.ts index b84d1d9..b761726 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,43 +1,60 @@ import { marbleAssert } from './assert/marbleAssert'; -import { RxSandbox } from './interfaces/RxSandbox'; +import { RxAsyncSandboxInstance, RxSandboxInstance } from './interfaces/RxSandboxInstance'; +import { AsyncFlushSandboxOption, SandboxOption } from './interfaces/SandboxOption'; import { parseObservableMarble } from './marbles/parseObservableMarble'; import { parseSubscriptionMarble } from './marbles/parseSubscriptionMarble'; import { TestMessage } from './message/TestMessage'; import { complete, error, next, subscribe } from './message/TestMessage'; -import { TestScheduler } from './scheduler/TestScheduler'; +import { createTestScheduler } from './scheduler/createTestScheduler'; import { interopOptionsFromArgument } from './utils/interopOptionsFromArgument'; export { - hotObservable, - coldObservable, - flushScheduler, - advanceToScheduler, - getObservableMessage, expectedObservable, expectedSubscription, RxSandboxInstance, + RxAsyncSandboxInstance } from './interfaces/RxSandboxInstance'; type marbleAssertion = typeof marbleAssert; -const rxSandbox: RxSandbox = { - create: (...args: Array) => { - const { autoFlush, frameTimeFactor, maxFrameValue } = interopOptionsFromArgument(args); +/** + * Creates a new instance of test scheduler for testing observables. + * + * @return {RxSandboxInstance} instance of test scheduler interfaces. + */ +function create(autoFlush?: boolean, frameTimeFactor?: number, maxFrameValue?: number): RxSandboxInstance; +/** + * Creates a new instance of test scheduler flushes action with native async tick for testing observables. + * + * NOTE: this is beta feature and likely have some issues. Also Until stablized internal implementation can change without sember breaking. + * @param {AsyncFlushSandboxOption} [options] customizable options to create test scheduler + */ +function create(options: AsyncFlushSandboxOption): RxAsyncSandboxInstance; +/** + * Creates a new instance of test scheduler for testing observables. + * + * @param {SandboxOptions} [options] customizable options to create test scheduler. + * @return {RxSandboxInstance} instance of test scheduler interfaces. + */ +function create(options?: Partial): RxSandboxInstance; +function create(...args: Array): any { + const { autoFlush, frameTimeFactor, maxFrameValue, flushWithAsyncTick } = interopOptionsFromArgument(args); - const scheduler = new TestScheduler(autoFlush, frameTimeFactor, Math.round(maxFrameValue / frameTimeFactor)); + // to get overloaded signatures + const instance = flushWithAsyncTick ? + createTestScheduler(autoFlush, frameTimeFactor, Math.round(maxFrameValue / frameTimeFactor), true) : + createTestScheduler(autoFlush, frameTimeFactor, Math.round(maxFrameValue / frameTimeFactor), false); - return { - scheduler, - hot: scheduler.createHotObservable.bind(scheduler) as typeof scheduler.createHotObservable, - cold: scheduler.createColdObservable.bind(scheduler) as typeof scheduler.createColdObservable, - flush: scheduler.flush.bind(scheduler) as typeof scheduler.flush, - advanceTo: scheduler.advanceTo.bind(scheduler) as typeof scheduler.advanceTo, - getMessages: scheduler.getMessages.bind(scheduler) as typeof scheduler.getMessages, - e: (marble: string, value?: { [key: string]: T } | null, error?: any) => - parseObservableMarble(marble, value, error, true, frameTimeFactor, frameTimeFactor * maxFrameValue), - s: (marble: string) => parseSubscriptionMarble(marble, frameTimeFactor, frameTimeFactor * maxFrameValue), - }; - }, - marbleAssert: marbleAssert, + return { + ...instance, + e: (marble: string, value?: { [key: string]: T } | null, error?: any) => + parseObservableMarble(marble, value, error, true, frameTimeFactor, frameTimeFactor * maxFrameValue), + s: (marble: string) => parseSubscriptionMarble(marble, frameTimeFactor, frameTimeFactor * maxFrameValue), + }; +} + +const rxSandbox = { + create, + marbleAssert, }; export { rxSandbox, TestMessage, marbleAssertion, next, error, complete, subscribe }; diff --git a/src/interfaces/ReturnTypeWithArgs.ts b/src/interfaces/ReturnTypeWithArgs.ts new file mode 100644 index 0000000..605c6e9 --- /dev/null +++ b/src/interfaces/ReturnTypeWithArgs.ts @@ -0,0 +1,22 @@ +/** + * To pick up specific overloaded fn signature when calculate return type signatures for creating test scheduler + */ +type ReturnTypeWithArgs any, ARGS_T> = Extract< + T extends { + (...args: infer A1): infer R1; + (...args: infer A2): infer R2; + (...args: infer A3): infer R3; + (...args: infer A4): infer R4; + } + ? [A1, R1] | [A2, R2] | [A3, R3] | [A4, R4] + : T extends { (...args: infer A1): infer R1; (...args: infer A2): infer R2; (...args: infer A3): infer R3 } + ? [A1, R1] | [A2, R2] | [A3, R3] + : T extends { (...args: infer A1): infer R1; (...args: infer A2): infer R2 } + ? [A1, R1] | [A2, R2] + : T extends { (...args: infer A1): infer R1 } + ? [A1, R1] + : never, + [ARGS_T, any] +>[1]; + +export { ReturnTypeWithArgs }; diff --git a/src/interfaces/RxAsyncSandboxInstance.ts b/src/interfaces/RxAsyncSandboxInstance.ts deleted file mode 100644 index 9464327..0000000 --- a/src/interfaces/RxAsyncSandboxInstance.ts +++ /dev/null @@ -1,3 +0,0 @@ -interface RxAsyncSandboxInstance {} - -export { RxAsyncSandboxInstance }; diff --git a/src/interfaces/RxSandbox.ts b/src/interfaces/RxSandbox.ts deleted file mode 100644 index 6018ba0..0000000 --- a/src/interfaces/RxSandbox.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { TestMessage } from '../message/TestMessage'; -import { SubscriptionLog } from '../utils/coreInternalImport'; -import { RxAsyncSandboxInstance } from './RxAsyncSandboxInstance'; -import { RxSandboxInstance } from './RxSandboxInstance'; -import { AsyncFlushSandboxOption, SandboxOption } from './SandboxOption'; - -export interface RxSandbox { - /** - * Creates new instance of test scheduler for testing observables. - * - * @param {SandboxOptions} [options] customizable options to create test scheduler. - * @return {RxSandboxInstance} instance of test scheduler interfaces. - */ - create(autoFlush?: boolean, frameTimeFactor?: number, maxFrameValue?: number): RxSandboxInstance; - create(options: AsyncFlushSandboxOption): RxAsyncSandboxInstance; - create(options?: Partial): RxSandboxInstance; - /** - * Utility assertion method to assert marble based observable test messages. - * By default return values of sandbox functions are plain object works with - * any testing framework or assertion library, while this assertion provides - * better visualization against observable test messages. - * - */ - marbleAssert( - source: Array>>> | Readonly>>>> - ): { - to: { - equal( - expected: - | Array>>> - | Readonly>>>> - ): void; - }; - }; - marbleAssert(source: Array): { to: { equal(expected: Array): void } }; -} diff --git a/src/interfaces/RxSandboxInstance.ts b/src/interfaces/RxSandboxInstance.ts index b865fc0..27bfa4a 100644 --- a/src/interfaces/RxSandboxInstance.ts +++ b/src/interfaces/RxSandboxInstance.ts @@ -1,50 +1,31 @@ -import { SchedulerLike } from 'rxjs'; import { TestMessage } from '../message/TestMessage'; -import { TestScheduler } from '../scheduler/TestScheduler'; +import { AsyncSchedulerInstance, SchedulerInstance } from '../scheduler/createTestScheduler'; import { SubscriptionLog } from '../utils/coreInternalImport'; -type hotObservable = typeof TestScheduler.prototype.createHotObservable; -type coldObservable = typeof TestScheduler.prototype.createColdObservable; -type flushScheduler = typeof TestScheduler.prototype.flush; -type advanceToScheduler = typeof TestScheduler.prototype.advanceTo; -type getObservableMessage = typeof TestScheduler.prototype.getMessages; -type expectedObservable = ( - marble: string, - value?: { [key: string]: T } | null, - error?: any -) => Readonly>>>>; -type expectedSubscription = (marble: string) => SubscriptionLog; - -interface RxSandboxInstance { - /** - * Test scheduler created for sandbox instance - */ - scheduler: SchedulerLike & Pick; - /** - * Creates a hot observable using marble diagram DSL, or TestMessage. - */ - hot: hotObservable; - /** - * Creates a cold obsrevable using marbld diagram DSL, or TestMessage. - */ - cold: coldObservable; - /** - * Flush out currently scheduled observables, fill values returned by `getMarbles`. - */ - flush: flushScheduler; - /** - * Flush out currently scheduled observables, only until reaches frame specfied. - */ - advanceTo: advanceToScheduler; - /** - * Get array of observable value's metadata TestMessage from observable - * created via `hot` or `cold`. Returned array will be filled once scheduler flushes - * scheduled actions, either via explicit `flush` or implicit `autoFlush`. - */ - getMessages: getObservableMessage; +/** + * Sandbox instance with test scheduler. All actions scheduled via scheduler + * will be flushed synchronously. + */ +interface RxSandboxInstance extends SchedulerInstance { + e: expectedObservable; /** - * Utility function to generate `expected` values via marble diagram. + * Utility function to generate `expected` subscriptions via marble diagram. */ + s: expectedSubscription; +} + +/** + * + * Sandbox instance with test scheduler. + * Scheduler will schedule actions into native async `tick`, trying to resolve any + * native async functions in innerobservables. + * + * Scheduler will no longer flush actions synchronously in result. + * + * @experimental Not a final implementation. Either interface or implementation can change + * without major breaking version bump. + */ +interface RxAsyncSandboxInstance extends AsyncSchedulerInstance { e: expectedObservable; /** * Utility function to generate `expected` subscriptions via marble diagram. @@ -52,13 +33,16 @@ interface RxSandboxInstance { s: expectedSubscription; } +type expectedObservable = ( + marble: string, + value?: { [key: string]: T } | null, + error?: any +) => Readonly>>>>; +type expectedSubscription = (marble: string) => SubscriptionLog; + export { - hotObservable, - coldObservable, - flushScheduler, - advanceToScheduler, - getObservableMessage, expectedObservable, expectedSubscription, RxSandboxInstance, + RxAsyncSandboxInstance }; diff --git a/src/scheduler/TestScheduler.ts b/src/scheduler/TestScheduler.ts deleted file mode 100644 index c7b2108..0000000 --- a/src/scheduler/TestScheduler.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { VirtualAction, VirtualTimeScheduler } from 'rxjs'; -import { Observable, ObservableNotification, Subscription } from 'rxjs'; -import { parseObservableMarble } from '../marbles/parseObservableMarble'; -import { SubscriptionMarbleToken } from '../marbles/SubscriptionMarbleToken'; -import { TestMessage } from '../message/TestMessage'; -import { TestMessageValue } from '../message/TestMessage'; -import { - AsyncAction, - ColdObservable, - COMPLETE_NOTIFICATION, - errorNotification, - HotObservable, - nextNotification, -} from '../utils/coreInternalImport'; -import { calculateSubscriptionFrame } from './calculateSubscriptionFrame'; - -/** - * @internal - */ -class TestScheduler extends VirtualTimeScheduler { - private readonly coldObservables: Array> = []; - private readonly hotObservables: Array> = []; - private flushed: boolean = false; - private flushing: boolean = false; - - private readonly _maxFrame: number; - - constructor(private readonly autoFlush: boolean, private readonly frameTimeFactor: number, maxFrameValue: number) { - super(VirtualAction, Number.POSITIVE_INFINITY); - this._maxFrame = maxFrameValue * frameTimeFactor; - } - - public get maxFrame(): number { - return this._maxFrame; - } - - public flush(): void { - this.flushUntil(); - } - - public getMessages(observable: Observable, unsubscriptionMarbles: string | null = null) { - const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame( - observable, - unsubscriptionMarbles, - this.frameTimeFactor - ); - - const observableMetadata: Array>>> = []; - const pushMetadata = (notification: ObservableNotification>>) => - observableMetadata.push(new TestMessageValue>>(this.frame, notification)); - - let subscription: Subscription | null = null; - this.schedule(() => { - subscription = observable.subscribe( - (value: T) => - pushMetadata( - nextNotification( - value instanceof Observable ? this.materializeInnerObservable(value, this.frame) : value - ) - ), - (err: any) => pushMetadata(errorNotification(err)), - () => pushMetadata(COMPLETE_NOTIFICATION) - ); - }, subscribedFrame); - - if (unsubscribedFrame !== Number.POSITIVE_INFINITY) { - this.schedule(() => subscription!.unsubscribe(), unsubscribedFrame); - } - - if (this.autoFlush) { - if (this.flushed) { - throw new Error(`Cannot schedule to get marbles, scheduler's already flushed`); - } - this.flush(); - } - - return observableMetadata; - } - - public createColdObservable( - marble: string, - value?: { [key: string]: T } | null, - error?: any - ): ColdObservable; - public createColdObservable(message: Array>): ColdObservable; - public createColdObservable(...args: Array): ColdObservable { - const [marbleValue, value, error] = args; - - if (typeof marbleValue === 'string' && marbleValue.indexOf(SubscriptionMarbleToken.SUBSCRIBE) !== -1) { - throw new Error(`Cold observable cannot have subscription offset ${SubscriptionMarbleToken.SUBSCRIBE}`); - } - - const messages = Array.isArray(marbleValue) - ? marbleValue - : (parseObservableMarble(marbleValue, value, error, false, this.frameTimeFactor, this._maxFrame) as any); - const observable = new ColdObservable(messages as Array>>>, this); - this.coldObservables.push(observable); - return observable; - } - - public createHotObservable( - marble: string, - value?: { [key: string]: T } | null, - error?: any - ): HotObservable; - public createHotObservable(message: Array>): HotObservable; - public createHotObservable(...args: Array): HotObservable { - const [marbleValue, value, error] = args; - - const messages = Array.isArray(marbleValue) - ? marbleValue - : (parseObservableMarble(marbleValue, value, error, false, this.frameTimeFactor, this._maxFrame) as any); - const subject = new HotObservable(messages as Array>>>, this); - this.hotObservables.push(subject); - return subject; - } - - public advanceTo(toFrame: number): void { - if (this.autoFlush) { - throw new Error('Cannot advance frame manually with autoflushing scheduler'); - } - - if (toFrame < 0 || toFrame < this.frame) { - throw new Error(`Cannot advance frame, given frame is either negative or smaller than current frame`); - } - - this.flushUntil(toFrame); - this.frame = toFrame; - } - - private materializeInnerObservable(observable: Observable, outerFrame: number): Array> { - const innerObservableMetadata: Array> = []; - const pushMetaData = (notification: ObservableNotification) => - innerObservableMetadata.push(new TestMessageValue(this.frame - outerFrame, notification)); - - observable.subscribe( - (value) => pushMetaData(nextNotification(value)), - (err) => pushMetaData(errorNotification(err)), - () => pushMetaData(COMPLETE_NOTIFICATION) - ); - - return innerObservableMetadata; - } - - private peek(): AsyncAction | null { - const { actions } = this; - return actions && actions.length > 0 ? actions[0] : null; - } - - private flushUntil(toFrame: number = this.maxFrame): void { - if (this.flushing) { - return; - } - - const { hotObservables } = this; - while (hotObservables.length > 0) { - hotObservables.shift()!.setup(); - } - - this.flushing = true; - - const { actions } = this; - let error: any; - let action: AsyncAction | null | undefined = null; - - while (this.flushing && (action = this.peek()) && action.delay <= toFrame) { - const action: AsyncAction = actions.shift()!; - this.frame = action.delay; - - if ((error = action.execute(action.state, action.delay))) { - break; - } - } - - this.flushing = false; - - if (toFrame >= this.maxFrame) { - this.flushed = true; - } - - if (error) { - while ((action = actions.shift())) { - action.unsubscribe(); - } - throw error; - } - } -} - -export { TestScheduler }; diff --git a/src/scheduler/createTestScheduler.ts b/src/scheduler/createTestScheduler.ts new file mode 100644 index 0000000..ce1e865 --- /dev/null +++ b/src/scheduler/createTestScheduler.ts @@ -0,0 +1,377 @@ +import { SchedulerLike, VirtualAction, VirtualTimeScheduler } from 'rxjs'; +import { Observable, ObservableNotification, Subscription } from 'rxjs'; +import { ReturnTypeWithArgs } from '../interfaces/ReturnTypeWithArgs'; +import { parseObservableMarble } from '../marbles/parseObservableMarble'; +import { SubscriptionMarbleToken } from '../marbles/SubscriptionMarbleToken'; +import { TestMessage } from '../message/TestMessage'; +import { TestMessageValue } from '../message/TestMessage'; +import { + AsyncAction, + ColdObservable, + COMPLETE_NOTIFICATION, + errorNotification, + HotObservable, + nextNotification, +} from '../utils/coreInternalImport'; +import { calculateSubscriptionFrame } from './calculateSubscriptionFrame'; + +/** + * State to be bind into each function we'll create for testscheduler. + */ +interface SandboxState { + coldObservables: Array>; + hotObservables: Array>; + flushed: boolean; + flushing: boolean; + maxFrame: Readonly; + frameTimeFactor: number; + scheduler: VirtualTimeScheduler; + autoFlush: boolean; +} + +/** + * Naive utility fn to determine if given object is promise. + */ +const isPromise = (obj: any): obj is Promise => !!obj && Promise.resolve(obj) == obj; + +/** + * Creates `createColdObservable` function. + */ +const getCreateColdObservable = (state: SandboxState) => { + const { frameTimeFactor, maxFrame, scheduler } = state; + + function createColdObservable( + marble: string, + value?: { [key: string]: T } | null, + error?: any + ): ColdObservable; + function createColdObservable(message: Array>): ColdObservable; + function createColdObservable(...args: Array): ColdObservable { + const [marbleValue, value, error] = args; + + if (typeof marbleValue === 'string' && marbleValue.indexOf(SubscriptionMarbleToken.SUBSCRIBE) !== -1) { + throw new Error(`Cold observable cannot have subscription offset ${SubscriptionMarbleToken.SUBSCRIBE}`); + } + + const messages = Array.isArray(marbleValue) + ? marbleValue + : (parseObservableMarble(marbleValue, value, error, false, frameTimeFactor, maxFrame) as any); + const observable = new ColdObservable(messages as Array>>>, scheduler); + state.coldObservables.push(observable); + return observable; + } + + return createColdObservable; +}; + +/** + * Creates `createHotObservable` function. + */ +const getCreateHotObservable = (state: SandboxState) => { + const { frameTimeFactor, maxFrame, scheduler } = state; + + function createHotObservable( + marble: string, + value?: { [key: string]: T } | null, + error?: any + ): HotObservable; + function createHotObservable(message: Array>): HotObservable; + function createHotObservable(...args: Array): HotObservable { + const [marbleValue, value, error] = args; + + const messages = Array.isArray(marbleValue) + ? marbleValue + : (parseObservableMarble(marbleValue, value, error, false, frameTimeFactor, maxFrame) as any); + const subject = new HotObservable(messages as Array>>>, scheduler); + state.hotObservables.push(subject); + return subject; + } + + return createHotObservable; +}; + +/** + * Create `flush` functions for given scheduler. If `flushWithAsyncTick` specified, + * will create flush function to schedule individual actions into native tick. + * + * As we don't inherit virtualtimescheduler anymore, only these functions should be + * used to properly flush out actions. Calling `scheduler.flush()` will not do any work. + */ +function getSchedulerFlushFunctions(state: SandboxState, flushWithAsyncTick: true): { + flushUntil: (toFrame?: number) => Promise; + advanceTo: (toFrame?: number) => Promise; +}; +function getSchedulerFlushFunctions(state: SandboxState, flushWithAsyncTick: false): { + flushUntil: (toFrame?: number) => void; + advanceTo: (toFrame?: number) => void; +}; +function getSchedulerFlushFunctions(state: SandboxState, flushWithAsyncTick: boolean): any { + const { maxFrame, autoFlush } = state; + + const flushUntil = (toFrame: number = maxFrame): Promise | void => { + if (state.flushing) { + if (flushWithAsyncTick) { + return Promise.resolve(); + } + } + + if (state.flushed) { + throw new Error(`Cannot schedule to get marbles, scheduler's already flushed`); + } + + while (state.hotObservables.length > 0) { + state.hotObservables.shift()!.setup(); + } + + state.flushing = true; + + /** + * Custom loop actions to schedule flusing actions synchronously or asynchronously based on flag. + * + * For synchronous loop, it'll use plain `while` loop. In case of flushing with tick, each action + * will be scheduled into promise instead. + */ + function loopActions(loopState: SandboxState, + condition: (loopState: SandboxState) => boolean, + fn: (loopState: SandboxState) => Error | undefined): Promise | Error | undefined { + if (!flushWithAsyncTick) { + let fnResult; + while (condition(loopState)) { + fnResult = fn(loopState); + if (!!fnResult) { + break; + } + } + + return fnResult; + } else { + function loopWithTick(tickState: SandboxState, error?: Error): Promise { + if (condition(tickState) && !error) { + const p = new Promise((res) => res(fn(tickState))); + return p.then((result: Error | undefined) => loopWithTick(tickState, result)); + } else { + return Promise.resolve(error); + } + } + + return loopWithTick(state); + } + } + + // flush actions via custom loop fn, as same as + // https://github.com/kwonoj/rx-sandbox/blob/c2922e5c5e2503739c64af626f2861b1e1f38159/src/scheduler/TestScheduler.ts#L166-L173 + const loopResult = loopActions(state, (flushState) => { + const action = flushState.scheduler.actions[0]; + return !!action && action.delay <= toFrame; + }, (flushState) => { + const action = flushState.scheduler.actions.shift()!; + flushState.scheduler.frame = action.delay; + + return action.execute(action.state, action.delay); + }); + + const tearDown = (error?: Error) => { + state.flushing = false; + + if (toFrame >= maxFrame) { + state.flushed = true; + } + + if (error) { + const { actions } = state.scheduler; + let action: AsyncAction | null | undefined = null; + while ((action = actions.shift())) { + action.unsubscribe(); + } + throw error; + } + }; + + if (isPromise(loopResult)) { + return loopResult.then((result) => tearDown(result)); + } else { + tearDown(loopResult); + } + }; + + const advanceTo = (toFrame: number) => { + if (autoFlush) { + const error = new Error('Cannot advance frame manually with autoflushing scheduler'); + if (flushWithAsyncTick) { + return Promise.reject(error); + } + throw error; + } + + if (toFrame < 0 || toFrame < state.scheduler.frame) { + const error = new Error(`Cannot advance frame, given frame is either negative or smaller than current frame`); + if (flushWithAsyncTick) { + return Promise.reject(error); + } + throw error; + } + + const flushResult = flushUntil(toFrame); + const tearDown = () => { state.scheduler.frame = toFrame; }; + return isPromise(flushResult) ? flushResult.then(() => tearDown()) : tearDown(); + }; + + return { flushUntil, advanceTo }; +} + +type getMessages = (observable: Observable, unsubscriptionMarbles?: string | null) => void; +type getMessagesWithTick = (observable: Observable, unsubscriptionMarbles?: string | null) => Promise; + +/** + * create getMessages function. Depends on flush, this'll either work asynchronously or synchronously. + */ +function createGetMessages(state: SandboxState, flush: () => Promise): getMessagesWithTick; +function createGetMessages(state: SandboxState, flush: () => void): getMessages; +function createGetMessages(state: SandboxState, flush: Function): Function { + const { frameTimeFactor, autoFlush } = state; + + const materializeInnerObservable = (observable: Observable, outerFrame: number): Array> => { + const innerObservableMetadata: Array> = []; + const pushMetaData = (notification: ObservableNotification) => + innerObservableMetadata.push(new TestMessageValue(state.scheduler.frame - outerFrame, notification)); + + observable.subscribe( + (value) => pushMetaData(nextNotification(value)), + (err) => pushMetaData(errorNotification(err)), + () => pushMetaData(COMPLETE_NOTIFICATION) + ); + + return innerObservableMetadata; + }; + + const getMessages = (observable: Observable, unsubscriptionMarbles: string | null = null) => { + const { subscribedFrame, unsubscribedFrame } = calculateSubscriptionFrame( + observable, + unsubscriptionMarbles, + frameTimeFactor + ); + + const observableMetadata: Array>>> = []; + const pushMetadata = (notification: ObservableNotification>>) => + observableMetadata.push(new TestMessageValue>>(state.scheduler.frame, notification)); + + let subscription: Subscription | null = null; + state.scheduler.schedule(() => { + subscription = observable.subscribe( + (value: T) => + pushMetadata( + nextNotification( + value instanceof Observable ? materializeInnerObservable(value, state.scheduler.frame) : value + ) + ), + (err: any) => pushMetadata(errorNotification(err)), + () => pushMetadata(COMPLETE_NOTIFICATION) + ); + }, subscribedFrame); + + if (unsubscribedFrame !== Number.POSITIVE_INFINITY) { + state.scheduler.schedule(() => subscription?.unsubscribe(), unsubscribedFrame); + } + + const flushResult = autoFlush ? flush() : null; + if (!isPromise(flushResult)) { + return observableMetadata; + } + + return flushResult.then(() => observableMetadata); + }; + + return getMessages; +} + +const initializeSandboxState = (autoFlush: boolean, frameTimeFactor: number, maxFrameValue: number): SandboxState => { + const maxFrame = maxFrameValue * frameTimeFactor; + return { + coldObservables: [], + hotObservables: [], + flushed: false, + flushing: false, + maxFrame, + frameTimeFactor, + scheduler: new VirtualTimeScheduler(VirtualAction, Number.POSITIVE_INFINITY), + autoFlush + }; +}; + +interface BaseSchedulerInstance { + /** + * Test scheduler created for sandbox instance + */ + scheduler: SchedulerLike; + + /** + * Creates a hot observable using marble diagram DSL, or TestMessage. + */ + hot: ReturnType; + /** + * Creates a cold obsrevable using marbld diagram DSL, or TestMessage. + */ + cold: ReturnType; + /** + * Maxmium frame number scheduler will flush into. + */ + maxFrame: number; +} + +interface SchedulerInstance extends BaseSchedulerInstance { + /** + * Flush out currently scheduled observables, only until reaches frame specfied. + */ + advanceTo: ReturnType['advanceTo']; + /** + * Flush out currently scheduled observables, fill values returned by `getMarbles`. + */ + flush: () => void; + /** + * Get array of observable value's metadata TestMessage from observable + * created via `hot` or `cold`. Returned array will be filled once scheduler flushes + * scheduled actions, either via explicit `flush` or implicit `autoFlush`. + */ + getMessages: ReturnType; +} + +interface AsyncSchedulerInstance extends BaseSchedulerInstance { + /** + * Flush out currently scheduled observables, only until reaches frame specfied. + */ + advanceTo: ReturnTypeWithArgs['advanceTo']; + /** + * Flush out currently scheduled observables, fill values returned by `getMarbles`. + */ + flush: () => Promise; + /** + * Get array of observable value's metadata TestMessage from observable + * created via `hot` or `cold`. Returned array will be filled once scheduler flushes + * scheduled actions, either via explicit `flush` or implicit `autoFlush`. + */ + getMessages: ReturnTypeWithArgs Promise]>; +} + +/** + * Creates a new instance of virtualScheduler, along with utility functions for sandbox assertions. + */ +function createTestScheduler(autoFlush: boolean, frameTimeFactor: number, maxFrameValue: number, flushWithAsyncTick: true): AsyncSchedulerInstance; +function createTestScheduler(autoFlush: boolean, frameTimeFactor: number, maxFrameValue: number, flushWithAsyncTick: false): SchedulerInstance; +function createTestScheduler(autoFlush: boolean, frameTimeFactor: number, maxFrameValue: number, flushWithAsyncTick: boolean): any { + const sandboxState = initializeSandboxState(autoFlush, frameTimeFactor, maxFrameValue); + + const { flushUntil, advanceTo } = getSchedulerFlushFunctions(sandboxState, flushWithAsyncTick as any); + const flush = () => flushUntil(); + + return { + scheduler: sandboxState.scheduler, + advanceTo, + getMessages: createGetMessages(sandboxState, flush), + cold: getCreateColdObservable(sandboxState), + hot: getCreateHotObservable(sandboxState), + flush, + maxFrame: sandboxState.maxFrame, + }; +} + +export { createTestScheduler, SchedulerInstance, AsyncSchedulerInstance }; diff --git a/src/utils/interopOptionsFromArgument.ts b/src/utils/interopOptionsFromArgument.ts index f9b1a6b..cb5dfe8 100644 --- a/src/utils/interopOptionsFromArgument.ts +++ b/src/utils/interopOptionsFromArgument.ts @@ -7,7 +7,7 @@ const defaultOption = { flushWithAsyncTick: false, }; -const interopOptionsFromArgument = (args: Array): SandboxOption => { +const interopOptionsFromArgument = (args: Array): SandboxOption & { flushWithAsyncTick: boolean } => { if (args.length === 0) { return defaultOption; } @@ -19,7 +19,7 @@ const interopOptionsFromArgument = (args: Array): SandboxOption => { frameTimeFactor: args[1] ?? defaultOption.frameTimeFactor, maxFrameValue: args[2] ?? defaultOption.maxFrameValue, flushWithAsyncTick: false, - } as any; + }; } return {