From a31ec148f4f683bba9fab5521320e2a63e8b10e3 Mon Sep 17 00:00:00 2001 From: John Dorlus Date: Fri, 16 Mar 2018 23:05:10 -0400 Subject: [PATCH] Adding firefox to test suite (#17195) * Created general driver to accept an argument select the driver to run. * Added environment variable for driver, changed package.json to use custim leadfoot, and made changes to browserdriver to use any driver. * Made changes per PR. * Fixed all nits. --- package.json | 3 +- .../lib/config/schema.js | 5 + .../browser_driver_api.js} | 25 ++--- .../browser_driver_local_api.js | 75 +++++++++++++++ .../browser_driver_remote_api.js | 12 +++ .../remote/browser_driver_api/index.js | 1 + .../ping.js | 0 .../chromedriver_local_api.js | 64 ------------- .../chromedriver_remote_api.js | 12 --- .../services/remote/chromedriver_api/index.js | 1 - .../services/remote/leadfoot_command.js | 13 ++- test/functional/services/remote/remote.js | 16 +++- yarn.lock | 92 ++++++++++++++++++- 13 files changed, 213 insertions(+), 106 deletions(-) rename test/functional/services/remote/{chromedriver_api/chromedriver_api.js => browser_driver_api/browser_driver_api.js} (59%) create mode 100644 test/functional/services/remote/browser_driver_api/browser_driver_local_api.js create mode 100644 test/functional/services/remote/browser_driver_api/browser_driver_remote_api.js create mode 100644 test/functional/services/remote/browser_driver_api/index.js rename test/functional/services/remote/{chromedriver_api => browser_driver_api}/ping.js (100%) delete mode 100644 test/functional/services/remote/chromedriver_api/chromedriver_local_api.js delete mode 100644 test/functional/services/remote/chromedriver_api/chromedriver_remote_api.js delete mode 100644 test/functional/services/remote/chromedriver_api/index.js diff --git a/package.json b/package.json index 31457f81243f..7c248299b869 100644 --- a/package.json +++ b/package.json @@ -242,6 +242,7 @@ "event-stream": "3.3.2", "expect.js": "0.3.1", "faker": "1.1.0", + "geckodriver": "1.10.0", "getopts": "2.0.0", "grunt": "1.0.1", "grunt-aws-s3": "0.14.5", @@ -269,7 +270,7 @@ "karma-junit-reporter": "1.2.0", "karma-mocha": "1.3.0", "karma-safari-launcher": "1.0.0", - "leadfoot": "1.7.1", + "leadfoot": "silne30/leadfoot#validation_for_response_in_ff58", "license-checker": "^16.0.0", "load-grunt-config": "0.19.2", "makelogs": "^4.0.4", diff --git a/src/functional_test_runner/lib/config/schema.js b/src/functional_test_runner/lib/config/schema.js index 1788ee5ffaa7..cae026a419f3 100644 --- a/src/functional_test_runner/lib/config/schema.js +++ b/src/functional_test_runner/lib/config/schema.js @@ -94,6 +94,11 @@ export const schema = Joi.object().keys({ url: Joi.string().uri({ scheme: /https?/ }).default('http://localhost:9515') }).default(), + firefoxdriver: Joi.object().keys({ + url: Joi.string().uri({ scheme: /https?/ }).default('http://localhost:2828') + }).default(), + + // definition of apps that work with `common.navigateToApp()` apps: Joi.object().pattern( ID_PATTERN, diff --git a/test/functional/services/remote/chromedriver_api/chromedriver_api.js b/test/functional/services/remote/browser_driver_api/browser_driver_api.js similarity index 59% rename from test/functional/services/remote/chromedriver_api/chromedriver_api.js rename to test/functional/services/remote/browser_driver_api/browser_driver_api.js index 28680ec2f8af..4778ec8dfa06 100644 --- a/test/functional/services/remote/chromedriver_api/chromedriver_api.js +++ b/test/functional/services/remote/browser_driver_api/browser_driver_api.js @@ -1,21 +1,21 @@ import { EventEmitter } from 'events'; -import { createLocalChromedriverApi } from './chromedriver_local_api'; -import { createRemoteChromedriverApi } from './chromedriver_remote_api'; +import { createLocalBrowserDriverApi } from './browser_driver_local_api'; +import { createRemoteBrowserDriverApi } from './browser_driver_remote_api'; import { ping } from './ping'; const noop = () => {}; /** - * Api for interacting with a local or remote instance of Chromedriver + * Api for interacting with a local or remote instance of a browser * * @type {Object} */ -export class ChromedriverApi extends EventEmitter { - static async factory(log, url) { +export class BrowserDriverApi extends EventEmitter { + static async factory(log, url, browserType) { return (await ping(url)) - ? createRemoteChromedriverApi(log, url) - : createLocalChromedriverApi(log, url); + ? createRemoteBrowserDriverApi(log, url) + : createLocalBrowserDriverApi(log, url, browserType); } constructor(options = {}) { @@ -25,19 +25,22 @@ export class ChromedriverApi extends EventEmitter { url, start = noop, stop = noop, + requiredCapabilities, } = options; if (!url) { throw new TypeError('url is a required parameter'); } - + this._requiredCapabilities = requiredCapabilities; this._url = url; this._state = undefined; this._callCustomStart = () => start(this); this._callCustomStop = () => stop(this); this._beforeStopFns = []; } - + getRequiredCapabilities() { + return this._requiredCapabilities; + } getUrl() { return this._url; } @@ -52,7 +55,7 @@ export class ChromedriverApi extends EventEmitter { async start() { if (this._state !== undefined) { - throw new Error('Chromedriver can only be started once'); + throw new Error('Driver can only be started once'); } this._state = 'started'; @@ -61,7 +64,7 @@ export class ChromedriverApi extends EventEmitter { async stop() { if (this._state !== 'started') { - throw new Error('Chromedriver can only be stopped after being started'); + throw new Error('Driver can only be stopped after being started'); } this._state = 'stopped'; diff --git a/test/functional/services/remote/browser_driver_api/browser_driver_local_api.js b/test/functional/services/remote/browser_driver_api/browser_driver_local_api.js new file mode 100644 index 000000000000..1ba0183723bf --- /dev/null +++ b/test/functional/services/remote/browser_driver_api/browser_driver_local_api.js @@ -0,0 +1,75 @@ +import { spawn } from 'child_process'; +import { parse as parseUrl } from 'url'; + +import treeKill from 'tree-kill'; +import { delay, fromNode as fcb } from 'bluebird'; +import { path as CHROMEDRIVER_EXEC } from 'chromedriver'; +import { path as FIREFOXDRIVER_EXEC } from 'geckodriver'; + +import { ping } from './ping'; +import { BrowserDriverApi } from './browser_driver_api'; +const START_TIMEOUT = 15000; +const PING_INTERVAL = 500; + +export function createLocalBrowserDriverApi(log, url, browser) { + let runningDriver = null; + const driverName = browser + 'driver'; + switch (browser) { + case 'firefox': + runningDriver = FIREFOXDRIVER_EXEC; + break; + default: + runningDriver = CHROMEDRIVER_EXEC; + } + let proc = null; + + return new BrowserDriverApi({ + url, + requiredCapabilities: Object.create({ browserType: browser }), + + async start(api) { + const { port } = parseUrl(url); + log.debug('Starting local ' + driverName + ' at port %d', port); + + proc = spawn(runningDriver, [`--port=${port}`], { + stdio: ['ignore', 'pipe', 'pipe'], + }); + + proc.stdout.on('data', chunk => { + log.debug('[' + driverName + ':stdout]', chunk.toString('utf8').trim()); + }); + proc.stderr.on('data', chunk => { + log.debug('[' + driverName + ':stderr]', chunk.toString('utf8').trim()); + }); + + proc.on('exit', (code) => { + if (!api.isStopped() || code > 0) { + api.emit('error', new Error(driverName + ` exited with code ${code}`)); + } + }); + + const pingsStartedAt = Date.now(); + while (true) { + log.debug('[' + driverName + ':ping] attempting to reach at %j', url); + if (await ping(url)) { + log.debug('[' + driverName + ':ping] success'); + break; + } else { + log.debug('[' + driverName + ':ping] failure'); + } + + if ((Date.now() - pingsStartedAt) < START_TIMEOUT) { + log.debug('[' + driverName + ':ping] waiting for %d before next ping', PING_INTERVAL); + await delay(PING_INTERVAL); + continue; + } + + throw new Error(driverName + ` did not start within the ${START_TIMEOUT}ms timeout`); + } + }, + + async stop() { + await fcb(cb => treeKill(proc.pid, undefined, cb)); + } + }); +} diff --git a/test/functional/services/remote/browser_driver_api/browser_driver_remote_api.js b/test/functional/services/remote/browser_driver_api/browser_driver_remote_api.js new file mode 100644 index 000000000000..5946bc3cdd3c --- /dev/null +++ b/test/functional/services/remote/browser_driver_api/browser_driver_remote_api.js @@ -0,0 +1,12 @@ +import { BrowserDriverApi } from './browser_driver_api'; + +export function createRemoteBrowserDriverApi(log, url) { + return new BrowserDriverApi({ + url, + + start() { + log.info(`Reusing instance at %j`, url); + } + + }); +} diff --git a/test/functional/services/remote/browser_driver_api/index.js b/test/functional/services/remote/browser_driver_api/index.js new file mode 100644 index 000000000000..7b85aa096de5 --- /dev/null +++ b/test/functional/services/remote/browser_driver_api/index.js @@ -0,0 +1 @@ +export { BrowserDriverApi } from './browser_driver_api'; diff --git a/test/functional/services/remote/chromedriver_api/ping.js b/test/functional/services/remote/browser_driver_api/ping.js similarity index 100% rename from test/functional/services/remote/chromedriver_api/ping.js rename to test/functional/services/remote/browser_driver_api/ping.js diff --git a/test/functional/services/remote/chromedriver_api/chromedriver_local_api.js b/test/functional/services/remote/chromedriver_api/chromedriver_local_api.js deleted file mode 100644 index bf7e24d80329..000000000000 --- a/test/functional/services/remote/chromedriver_api/chromedriver_local_api.js +++ /dev/null @@ -1,64 +0,0 @@ -import { spawn } from 'child_process'; -import { parse as parseUrl } from 'url'; - -import treeKill from 'tree-kill'; -import { delay, fromNode as fcb } from 'bluebird'; -import { path as CHROMEDRIVER_EXEC } from 'chromedriver'; - -import { ping } from './ping'; -import { ChromedriverApi } from './chromedriver_api'; -const START_TIMEOUT = 15000; -const PING_INTERVAL = 500; - -export function createLocalChromedriverApi(log, url) { - let proc = null; - - return new ChromedriverApi({ - url, - - async start(api) { - const { port } = parseUrl(url); - log.info('Starting local chromedriver at port %d', port); - - proc = spawn(CHROMEDRIVER_EXEC, [`--port=${port}`], { - stdio: ['ignore', 'pipe', 'pipe'], - }); - - proc.stdout.on('data', chunk => { - log.debug('[chromedriver:stdout]', chunk.toString('utf8').trim()); - }); - proc.stderr.on('data', chunk => { - log.debug('[chromedriver:stderr]', chunk.toString('utf8').trim()); - }); - - proc.on('exit', (code) => { - if (!api.isStopped() || code > 0) { - api.emit('error', new Error(`Chromedriver exited with code ${code}`)); - } - }); - - const pingsStartedAt = Date.now(); - while (true) { - log.debug('[chromedriver:ping] attempting to reach chromedriver at %j', url); - if (await ping(url)) { - log.debug('[chromedriver:ping] success'); - break; - } else { - log.debug('[chromedriver:ping] failure'); - } - - if ((Date.now() - pingsStartedAt) < START_TIMEOUT) { - log.debug('[chromedriver:ping] waiting for %d before next ping', PING_INTERVAL); - await delay(PING_INTERVAL); - continue; - } - - throw new Error(`Chromedriver did not start within the ${START_TIMEOUT}ms timeout`); - } - }, - - async stop() { - await fcb(cb => treeKill(proc.pid, undefined, cb)); - } - }); -} diff --git a/test/functional/services/remote/chromedriver_api/chromedriver_remote_api.js b/test/functional/services/remote/chromedriver_api/chromedriver_remote_api.js deleted file mode 100644 index fb196ed1e765..000000000000 --- a/test/functional/services/remote/chromedriver_api/chromedriver_remote_api.js +++ /dev/null @@ -1,12 +0,0 @@ -import { ChromedriverApi } from './chromedriver_api'; - -export function createRemoteChromedriverApi(log, url) { - return new ChromedriverApi({ - url, - - start() { - log.info(`Reusing instance at %j`, url); - } - - }); -} diff --git a/test/functional/services/remote/chromedriver_api/index.js b/test/functional/services/remote/chromedriver_api/index.js deleted file mode 100644 index fc5e852fb190..000000000000 --- a/test/functional/services/remote/chromedriver_api/index.js +++ /dev/null @@ -1 +0,0 @@ -export { ChromedriverApi } from './chromedriver_api'; diff --git a/test/functional/services/remote/leadfoot_command.js b/test/functional/services/remote/leadfoot_command.js index 56a3279d8736..7af2e380e640 100644 --- a/test/functional/services/remote/leadfoot_command.js +++ b/test/functional/services/remote/leadfoot_command.js @@ -8,15 +8,14 @@ const SECOND = 1000; const MINUTE = 60 * SECOND; let attemptCounter = 0; -async function attemptToCreateCommand(log, server, chromedriverApi) { +async function attemptToCreateCommand(log, server, driverApi) { const attemptId = ++attemptCounter; - log.debug('[leadfoot:command] Creating session'); - const session = await server.createSession({ browserName: 'chrome' }); + const session = await server.createSession({}, driverApi.getRequiredCapabilities()); if (attemptId !== attemptCounter) return; // abort log.debug('[leadfoot:command] Registerying session for teardown'); - chromedriverApi.beforeStop(async () => session.quit()); + driverApi.beforeStop(async () => session.quit()); if (attemptId !== attemptCounter) return; // abort log.debug('[leadfoot:command] Completing session capabilities'); @@ -29,7 +28,7 @@ async function attemptToCreateCommand(log, server, chromedriverApi) { return { command: new Command(session) }; } -export async function initLeadfootCommand({ log, chromedriverApi }) { +export async function initLeadfootCommand({ log, browserDriverApi }) { return await Promise.race([ (async () => { await delay(2 * MINUTE); @@ -41,7 +40,7 @@ export async function initLeadfootCommand({ log, chromedriverApi }) { // backend (chromedriver in this case). it helps with session management // and all communication to the remote browser go through it, so we shim // some of it's methods to enable very verbose logging. - const server = initVerboseRemoteLogging(log, new Server(chromedriverApi.getUrl())); + const server = initVerboseRemoteLogging(log, new Server(browserDriverApi.getUrl())); // by default, calling server.createSession() automatically fixes the webdriver // "capabilities" hash so that leadfoot knows the hoops it has to jump through @@ -59,7 +58,7 @@ export async function initLeadfootCommand({ log, chromedriverApi }) { while (true) { const command = await Promise.race([ delay(30 * SECOND), - attemptToCreateCommand(log, server, chromedriverApi) + attemptToCreateCommand(log, server, browserDriverApi) ]); if (!command) { diff --git a/test/functional/services/remote/remote.js b/test/functional/services/remote/remote.js index 26fd5b13fd59..5cada9cb00dc 100644 --- a/test/functional/services/remote/remote.js +++ b/test/functional/services/remote/remote.js @@ -1,18 +1,24 @@ import { initLeadfootCommand } from './leadfoot_command'; import { createRemoteInterceptors } from './interceptors'; -import { ChromedriverApi } from './chromedriver_api'; +import { BrowserDriverApi } from './browser_driver_api'; export async function RemoteProvider({ getService }) { const lifecycle = getService('lifecycle'); const config = getService('config'); const log = getService('log'); + const possibleBrowsers = ['chrome', 'firefox']; + const browserType = process.env.TEST_BROWSER_TYPE || 'chrome'; - const chromedriverApi = await ChromedriverApi.factory(log, config.get('chromedriver.url')); - lifecycle.on('cleanup', async () => await chromedriverApi.stop()); + if (!possibleBrowsers.includes(browserType)) { + throw new Error(`Unexpected TEST_BROWSER_TYPE "${browserType}". Valid options are ` + possibleBrowsers.join(',')); + } - await chromedriverApi.start(); + const browserDriverApi = await BrowserDriverApi.factory(log, config.get(browserType + 'driver.url'), browserType); + lifecycle.on('cleanup', async () => await browserDriverApi.stop()); - const { command } = await initLeadfootCommand({ log, chromedriverApi }); + await browserDriverApi.start(); + + const { command } = await initLeadfootCommand({ log, browserDriverApi: browserDriverApi }); const interceptors = createRemoteInterceptors(command); log.info('Remote initialized'); diff --git a/yarn.lock b/yarn.lock index 0d31f88e3c25..1c4d2cf6eab1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -246,6 +246,10 @@ address@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" +adm-zip@0.4.7: + version "0.4.7" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.7.tgz#8606c2cbf1c426ce8c8ec00174447fd49b6eafc1" + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -1460,6 +1464,10 @@ bluebird@2.9.34: version "2.9.34" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.9.34.tgz#2f7b4ec80216328a9fddebdf69c8d4942feff7d8" +bluebird@3.4.6: + version "3.4.6" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" + bluebird@3.5.1, bluebird@^3.3.0: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -2025,6 +2033,10 @@ chokidar@^1.4.1, chokidar@^1.6.0, chokidar@^1.7.0: optionalDependencies: fsevents "^1.0.0" +chownr@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + chromedriver@2.33.2: version "2.33.2" resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-2.33.2.tgz#8fc779d54b6e45bef55d264a1eceed52427a9b49" @@ -2493,7 +2505,7 @@ create-ecdh@^4.0.0: bn.js "^4.1.0" elliptic "^6.0.0" -create-error-class@^3.0.0: +create-error-class@^3.0.0, create-error-class@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" dependencies: @@ -3277,6 +3289,12 @@ dtrace-provider@~0.6: dependencies: nan "^2.0.8" +duplexer2@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + dependencies: + readable-stream "^2.0.2" + duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -4516,6 +4534,15 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +geckodriver@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-1.10.0.tgz#73e2f785666521d0d3a9ddc9fd5a0a5e3bf47845" + dependencies: + adm-zip "0.4.7" + bluebird "3.4.6" + got "5.6.0" + tar "4.0.2" + generate-function@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" @@ -4753,6 +4780,27 @@ good-squeeze@2.1.0: hoek "2.x.x" json-stringify-safe "5.0.x" +got@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-5.6.0.tgz#bb1d7ee163b78082bbc8eb836f3f395004ea6fbf" + dependencies: + create-error-class "^3.0.1" + duplexer2 "^0.1.4" + is-plain-obj "^1.0.0" + is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + node-status-codes "^1.0.0" + object-assign "^4.0.1" + parse-json "^2.1.0" + pinkie-promise "^2.0.0" + read-all-stream "^3.0.0" + readable-stream "^2.0.5" + timed-out "^2.0.0" + unzip-response "^1.0.0" + url-parse-lax "^1.0.0" + got@^3.2.0: version "3.3.1" resolved "https://registry.yarnpkg.com/got/-/got-3.3.1.tgz#e5d0ed4af55fc3eef4d56007769d98192bcb2eca" @@ -6818,9 +6866,9 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -leadfoot@1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/leadfoot/-/leadfoot-1.7.1.tgz#7dae9961f01c9bec862b621ccece0826c8c64599" +leadfoot@silne30/leadfoot#validation_for_response_in_ff58: + version "1.7.4" + resolved "https://codeload.github.com/silne30/leadfoot/tar.gz/460c8ed67c08177adc9c79243ff045880f4cad09" dependencies: dojo "2.0.0-alpha.7" jszip "2.5.0" @@ -7580,6 +7628,18 @@ minimost@^1.0.0: camelcase-keys "^4.0.0" minimist "^1.2.0" +minipass@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.2.1.tgz#5ada97538b1027b4cf7213432428578cb564011f" + dependencies: + yallist "^3.0.0" + +minizlib@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" + dependencies: + minipass "^2.2.1" + mkdirp@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" @@ -7838,6 +7898,10 @@ node-pre-gyp@^0.6.39: tar "^2.2.1" tar-pack "^3.4.0" +node-status-codes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f" + node-uuid@~1.4.0: version "1.4.8" resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" @@ -8238,7 +8302,7 @@ parse-headers@^2.0.0: for-each "^0.3.2" trim "0.0.1" -parse-json@^2.2.0: +parse-json@^2.1.0, parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" dependencies: @@ -10598,6 +10662,16 @@ tar@2.2.0: fstream "^1.0.2" inherits "2" +tar@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.0.2.tgz#e8e22bf3eec330e5c616d415a698395e294e8fad" + dependencies: + chownr "^1.0.1" + minipass "^2.2.1" + minizlib "^1.0.4" + mkdirp "^0.5.0" + yallist "^3.0.2" + tar@^2.2.1, "tar@~2.2.1 ": version "2.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" @@ -11038,6 +11112,10 @@ unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" +unzip-response@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" + unzip-response@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" @@ -11822,6 +11900,10 @@ yallist@^2.0.0, yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" + yargs-parser@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"