From 66b742707726620b8e2404208f5b5594b925e084 Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Thu, 17 Dec 2020 15:24:42 -0600 Subject: [PATCH] feat: initial commit --- .eslintrc | 4 +- README.md | 65 +++++++++++------ package.json | 25 +++++-- src/index.js | 95 ++++++++++++++++++++++++- test/.eslintrc | 2 + test/index.js | 187 +++++++++++++++++++++++++++++++++++++++++++++++-- yarn.lock | 138 ++++++++++++++++++++++++++---------- 7 files changed, 442 insertions(+), 74 deletions(-) diff --git a/.eslintrc b/.eslintrc index 4fa374a..ce1d747 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,6 +8,8 @@ "sourceType": "module" }, "env": { - "es6": true + "es6": true, + "commonjs": true, + "shared-node-browser": true } } diff --git a/README.md b/README.md index 7ad73af..55f7b42 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,52 @@ -# es2015-library-skeleton +# chai-wait-for -[![CircleCI](https://circleci.com/gh/jedwards1211/es2015-library-skeleton.svg?style=svg)](https://circleci.com/gh/jedwards1211/es2015-library-skeleton) -[![Coverage Status](https://codecov.io/gh/jedwards1211/es2015-library-skeleton/branch/master/graph/badge.svg)](https://codecov.io/gh/jedwards1211/es2015-library-skeleton) +[![CircleCI](https://circleci.com/gh/jcoreio/chai-wait-for.svg?style=svg)](https://circleci.com/gh/jcoreio/chai-wait-for) +[![Coverage Status](https://codecov.io/gh/jcoreio/chai-wait-for/branch/master/graph/badge.svg)](https://codecov.io/gh/jcoreio/chai-wait-for) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) -[![npm version](https://badge.fury.io/js/es2015-library-skeleton.svg)](https://badge.fury.io/js/es2015-library-skeleton) +[![npm version](https://badge.fury.io/js/chai-wait-for.svg)](https://badge.fury.io/js/chai-wait-for) -This is my personal skeleton for creating an ES2015 library npm package. You are welcome to use it. +poll an assertion until it succeeds. Provides an especially clean syntax for working with some chai plugins like `chai-fs`, `chai-webdriverio-async` etc: -## Quick start +```js +await waitFor('#submittedMessage').to.have.text('Your changes have been saved!') +``` + +# Usage ```sh -npx 0-60 clone https://github.com/jedwards1211/es2015-library-skeleton.git +npm install --save-dev chai-wait-for ``` -## Tools used - -- babel 7 -- mocha -- chai -- istanbul -- nyc -- eslint -- flow -- prettier -- husky -- semantic-release -- renovate -- Circle CI -- Codecov.io +```js +// First, use the plugin +const chai = require('chai') +const chaiWaitFor = require('chai-wait-for') +chai.use(chaiWaitFor) + +// Then create your `waitFor` with default options: +const waitFor = chaiWaitFor.bindWaitFor({ + // If no assertion attempt succeeds before this time elapses (in milliseconds), the waitFor will fail. + timeout: 5000, + // If an assertion attempt fails, it will retry after this amount of time (in milliseconds) + retryInterval: 100, +}) + +it('wait for something', async function () { + this.timeout(10000) + + const myObj = { foo: 0 } + + setInterval(() => myObj.foo++, 1000) + + // Then use it just like you would expect(): + await waitFor(myObj).to.have.property('foo').that.equals(3) + + // You can also use a getter function: + await waitFor(() => myObj.foo).to.equal(4) + + // If you need to override the defaults: + waitFor.timeout(1000)(myObj).to.have.property('foo').that.equals(3) + waitFor.retryInterval(500)(myObj).to.have.property('foo').that.equals(3) +}) +``` diff --git a/package.json b/package.json index 83ab0c9..ea13f96 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "es2015-library-skeleton", + "name": "chai-wait-for", "version": "0.0.0-development", - "description": "my personal ES2015 library project skeleton", + "description": "poll until an assertion succeeds", "main": "index.js", "sideEffects": false, "scripts": { @@ -61,17 +61,24 @@ }, "repository": { "type": "git", - "url": "https://github.com/jedwards1211/es2015-library-skeleton.git" + "url": "https://github.com/jcoreio/chai-wait-for.git" }, "keywords": [ - "es2015" + "chai", + "wait", + "await", + "async", + "async-await", + "poll", + "assertion", + "retry" ], "author": "Andy Edwards", "license": "MIT", "bugs": { - "url": "https://github.com/jedwards1211/es2015-library-skeleton/issues" + "url": "https://github.com/jcoreio/chai-wait-for/issues" }, - "homepage": "https://github.com/jedwards1211/es2015-library-skeleton#readme", + "homepage": "https://github.com/jcoreio/chai-wait-for#readme", "devDependencies": { "@babel/cli": "^7.12.10", "@babel/core": "^7.12.10", @@ -92,6 +99,9 @@ "babel-eslint": "^10.1.0", "babel-plugin-istanbul": "^6.0.0", "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", + "chai-subset": "^1.6.0", + "chai-webdriverio-async": "^2.0.0", "codecov": "^3.8.1", "copy": "^0.3.2", "cross-env": "^7.0.3", @@ -108,7 +118,8 @@ "prettier": "^2.2.1", "prettier-eslint": "^12.0.0", "rimraf": "^3.0.2", - "semantic-release": "^17.3.0" + "semantic-release": "^17.3.0", + "sinon": "^9.2.2" }, "dependencies": { "@babel/runtime": "^7.12.5" diff --git a/src/index.js b/src/index.js index 194c71f..d74a467 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,93 @@ -/* @flow */ +const chai = require('chai') -/* eslint-disable no-console, no-undef */ -console.log('Hello world!') +class WaitFor { + constructor(options, buildAssertion) { + this.options = options + this.buildAssertion = buildAssertion + } + + then(onResolve, onReject) { + const { timeout, retryInterval } = this.options + let numAttempts = 0 + let startTime = new Date().getTime() + let timeoutTime = startTime + timeout + + const poll = async () => { + numAttempts++ + const thisAttemptStartTime = new Date().getTime() + try { + await this.buildAssertion() + } catch (error) { + const delay = + Math.min(timeoutTime, thisAttemptStartTime + retryInterval) - + new Date().getTime() + + if (delay <= 0) { + error.message += ` (timed out after ${timeout}ms, ${numAttempts} attempts)` + throw error + } + + await new Promise((resolve) => setTimeout(resolve, delay)) + await poll() + } + } + + return poll().then(onResolve, onReject) + } +} + +function bindWaitFor(options) { + const bound = (value, ...args) => + new WaitFor(options, () => + chai.expect(typeof value === 'function' ? value() : value, ...args) + ) + bound.timeout = (timeout) => bindWaitFor({ ...options, timeout }) + bound.retryInterval = (retryInterval) => + bindWaitFor({ ...options, retryInterval }) + return bound +} + +module.exports = (chai, utils) => { + const Assertion = chai.Assertion + + const propertyNames = Object.getOwnPropertyNames(Assertion.prototype) + + const propertyDescs = {} + for (const name of propertyNames) { + propertyDescs[name] = Object.getOwnPropertyDescriptor( + Assertion.prototype, + name + ) + } + + // We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage. + const methodNames = propertyNames.filter((name) => { + return name !== 'assert' && typeof propertyDescs[name].value === 'function' + }) + + methodNames.forEach((methodName) => { + WaitFor.prototype[methodName] = function () { + return new WaitFor(this.options, () => { + const assertion = this.buildAssertion() + return assertion[methodName].apply(assertion, arguments) + }) + } + }) + + const getterNames = propertyNames.filter((name) => { + return name !== '_obj' && typeof propertyDescs[name].get === 'function' + }) + + getterNames.forEach((getterName) => { + Object.defineProperty(WaitFor.prototype, getterName, { + get() { + return new WaitFor(this.options, () => { + const assertion = this.buildAssertion() + return assertion[getterName] + }) + }, + }) + }) +} + +module.exports.bindWaitFor = bindWaitFor diff --git a/test/.eslintrc b/test/.eslintrc index 7eeefc3..16bb156 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -1,5 +1,7 @@ { "env": { + "node": true, + "es6": true, "mocha": true } } diff --git a/test/index.js b/test/index.js index 3fd4478..282bd6b 100644 --- a/test/index.js +++ b/test/index.js @@ -1,7 +1,186 @@ -// @flow +const { describe, it, beforeEach, afterEach } = require('mocha') +const { expect } = require('chai') +const { bindWaitFor } = require('../src/index') +const sinon = require('sinon') -import '../src/index' +const waitFor = bindWaitFor({ retryInterval: 100, timeout: 1000 }) -describe('test setup', () => { - it('works', () => {}) +let browser + +const chai = require('chai') +chai.use(require('chai-subset')) +chai.use(require('chai-as-promised')) +chai.use( + require('chai-webdriverio-async').default({ + $: (selector) => browser.$(selector), + $$: (selector) => browser.$$(selector), + waitUntil: (...args) => browser.waitUntil(...args), + }) +) +chai.use(require('../src/index')) + +describe('waitFor', function () { + let clock + + beforeEach(() => { + clock = sinon.useFakeTimers() + }) + afterEach(() => { + clock.restore() + }) + + it('resolves when an assertion attempt passes within timeout', async function () { + let i = 0 + const values = [ + { foo: 1, bar: 1 }, + { foo: 2, bar: 1 }, + { foo: 3, bar: 1 }, + { foo: 4, bar: 1 }, + ] + await Promise.all([ + waitFor(() => values[i++]).to.containSubset({ foo: 3 }), + clock.tickAsync(501), + ]) + expect(i).to.equal(3) + }) + + it('works with chai-as-promised', async function () { + let i = 0 + const values = [ + { foo: 1, bar: 1 }, + { foo: 2, bar: 1 }, + { foo: 3, bar: 1 }, + { foo: 4, bar: 1 }, + ] + await Promise.all([ + waitFor(async () => values[i++]).to.eventually.containSubset({ + foo: 3, + }), + clock.tickAsync(1001), + ]) + expect(i).to.equal(3) + }) + + it('works with chai-webdriverio-async', async function () { + let i = 0 + const values = ['foo', 'bar', 'baz', 'qux'] + browser = { + $: async () => ({ + getText: async () => values[Math.min(values.length - 1, i++)], + }), + $$: async () => [await browser.$()], + } + await Promise.all([ + waitFor('#foo').to.have.text('baz'), + clock.tickAsync(501), + ]) + expect(i).to.equal(3) + await Promise.all([ + expect( + waitFor('#foo') + .to.have.text('forgh') + .then(() => {}) + ).to.be.rejectedWith( + 'Expected element <#foo> to have text "forgh", but only found: "qux" (timed out after 1000ms, 11 attempts)' + ), + clock.tickAsync(1001), + ]) + }) + + it(`supports non-function assertion._obj`, async function () { + const values = { foo: 1, bar: 1 } + setTimeout(() => (values.foo = 3), 300) + await Promise.all([ + waitFor(values).to.containSubset({ foo: 3 }), + clock.tickAsync(500), + ]) + }) + + it(`works with .have.property`, async function () { + const values = { foo: 1, bar: 1 } + setTimeout(() => (values.foo = 3), 300) + await Promise.all([ + waitFor(values).to.have.property('foo').that.equals(3), + clock.tickAsync(500), + ]) + }) + + it(`supports custom error message`, async function () { + const values = { foo: 1, bar: 1 } + await Promise.all([ + expect( + waitFor(values, 'blah') + .to.containSubset({ foo: 3 }) + .then(() => {}) + ).to.be.rejectedWith( + 'blah: expected { foo: 1, bar: 1 } to contain subset { foo: 3 } (timed out after 1000ms, 11 attempts)' + ), + clock.tickAsync(1001), + ]) + }) + + it('rejects when no assertion attempt passes within timeout', async function () { + let i = 0 + const values = [ + { foo: 1, bar: 1 }, + { foo: 2, bar: 1 }, + { foo: 3, bar: 1 }, + { foo: 4, bar: 1 }, + ] + await Promise.all([ + expect( + waitFor(() => values[Math.min(values.length - 1, i++)]) + .to.containSubset({ foo: 5 }) + .then(() => {}) + ).to.be.rejectedWith( + 'expected { foo: 4, bar: 1 } to contain subset { foo: 5 } (timed out after 1000ms, 11 attempts)' + ), + clock.tickAsync(1001), + ]) + expect(i).to.equal(11) + }) + + it('allows changing the timeout with .timeout()', async function () { + let i = 0 + const values = [ + { foo: 1, bar: 1 }, + { foo: 2, bar: 1 }, + { foo: 3, bar: 1 }, + { foo: 4, bar: 1 }, + ] + await Promise.all([ + expect( + waitFor + .timeout(500)(() => values[Math.min(values.length - 1, i++)]) + .to.containSubset({ foo: 5 }) + .then(() => {}) + ).to.be.rejectedWith( + 'expected { foo: 4, bar: 1 } to contain subset { foo: 5 } (timed out after 500ms, 6 attempts)' + ), + clock.tickAsync(501), + ]) + expect(i).to.equal(6) + }) + + it('allows changing the retry interval with .retryInterval()', async function () { + let i = 0 + const values = [ + { foo: 1, bar: 1 }, + { foo: 2, bar: 1 }, + { foo: 3, bar: 1 }, + { foo: 4, bar: 1 }, + ] + await Promise.all([ + expect( + waitFor + .retryInterval(200)(() => values[Math.min(values.length - 1, i++)]) + .to.containSubset({ foo: 5 }) + .then(() => {}) + ).to.be.rejectedWith( + 'expected { foo: 4, bar: 1 } to contain subset { foo: 5 } (timed out after 1000ms, 6 attempts)' + ), + clock.tickAsync(1001), + ]) + expect(i).to.equal(6) + }) }) diff --git a/yarn.lock b/yarn.lock index 09918a6..862d05c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -849,7 +849,7 @@ pirates "^4.0.0" source-map-support "^0.5.16" -"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.1.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4": version "7.12.5" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== @@ -1297,6 +1297,42 @@ lodash "^4.17.4" read-pkg-up "^7.0.0" +"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": + version "1.8.1" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" + integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/formatio@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089" + integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ== + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^5.0.2" + +"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.3.0": + version "5.3.0" + resolved "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.0.tgz#1d2f0743dc54bf13fe9d508baefacdffa25d4329" + integrity sha512-hXpcfx3aq+ETVBwPlRFICld5EnrkexXuXDwqUNhDdr5L8VjvMeSRwyOa0qL7XFmR+jVWR4rUZtnxlG7RX72sBg== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -2012,6 +2048,25 @@ caseless@~0.12.0: resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +chai-as-promised@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" + integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== + dependencies: + check-error "^1.0.2" + +chai-subset@^1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz#a5d0ca14e329a79596ed70058b6646bd6988cfe9" + integrity sha1-pdDKFOMpp5WW7XAFi2ZGvWmIz+k= + +chai-webdriverio-async@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/chai-webdriverio-async/-/chai-webdriverio-async-2.0.0.tgz#bb43a4693aa726fff5828f81a0c3b03a6bffefb1" + integrity sha512-F25/zwtWy1rRwrHZ+zYcX1Z3SBDT3+XoevBPIQBg7yWoFVp53f1L9Xmy3inzjo04n5cwYWNQxr1OyLodZ3ypNA== + dependencies: + "@babel/runtime" "^7.1.5" + chai@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" @@ -2588,7 +2643,7 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -2728,7 +2783,7 @@ dezalgo@^1.0.0, dezalgo@~1.0.3: asap "^2.0.0" wrappy "1" -diff@4.0.2: +diff@4.0.2, diff@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== @@ -4103,7 +4158,7 @@ import-lazy@^2.1.0: resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -4739,6 +4794,11 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +just-extend@^4.0.2: + version "4.1.1" + resolved "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz#158f1fdb01f128c411dc8b286a7b4837b3545282" + integrity sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA== + kefir@^3.7.3: version "3.8.8" resolved "https://registry.npmjs.org/kefir/-/kefir-3.8.8.tgz#235932ddfbed422acebf5d7cba503035e9ea05c5" @@ -5055,11 +5115,6 @@ lockfile@^1.0.4: dependencies: signal-exit "^3.0.2" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.npmjs.org/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -5068,33 +5123,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*: - version "3.0.1" - resolved "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= - -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.npmjs.org/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= - -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.npmjs.org/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@*, lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -5125,6 +5158,11 @@ lodash.flattendeep@^4.4.0: resolved "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" @@ -5145,11 +5183,6 @@ lodash.merge@^4.6.0: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.restparam@*: - version "3.6.1" - resolved "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - lodash.template@^4.0.2: version "4.5.0" resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" @@ -5623,6 +5656,17 @@ nerf-dart@^1.0.0: resolved "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz#e6dab7febf5ad816ea81cf5c629c5a0ebde72c1a" integrity sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo= +nise@^4.0.4: + version "4.0.4" + resolved "https://registry.npmjs.org/nise/-/nise-4.0.4.tgz#d73dea3e5731e6561992b8f570be9e363c4512dd" + integrity sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + node-emoji@^1.10.0: version "1.10.0" resolved "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" @@ -6416,6 +6460,13 @@ path-parse@^1.0.6: resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + path-type@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" @@ -7287,6 +7338,19 @@ signale@^1.2.1: figures "^2.0.0" pkg-conf "^2.1.0" +sinon@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/sinon/-/sinon-9.2.2.tgz#b83cf5d43838f99cfa3644453f4c7db23e7bd535" + integrity sha512-9Owi+RisvCZpB0bdOVFfL314I6I4YoRlz6Isi4+fr8q8YQsDPoCe5UnmNtKHRThX3negz2bXHWIuiPa42vM8EQ== + dependencies: + "@sinonjs/commons" "^1.8.1" + "@sinonjs/fake-timers" "^6.0.1" + "@sinonjs/formatio" "^5.0.1" + "@sinonjs/samsam" "^5.3.0" + diff "^4.0.2" + nise "^4.0.4" + supports-color "^7.1.0" + slash@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" @@ -8020,7 +8084,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==