Merge branch 'develop' into v5.0-release

# Conflicts:
#	circle.yml
#	packages/server/test/unit/browsers/firefox_spec.ts
This commit is contained in:
Brian Mann
2020-07-17 18:51:52 -04:00
31 changed files with 637 additions and 136 deletions
+29
View File
@@ -1308,6 +1308,16 @@ jobs:
repo: cypress-example-recipes
command: npm run test:ci:firefox
# This is a special job. It allows you to test the current
# built test runner against a pull request in the repo
# cypress-example-recipes.
# Imagine you are working on a feature and want to show / test a recipe
# You would need to run the built test runner before release
# against a PR that cannot be merged until the new version
# of the test runner is released.
# Use:
# specify pull request number
# and the recipe folder
test-binary-against-recipe-pull-request:
<<: *defaults
steps:
@@ -1317,6 +1327,10 @@ jobs:
command: npm run test:ci
pull_request_id: 515
folder: examples/fundamentals__typescript
- test-binary-against-repo:
command: npm test
pull_request_id: 513
folder: examples/fundamentals__module-api-wrap
"test-binary-against-kitchensink":
<<: *defaults
@@ -1658,6 +1672,21 @@ linux-workflow: &linux-workflow
branches:
only:
- v5.0-release
# when working on a feature or a fix,
# you are probably working in a branch
# and you want to run a specific PR in the cypress-example-recipes
# against this branch. This workflow job includes
# the job but only when it runs on specific branch
# DO NOT DELETE THIS JOB BEFORE MERGING TO DEVELOP
# on "develop" this branch will be ignored anyway
# and someone else might use this job definition for another
# feature branch and would just update the branch filter
- test-binary-against-recipe-pull-request:
name: Test cypress run parsing
filters:
branches:
only:
- cli-to-module-api-7760
requires:
- build-binary
- build-npm-package
+109 -31
View File
@@ -1,4 +1,5 @@
const _ = require('lodash')
const R = require('ramda')
const commander = require('commander')
const { stripIndent } = require('common-tags')
const logSymbols = require('log-symbols')
@@ -25,6 +26,10 @@ const coerceFalse = (arg) => {
return arg !== 'false'
}
const coerceAnyStringToInt = (arg) => {
return typeof arg === 'string' ? parseInt(arg) : arg
}
const spaceDelimitedArgsMsg = (flag, args) => {
let msg = `
${logSymbols.warning} Warning: It looks like you're passing --${flag} a space-separated list of arguments:
@@ -162,7 +167,109 @@ function showVersions () {
.catch(util.logErrorExit1)
}
const createProgram = () => {
const program = new commander.Command()
// bug in commander not printing name
// in usage help docs
program._name = 'cypress'
program.usage('<command> [options]')
return program
}
const addCypressRunCommand = (program) => {
return program
.command('run')
.usage('[options]')
.description('Runs Cypress tests from the CLI without the GUI')
.option('-b, --browser <browser-name-or-path>', text('browserRunMode'))
.option('--ci-build-id <id>', text('ciBuildId'))
.option('-c, --config <config>', text('config'))
.option('-C, --config-file <config-file>', text('configFile'))
.option('-e, --env <env>', text('env'))
.option('--group <name>', text('group'))
.option('-k, --key <record-key>', text('key'))
.option('--headed', text('headed'))
.option('--headless', text('headless'))
.option('--no-exit', text('exit'))
.option('--parallel', text('parallel'))
.option('-p, --port <port>', text('port'))
.option('-P, --project <project-path>', text('project'))
.option('-q, --quiet', text('quiet'))
.option('--record [bool]', text('record'), coerceFalse)
.option('-r, --reporter <reporter>', text('reporter'))
.option('-o, --reporter-options <reporter-options>', text('reporterOptions'))
.option('-s, --spec <spec>', text('spec'))
.option('-t, --tag <tag>', text('tag'))
.option('--dev', text('dev'), coerceFalse)
}
/**
* Casts known command line options for "cypress run" to their intended type.
* For example if the user passes "--port 5005" the ".port" property should be
* a number 5005 and not a string "5005".
*
* Returns a clone of the original object.
*/
const castCypressRunOptions = (opts) => {
// only properties that have type "string | false" in our TS definition
// require special handling, because CLI parsing takes care of purely
// boolean arguments
const result = R.evolve({
port: coerceAnyStringToInt,
configFile: coerceFalse,
})(opts)
return result
}
module.exports = {
/**
* Parses `cypress run` command line option array into an object
* with options that you can feed into a `cypress.run()` module API call.
* @example
* const options = parseRunCommand(['cypress', 'run', '--browser', 'chrome'])
* // options is {browser: 'chrome'}
*/
parseRunCommand (args) {
return new Promise((resolve, reject) => {
if (!Array.isArray(args)) {
return reject(new Error('Expected array of arguments'))
}
// make a copy of the input arguments array
// and add placeholders where "node ..." would usually be
// also remove "cypress" keyword at the start if present
const cliArgs = args[0] === 'cypress' ? [...args.slice(1)] : [...args]
cliArgs.unshift(null, null)
debug('creating program parser')
const program = createProgram()
addCypressRunCommand(program)
.action((...fnArgs) => {
debug('parsed Cypress run %o', fnArgs)
const options = parseVariableOpts(fnArgs, cliArgs)
debug('parsed options %o', options)
const casted = castCypressRunOptions(options)
debug('casted options %o', casted)
resolve(casted)
})
debug('parsing args: %o', cliArgs)
program.parse(cliArgs)
})
},
/**
* Parses the command line and kicks off Cypress process.
*/
init (args) {
if (!args) {
args = process.argv
@@ -194,13 +301,7 @@ module.exports = {
logger.log()
}
const program = new commander.Command()
// bug in commander not printing name
// in usage help docs
program._name = 'cypress'
program.usage('<command> [options]')
const program = createProgram()
program
.command('help')
@@ -215,30 +316,7 @@ module.exports = {
.description(text('version'))
.action(showVersions)
program
.command('run')
.usage('[options]')
.description('Runs Cypress tests from the CLI without the GUI')
.option('-b, --browser <browser-name-or-path>', text('browserRunMode'))
.option('--ci-build-id <id>', text('ciBuildId'))
.option('-c, --config <config>', text('config'))
.option('-C, --config-file <config-file>', text('configFile'))
.option('-e, --env <env>', text('env'))
.option('--group <name>', text('group'))
.option('-k, --key <record-key>', text('key'))
.option('--headed', text('headed'))
.option('--headless', text('headless'))
.option('--no-exit', text('exit'))
.option('--parallel', text('parallel'))
.option('-p, --port <port>', text('port'))
.option('-P, --project <project-path>', text('project'))
.option('-q, --quiet', text('quiet'))
.option('--record [bool]', text('record'), coerceFalse)
.option('-r, --reporter <reporter>', text('reporter'))
.option('-o, --reporter-options <reporter-options>', text('reporterOptions'))
.option('-s, --spec <spec>', text('spec'))
.option('-t, --tag <tag>', text('tag'))
.option('--dev', text('dev'), coerceFalse)
addCypressRunCommand(program)
.action((...fnArgs) => {
debug('running Cypress with args %o', fnArgs)
require('./exec/run')
+17
View File
@@ -7,6 +7,7 @@ const fs = require('./fs')
const open = require('./exec/open')
const run = require('./exec/run')
const util = require('./util')
const cli = require('./cli')
const cypressModuleApi = {
/**
@@ -50,6 +51,22 @@ const cypressModuleApi = {
})
})
},
cli: {
/**
* Parses CLI arguments into an object that you can pass to "cypress.run"
* @example
* const cypress = require('cypress')
* const cli = ['cypress', 'run', '--browser', 'firefox']
* const options = await cypress.cli.parseRunArguments(cli)
* // options is {browser: 'firefox'}
* await cypress.run(options)
* @see https://on.cypress.io/module-api
*/
parseRunArguments (args) {
return cli.parseRunCommand(args)
},
},
}
module.exports = cypressModuleApi
+1 -1
View File
@@ -86,7 +86,7 @@
"mocked-env": "1.2.4",
"nock": "12.0.2",
"postinstall-postinstall": "2.0.0",
"proxyquire": "2.1.0",
"proxyquire": "2.1.3",
"resolve-pkg": "2.0.0",
"shelljs": "0.8.3",
"sinon": "7.2.2",
+67
View File
@@ -172,4 +172,71 @@ describe('cypress', function () {
})
})
})
context('cli', function () {
describe('.parseRunArguments', function () {
it('parses CLI cypress run arguments', async () => {
const args = 'cypress run --browser chrome --spec my/test/spec.js'.split(' ')
const options = await cypress.cli.parseRunArguments(args)
expect(options).to.deep.equal({
browser: 'chrome',
spec: 'my/test/spec.js',
})
})
it('parses CLI cypress run shorthand arguments', async () => {
const args = 'cypress run -b firefox -p 5005 --headed --quiet'.split(' ')
const options = await cypress.cli.parseRunArguments(args)
expect(options).to.deep.equal({
browser: 'firefox',
port: 5005,
headed: true,
quiet: true,
})
})
it('coerces --record and --dev', async () => {
const args = 'cypress run --record false --dev true'.split(' ')
const options = await cypress.cli.parseRunArguments(args)
expect(options).to.deep.equal({
record: false,
dev: true,
})
})
it('parses config file false', async () => {
const args = 'cypress run --config-file false'.split(' ')
const options = await cypress.cli.parseRunArguments(args)
expect(options).to.deep.equal({
configFile: false,
})
})
it('parses config', async () => {
const args = 'cypress run --config baseUrl=localhost,video=true'.split(' ')
const options = await cypress.cli.parseRunArguments(args)
// we don't need to convert the config into an object
// since the logic inside cypress.run handles that
expect(options).to.deep.equal({
config: 'baseUrl=localhost,video=true',
})
})
it('parses env', async () => {
const args = 'cypress run --env MY_NUMBER=42,MY_FLAG=true'.split(' ')
const options = await cypress.cli.parseRunArguments(args)
// we don't need to convert the environment into an object
// since the logic inside cypress.run handles that
expect(options).to.deep.equal({
env: 'MY_NUMBER=42,MY_FLAG=true',
})
})
})
})
})
+25
View File
@@ -306,6 +306,25 @@ declare module 'cypress' {
message: string
}
/**
* Methods allow parsing given CLI arguments the same way Cypress CLI does it.
*/
interface CypressCliParser {
/**
* Parses the given array of string arguments to "cypress run"
* just like Cypress CLI does it.
* @see https://on.cypress.io/module-api
* @example
* const cypress = require('cypress')
* const args = ['cypress', 'run', '--browser', 'chrome']
* const options = await cypress.cli.parseRunArguments(args)
* // options is {browser: 'chrome'}
* // pass the options to cypress.run()
* const results = await cypress.run(options)
*/
parseRunArguments(args: string[]): Promise<Partial<CypressRunOptions>>
}
/**
* Cypress NPM module interface.
* @see https://on.cypress.io/module-api
@@ -337,6 +356,12 @@ declare module 'cypress' {
* @see https://on.cypress.io/module-api#cypress-open
*/
open(options?: Partial<CypressOpenOptions>): Promise<void>
/**
* Utility functions for parsing CLI arguments the same way
* Cypress does
*/
cli: CypressCliParser
}
// export Cypress NPM module interface
+64 -6
View File
@@ -502,6 +502,10 @@ declare namespace Cypress {
}
type CanReturnChainable = void | Chainable
type ThenReturn<S, R> =
R extends void ? Chainable<S> :
R extends R | undefined ? Chainable<S | Exclude<R, undefined>> :
Chainable<S>
/**
* Chainable interface for non-array Subjects
@@ -713,6 +717,18 @@ declare namespace Cypress {
* This means that when you instantiate new Date in your application,
* it will have a time of January 1st, 1970.
*
* To restore the real clock call `.restore()`
*
* @example
* cy.clock()
* ...
* // restore the application clock
* cy.clock().then(clock => {
* clock.restore()
* })
* // or use this shortcut
* cy.clock().invoke('restore')
*
* @see https://on.cypress.io/clock
*/
clock(): Chainable<Clock>
@@ -722,15 +738,22 @@ declare namespace Cypress {
*
* @see https://on.cypress.io/clock
* @example
* // your app code
* // in your app code
* $('#date').text(new Date().toJSON())
* // from spec file
* const now = new Date(2017, 3, 14).getTime() // March 14, 2017 timestamp
* // in the spec file
* // March 14, 2017 timestamp or Date object
* const now = new Date(2017, 2, 14).getTime()
* cy.clock(now)
* cy.visit('/index.html')
* cy.get('#date').contains('2017-03-14')
* // to restore the real clock
* cy.clock().then(clock => {
* clock.restore()
* })
* // or use this shortcut
* cy.clock().invoke('restore')
*/
clock(now: number, options?: Loggable): Chainable<Clock>
clock(now: number|Date, options?: Loggable): Chainable<Clock>
/**
* Mocks global clock but only overrides specific functions.
*
@@ -739,7 +762,7 @@ declare namespace Cypress {
* // keep current date but override "setTimeout" and "clearTimeout"
* cy.clock(null, ['setTimeout', 'clearTimeout'])
*/
clock(now: number, functions?: Array<'setTimeout' | 'clearTimeout' | 'setInterval' | 'clearInterval' | 'Date'>, options?: Loggable): Chainable<Clock>
clock(now: number|Date, functions?: Array<'setTimeout' | 'clearTimeout' | 'setInterval' | 'clearInterval' | 'Date'>, options?: Loggable): Chainable<Clock>
/**
* Mocks global clock and all functions.
*
@@ -1761,12 +1784,24 @@ declare namespace Cypress {
* @see https://on.cypress.io/then
*/
then<S extends object | any[] | string | number | boolean>(fn: (this: ObjectLike, currentSubject: Subject) => S): Chainable<S>
/**
* Enables you to work with the subject yielded from the previous command / promise.
*
* @see https://on.cypress.io/then
*/
then<S>(fn: (this: ObjectLike, currentSubject: Subject) => S): ThenReturn<Subject, S>
/**
* Enables you to work with the subject yielded from the previous command / promise.
*
* @see https://on.cypress.io/then
*/
then<S extends object | any[] | string | number | boolean>(options: Partial<Timeoutable>, fn: (this: ObjectLike, currentSubject: Subject) => S): Chainable<S>
/**
* Enables you to work with the subject yielded from the previous command / promise.
*
* @see https://on.cypress.io/then
*/
then<S>(options: Partial<Timeoutable>, fn: (this: ObjectLike, currentSubject: Subject) => S): ThenReturn<Subject, S>
/**
* Enables you to work with the subject yielded from the previous command.
*
@@ -1791,6 +1826,17 @@ declare namespace Cypress {
* `cy.clock()` must be called before `cy.tick()`
*
* @see https://on.cypress.io/clock
* @example
* cy.clock()
* ...
* // advance time by 10 minutes
* cy.tick(600*1000)
* // you can restore the real clock
* cy.tick(1000).then(clock => {
* clock.restore()
* })
* // or use this shortcut
* cy.tick(5000).invoke('restore')
*/
tick(milliseconds: number): Chainable<Clock>
@@ -4742,10 +4788,22 @@ declare namespace Cypress {
* Move the clock the specified number of `milliseconds`.
* Any timers within the affected range of time will be called.
* @param time Number in ms to advance the clock
* @see https://on.cypress.io/tick
*/
tick(time: number): void
/**
* Restore all overridden native functions. This is automatically called between tests, so should not generally be needed.
* Restore all overridden native functions.
* This is automatically called between tests, so should not generally be needed.
* @see https://on.cypress.io/clock
* @example
* cy.clock()
* cy.visit('/')
* ...
* cy.clock().then(clock => {
* clock.restore()
* })
* // or use this shortcut
* cy.clock().invoke('restore')
*/
restore(): void
}
+33 -2
View File
@@ -179,6 +179,25 @@ cy.wrap(['bar', 'baz'])
})
describe('then', () => {
// https://github.com/cypress-io/cypress/issues/5575
it('should respect the return type of callback', () => {
// Expected type is verbose here because the function below matches 2 declarations.
// * then<S extends object | any[] | string | number | boolean>(fn: (this: ObjectLike, currentSubject: Subject) => S): Chainable<S>
// * then<S>(fn: (this: ObjectLike, currentSubject: Subject) => S): ThenReturn<Subject, S>
// For our purpose, it doesn't matter.
const result = cy.get('foo').then(el => el.attr('foo'))
result // $ExpectType Chainable<JQuery<HTMLElement>> | Chainable<string | JQuery<HTMLElement>>
const result2 = cy.get('foo').then(el => `${el}`)
result2 // $ExpectType Chainable<string>
const result3 = cy.get('foo').then({ timeout: 1234 }, el => el.attr('foo'))
result3 // $ExpectType Chainable<JQuery<HTMLElement>> | Chainable<string | JQuery<HTMLElement>>
const result4 = cy.get('foo').then({ timeout: 1234 }, el => `${el}`)
result4 // $ExpectType Chainable<string>
})
it('should have the correct type signature', () => {
cy.wrap({ foo: 'bar' })
.then(s => {
@@ -380,8 +399,20 @@ namespace CypressTriggerTests {
})
}
const now = new Date(2019, 3, 2).getTime()
cy.clock(now, ['Date'])
namespace CypressClockTests {
// timestamp
cy.clock(new Date(2019, 3, 2).getTime(), ['Date'])
// timestamp shortcut
cy.clock(+ new Date(), ['Date'])
// Date object
cy.clock(new Date(2019, 3, 2))
// restoring the clock
cy.clock().then(clock => {
clock.restore()
})
// restoring the clock shortcut
cy.clock().invoke('restore')
}
namespace CypressContainsTests {
cy.contains('#app')
+1 -1
View File
@@ -171,7 +171,7 @@
"prefixed-list": "1.0.1",
"pretty-ms": "6.0.1",
"print-arch": "1.0.0",
"proxyquire": "2.1.0",
"proxyquire": "2.1.3",
"ramda": "0.24.1",
"shelljs": "0.8.3",
"shx": "0.3.2",
@@ -37,7 +37,7 @@ describe('src/cy/commands/clock', () => {
})
})
it('takes now arg', () => {
it('takes number now arg', () => {
const now = 1111111111111
cy.clock(now).then(function (clock) {
@@ -47,6 +47,18 @@ describe('src/cy/commands/clock', () => {
})
})
it('takes Date now arg', () => {
// April 15, 2017
const now = new Date(2017, 3, 15)
const nowTimestamp = now.getTime()
cy.clock(now).then(function (clock) {
expect(new this.window.Date().getTime()).to.equal(nowTimestamp)
clock.tick(4321)
expect(new this.window.Date().getTime()).to.equal(nowTimestamp + 4321)
})
})
it('restores window time methods when calling restore', (done) => {
cy.clock().then(function (clock) {
this.window.setTimeout(() => {
@@ -326,6 +326,25 @@ describe('per-test config', () => {
})
})
describe('viewport', () => {
// https://github.com/cypress-io/cypress/issues/7631
it('can set viewport in testConfigOverrides', { viewportWidth: 200, viewportHeight: 100 }, () => {
cy.visit('/fixtures/generic.html')
cy.window().then((win) => {
expect(win.innerWidth).eq(200)
expect(win.innerHeight).eq(100)
})
})
it('viewport does not leak between tests', () => {
cy.visit('/fixtures/generic.html')
cy.window().then((win) => {
expect(win.innerWidth).eq(1000)
expect(win.innerHeight).eq(660)
})
})
})
describe('testConfigOverrides baseUrl @slow', () => {
it('visit 1', { baseUrl: 'http://localhost:3501' }, () => {
cy.visit('/fixtures/generic.html')
+4
View File
@@ -43,6 +43,10 @@ module.exports = function (Commands, Cypress, cy, state) {
return clock
}
if (_.isDate(now)) {
now = now.getTime()
}
if (_.isObject(now)) {
userOptions = now
now = undefined
+5 -3
View File
@@ -29,8 +29,8 @@ const validOrientations = ['landscape', 'portrait']
// refresh would cause viewport to hang
let currentViewport = null
module.exports = (Commands, Cypress, cy, state, config) => {
const defaultViewport = _.pick(config(), 'viewportWidth', 'viewportHeight')
module.exports = (Commands, Cypress, cy, state) => {
const defaultViewport = _.pick(Cypress.config(), 'viewportWidth', 'viewportHeight')
// currentViewport could already be set due to previous runs
currentViewport = currentViewport || defaultViewport
@@ -41,7 +41,9 @@ module.exports = (Commands, Cypress, cy, state, config) => {
// need to restore prior to running the next test
// after which we simply null and wait for the
// next viewport change
setViewportAndSynchronize(defaultViewport.viewportWidth, defaultViewport.viewportHeight)
const configDefaultViewport = _.pick(Cypress.config(), 'viewportWidth', 'viewportHeight')
setViewportAndSynchronize(configDefaultViewport.viewportWidth, configDefaultViewport.viewportHeight)
})
const setViewportAndSynchronize = (width, height) => {
+1 -1
View File
@@ -27,7 +27,7 @@
"devDependencies": {
"chai": "3.5.0",
"cross-env": "6.0.3",
"cypress-example-kitchensink": "1.11.1",
"cypress-example-kitchensink": "1.11.2",
"gulp": "4.0.2",
"gulp-clean": "0.4.0",
"gulp-gh-pages": "0.6.0-6",
+4 -1
View File
@@ -70,7 +70,10 @@ class Test extends Component<Props> {
if (appState.autoScrollingEnabled && appState.isRunning && shouldRender && isActive != null) {
window.requestAnimationFrame(() => {
scroller.scrollIntoView(this.containerRef.current as HTMLElement)
// since this executes async in a RAF the ref might be null
if (this.containerRef.current) {
scroller.scrollIntoView(this.containerRef.current as HTMLElement)
}
})
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
const _ = require('lodash')
const uuid = require('uuid')
const { v4: uuidv4 } = require('uuid')
const Promise = require('bluebird')
const Cookies = require('./cookies')
const Screenshot = require('./screenshot')
@@ -43,7 +43,7 @@ module.exports = {
const requestAutomationResponse = (message, data, fn) => {
return new Promise((resolve, reject) => {
const id = uuid.v4()
const id = uuidv4()
requests[id] = function (obj) {
// normalize the error from automation responses
+1 -1
View File
@@ -11,7 +11,7 @@ const debugVerboseSend = debugModule('cypress-verbose:server:browsers:cri-client
// debug using cypress-verbose:server:browsers:cri-client:recv:*
const debugVerboseReceive = debugModule('cypress-verbose:server:browsers:cri-client:recv:[<--]')
const WEBSOCKET_NOT_OPEN_RE = /^WebSocket is not open/
const WEBSOCKET_NOT_OPEN_RE = /^WebSocket is (?:not open|already in CLOSING or CLOSED state)/
/**
* Url returned by the Chrome Remote Interface
@@ -177,6 +177,10 @@ export default {
const { browser } = foxdriver
browser.on('error', (err) => {
debug('received error from foxdriver connection, ignoring %o', err)
})
forceGcCc = () => {
let gcDuration; let ccDuration
+28 -3
View File
@@ -9,7 +9,10 @@ import FirefoxProfile from 'firefox-profile'
import firefoxUtil from './firefox-util'
import utils from './utils'
import * as launcherDebug from '@packages/launcher/lib/log'
import { Browser } from './types'
import { Browser, BrowserInstance } from './types'
import { EventEmitter } from 'events'
import os from 'os'
import treeKill from 'tree-kill'
const errors = require('../errors')
const debug = Debug('cypress:server:browsers:firefox')
@@ -287,7 +290,23 @@ const defaultPreferences = {
'marionette.log.level': launcherDebug.log.enabled ? 'Debug' : undefined,
}
export async function open (browser: Browser, url, options: any = {}) {
export function _createDetachedInstance (browserInstance: BrowserInstance): BrowserInstance {
const detachedInstance: BrowserInstance = new EventEmitter() as BrowserInstance
detachedInstance.pid = browserInstance.pid
// kill the entire process tree, from the spawned instance up
detachedInstance.kill = (): void => {
treeKill(browserInstance.pid, (err?, result?) => {
debug('force-exit of process tree complete %o', { err, result })
detachedInstance.emit('exit')
})
}
return detachedInstance
}
export async function open (browser: Browser, url, options: any = {}): Bluebird<BrowserInstance> {
const defaultLaunchOptions = utils.getDefaultLaunchOptions({
extensions: [] as string[],
preferences: _.extend({}, defaultPreferences),
@@ -347,7 +366,7 @@ export async function open (browser: Browser, url, options: any = {}) {
launchOptions,
] = await Bluebird.all([
utils.ensureCleanCache(browser, options.isTextTerminal),
utils.writeExtension(browser, options.isTextTerminal, options.proxyUrl, options.socketIoRoute, options.onScreencastFrame),
utils.writeExtension(browser, options.isTextTerminal, options.proxyUrl, options.socketIoRoute),
utils.executeBeforeBrowserLaunch(browser, defaultLaunchOptions, options),
])
@@ -419,5 +438,11 @@ export async function open (browser: Browser, url, options: any = {}) {
errors.throw('FIREFOX_COULD_NOT_CONNECT', err)
})
if (os.platform() === 'win32') {
// override the .kill method for Windows so that the detached Firefox process closes between specs
// @see https://github.com/cypress-io/cypress/issues/6392
return _createDetachedInstance(browserInstance)
}
return browserInstance
}
+6
View File
@@ -1,7 +1,13 @@
import { FoundBrowser } from '@packages/launcher'
import { EventEmitter } from 'events'
export type Browser = FoundBrowser & {
majorVersion: number
isHeadless: boolean
isHeaded: boolean
}
export type BrowserInstance = EventEmitter & {
kill: () => void
pid: number
}
+3 -3
View File
@@ -205,12 +205,12 @@ export = {
launch: launcher.launch,
writeExtension (browser, isTextTerminal, proxyUrl, socketIoRoute, onScreencastFrame) {
writeExtension (browser, isTextTerminal, proxyUrl, socketIoRoute) {
debug('writing extension')
// debug('writing extension to chrome browser')
// get the string bytes for the final extension file
return extension.setHostAndPath(proxyUrl, socketIoRoute, onScreencastFrame)
return extension.setHostAndPath(proxyUrl, socketIoRoute)
.then((str) => {
const extensionDest = getExtensionDir(browser, isTextTerminal)
const extensionBg = path.join(extensionDest, 'background.js')
@@ -231,7 +231,7 @@ export = {
debug('getBrowsers')
return launcher.detect()
.then((browsers = []) => {
.then((browsers: FoundBrowser[] = []) => {
let majorVersion
debug('found browsers %o', { browsers })
+1 -1
View File
@@ -510,7 +510,7 @@ module.exports = {
// {value: obj.val, from: "plugin"}
setPluginResolvedOn (resolvedObj, obj) {
return _.each(obj, (val, key) => {
if (_.isObject(val) && !_.isArray(val)) {
if (_.isObject(val) && !_.isArray(val) && resolvedObj[key]) {
// recurse setting overrides
// inside of this nested objected
return this.setPluginResolvedOn(resolvedObj[key], val)
+15 -1
View File
@@ -67,7 +67,21 @@ try {
if (process.env.ELECTRON_EXTRA_LAUNCH_ARGS) {
const electronLaunchArguments = process.env.ELECTRON_EXTRA_LAUNCH_ARGS.split(' ')
electronLaunchArguments.forEach(app.commandLine.appendArgument)
electronLaunchArguments.forEach((arg) => {
// arg can be just key --disable-http-cache
// or key value --remote-debugging-port=8315
// https://github.com/cypress-io/cypress/issues/7994
const [key, value] = arg.split('=')
// because this is an environment variable, everything is a string
// thus we don't have to worry about casting
// --foo=false for example will be "--foo", "false"
if (value) {
app.commandLine.appendSwitch(key, value)
} else {
app.commandLine.appendSwitch(key)
}
})
}
} catch (e) {
debug('environment error %s', e.message)
+3 -3
View File
@@ -550,7 +550,7 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) {
return stripIndent`\
The support file is missing or invalid.
Your \`supportFile\` is set to \`${arg1}\`, but either the file is missing or it's invalid. The \`supportFile\` must be a \`.js\` or \`.coffee\` file or, if you're using a preprocessor plugin, it must be supported by that plugin.
Your \`supportFile\` is set to \`${arg1}\`, but either the file is missing or it's invalid. The \`supportFile\` must be a \`.js\`, \`.ts\`, \`.coffee\` file or be supported by your preprocessor plugin (if configured).
Correct your \`${arg2}\`, create the appropriate file, or set \`supportFile\` to \`false\` if a support file is not necessary for your project.
@@ -561,9 +561,9 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) {
msg = stripIndent`\
The plugins file is missing or invalid.
Your \`pluginsFile\` is set to \`${arg1}\`, but either the file is missing, it contains a syntax error, or threw an error when required. The \`pluginsFile\` must be a \`.js\` or \`.coffee\` file.
Your \`pluginsFile\` is set to \`${arg1}\`, but either the file is missing, it contains a syntax error, or threw an error when required. The \`pluginsFile\` must be a \`.js\`, \`.ts\`, or \`.coffee\` file.
Or you might have renamed the extension of your \`pluginsFile\` to \`.ts\`. If that's the case, restart the test runner.
Or you might have renamed the extension of your \`pluginsFile\`. If that's the case, restart the test runner.
Please fix this, or set \`pluginsFile\` to \`false\` if a plugins file is not necessary for your project.`.trim()
+6 -5
View File
@@ -55,7 +55,7 @@
"execa": "1.0.0",
"express": "4.16.4",
"find-process": "1.4.1",
"firefox-profile": "1.3.1",
"firefox-profile": "2.0.0",
"fix-path": "2.1.0",
"fluent-ffmpeg": "2.1.2",
"foxdriver": "0.1.1",
@@ -96,7 +96,7 @@
"pluralize": "8.0.0",
"ramda": "0.24.1",
"randomstring": "1.1.5",
"resolve": "1.13.1",
"resolve": "1.17.0",
"return-deep-diff": "0.4.0",
"sanitize-filename": "1.6.3",
"semver": "6.3.0",
@@ -106,15 +106,16 @@
"squirrelly": "7.9.2",
"strip-ansi": "6.0.0",
"syntax-error": "1.4.0",
"systeminformation": "4.21.1",
"systeminformation": "4.26.9",
"term-size": "2.1.0",
"tough-cookie": "4.0.0",
"trash": "5.2.0",
"tree-kill": "1.2.2",
"ts-node": "8.5.4",
"underscore": "1.9.1",
"underscore.string": "3.3.5",
"url-parse": "1.4.7",
"uuid": "3.3.2",
"uuid": "8.2.0",
"which": "2.0.2",
"widest-line": "3.1.0",
"winston": "2.4.4"
@@ -174,7 +175,7 @@
"mockery": "2.1.0",
"multiparty": "4.2.1",
"nock": "12.0.2",
"proxyquire": "2.1.0",
"proxyquire": "2.1.3",
"react": "16.8.6",
"repl.history": "0.1.4",
"sinon": "5.1.1",
@@ -68,4 +68,12 @@ describe('e2e firefox', function () {
expect(stderr).not.to.include('foxdriver')
},
})
// NOTE: only an issue on windows
// https://github.com/cypress-io/cypress/issues/6392
e2e.it.skip('can run multiple specs', {
browser: 'firefox',
project: Fixtures.projectPath('e2e'),
spec: 'simple_spec.coffee,simple_passing_spec.coffee',
})
})
@@ -13,10 +13,7 @@ describe('lib/browsers/cri-client', function () {
let send: sinon.SinonStub
let criImport: sinon.SinonStub
let onError: sinon.SinonStub
function getClient () {
return criClient.create(DEBUGGER_URL, onError)
}
let getClient: () => ReturnType<typeof create>
beforeEach(function () {
sinon.stub(Bluebird, 'promisify').returnsArg(0)
@@ -29,11 +26,17 @@ describe('lib/browsers/cri-client', function () {
target: DEBUGGER_URL,
local: true,
})
.resolves({ send, _notifier: new EventEmitter() })
.resolves({
send,
close: sinon.stub(),
_notifier: new EventEmitter(),
})
criClient = proxyquire('../lib/browsers/cri-client', {
'chrome-remote-interface': criImport,
})
getClient = () => criClient.create(DEBUGGER_URL, onError)
})
context('.create', function () {
@@ -51,6 +54,37 @@ describe('lib/browsers/cri-client', function () {
client.send('Browser.getVersion', { baz: 'quux' })
expect(send).to.be.calledWith('Browser.getVersion', { baz: 'quux' })
})
it('rejects if cri.send rejects', async function () {
const err = new Error
send.rejects(err)
const client = await getClient()
await expect(client.send('Browser.getVersion', { baz: 'quux' }))
.to.be.rejectedWith(err)
})
context('retries', () => {
([
'WebSocket is not open',
// @see https://github.com/cypress-io/cypress/issues/7180
'WebSocket is already in CLOSING or CLOSED state',
]).forEach((msg) => {
it(`with '${msg}'`, async function () {
const err = new Error(msg)
send.onFirstCall().rejects(err)
send.onSecondCall().resolves()
const client = await getClient()
await client.send('Browser.getVersion', { baz: 'quux' })
expect(send).to.be.calledTwice
})
})
})
})
context('#ensureMinimumProtocolVersion', function () {
@@ -1,13 +1,15 @@
require('../../spec_helper')
import { expect } from 'chai'
import sinon from 'sinon'
import 'chai-as-promised'
import firefoxUtil from '../../../lib/browsers/firefox-util'
import * as firefox from '../../../lib/browsers/firefox'
import { expect } from 'chai'
import { EventEmitter } from 'events'
import Marionette from 'marionette-client'
import Foxdriver from 'foxdriver'
import Marionette from 'marionette-client'
import os from 'os'
import sinon from 'sinon'
import * as firefox from '../../../lib/browsers/firefox'
import firefoxUtil from '../../../lib/browsers/firefox-util'
const mockfs = require('mock-fs')
const FirefoxProfile = require('firefox-profile')
const utils = require('../../../lib/browsers/utils')
@@ -67,6 +69,7 @@ describe('lib/browsers/firefox', () => {
const browser = {
listTabs: sinon.stub().resolves([foxdriverTab]),
request: sinon.stub().withArgs('listTabs').resolves({ tabs: [foxdriverTab] }),
on: sinon.stub(),
}
foxdriver = {
@@ -102,6 +105,11 @@ describe('lib/browsers/firefox', () => {
browser: this.browser,
}
this.browserInstance = {
// should be high enough to not kill any real PIDs
pid: Number.MAX_SAFE_INTEGER,
}
sinon.stub(process, 'pid').value(1111)
protocol.foo = 'bar'
@@ -109,7 +117,6 @@ describe('lib/browsers/firefox', () => {
sinon.stub(plugins, 'has')
sinon.stub(plugins, 'execute')
sinon.stub(utils, 'writeExtension').resolves('/path/to/ext')
this.browserInstance = {}
sinon.stub(utils, 'launch').resolves(this.browserInstance)
sinon.spy(FirefoxProfile.prototype, 'setPreference')
sinon.spy(FirefoxProfile.prototype, 'updatePreferences')
@@ -201,7 +208,7 @@ describe('lib/browsers/firefox', () => {
it('writes extension', function () {
return firefox.open(this.browser, 'http://', this.options).then(() => {
expect(utils.writeExtension).to.be.calledWith(this.options.browser, this.options.isTextTerminal, this.options.proxyUrl, this.options.socketIoRoute, this.options.onScreencastFrame)
expect(utils.writeExtension).to.be.calledWith(this.options.browser, this.options.isTextTerminal, this.options.proxyUrl, this.options.socketIoRoute)
})
})
@@ -322,6 +329,29 @@ describe('lib/browsers/firefox', () => {
expect(wrapperErr.message).to.include(err.message)
})
})
context('returns BrowserInstance', function () {
it('from browsers.launch', async function () {
const instance = await firefox.open(this.browser, 'http://', this.options)
expect(instance).to.eq(this.browserInstance)
})
// @see https://github.com/cypress-io/cypress/issues/6392
it('detached on Windows', async function () {
sinon.stub(os, 'platform').returns('win32')
const instance = await firefox.open(this.browser, 'http://', this.options)
expect(instance).to.not.eq(this.browserInstance)
expect(instance.pid).to.eq(this.browserInstance.pid)
await new Promise((resolve) => {
// ensure events are wired as expected
instance.on('exit', resolve)
instance.kill()
})
})
})
})
context('firefox-util', () => {
+26
View File
@@ -1318,6 +1318,32 @@ describe('lib/config', () => {
},
})
})
// https://github.com/cypress-io/cypress/issues/7959
it('resolves a single object', () => {
const cfg = {
}
const obj = {
foo: {
bar: {
baz: 42,
},
},
}
config.setPluginResolvedOn(cfg, obj)
expect(cfg).to.deep.eq({
foo: {
from: 'plugin',
value: {
bar: {
baz: 42,
},
},
},
})
})
})
context('_.defaultsDeep', () => {
+35 -9
View File
@@ -47,23 +47,49 @@ describe('lib/environment', () => {
context('parses ELECTRON_EXTRA_LAUNCH_ARGS', () => {
let restore = null
beforeEach(() => {
return restore = mockedEnv({
it('sets launch args', () => {
restore = mockedEnv({
ELECTRON_EXTRA_LAUNCH_ARGS: '--foo --bar=baz --quux=true',
})
sinon.stub(app.commandLine, 'appendSwitch')
require(`${root}lib/environment`)
expect(app.commandLine.appendSwitch).to.have.been.calledWith('--foo')
expect(app.commandLine.appendSwitch).to.have.been.calledWith('--bar', 'baz')
expect(app.commandLine.appendSwitch).to.have.been.calledWith('--quux', 'true')
})
it('sets launch args', () => {
sinon.stub(app.commandLine, 'appendArgument')
require(`${root}lib/environment`)
expect(app.commandLine.appendArgument).to.have.been.calledWith('--foo')
expect(app.commandLine.appendArgument).to.have.been.calledWith('--bar=baz')
it('sets launch args with zero', () => {
restore = mockedEnv({
ELECTRON_EXTRA_LAUNCH_ARGS: '--foo --bar=baz --quux=0',
})
expect(app.commandLine.appendArgument).to.have.been.calledWith('--quux=true')
sinon.stub(app.commandLine, 'appendSwitch')
require(`${root}lib/environment`)
expect(app.commandLine.appendSwitch).to.have.been.calledWith('--foo')
expect(app.commandLine.appendSwitch).to.have.been.calledWith('--bar', 'baz')
expect(app.commandLine.appendSwitch).to.have.been.calledWith('--quux', '0')
})
it('sets launch args with false', () => {
restore = mockedEnv({
ELECTRON_EXTRA_LAUNCH_ARGS: '--foo --bar=baz --quux=false',
})
sinon.stub(app.commandLine, 'appendSwitch')
require(`${root}lib/environment`)
expect(app.commandLine.appendSwitch).to.have.been.calledWith('--foo')
expect(app.commandLine.appendSwitch).to.have.been.calledWith('--bar', 'baz')
expect(app.commandLine.appendSwitch).to.have.been.calledWith('--quux', 'false')
})
return afterEach(() => {
return restore()
if (restore) {
return restore()
}
})
})
+31 -49
View File
@@ -9397,10 +9397,10 @@ cyclist@^1.0.1:
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
cypress-example-kitchensink@1.11.1:
version "1.11.1"
resolved "https://registry.yarnpkg.com/cypress-example-kitchensink/-/cypress-example-kitchensink-1.11.1.tgz#2320d5c1010edcaac0dcc8a2ebaa2158e92aa42c"
integrity sha512-6Y85bIGOwxVGeMEio2bTn783MlIku3TYQSdqJ3LZUNp6lTkKwRZWk1EluMLBTo5BYVP7w5hHO3K785/1LBC8eA==
cypress-example-kitchensink@1.11.2:
version "1.11.2"
resolved "https://registry.yarnpkg.com/cypress-example-kitchensink/-/cypress-example-kitchensink-1.11.2.tgz#d5e29ab82c50db000c923b97dfddbc0303035a12"
integrity sha512-n2mqfFxXsrXaMDeL51q4BGbbfznKYGXU+LSsXHDWH40zNGSYRikSXHi+Vzw+r8K77NTlFQwfwcjWyzWPpRV4Hw==
dependencies:
npm-run-all "^4.1.2"
serve "11.3.0"
@@ -12102,10 +12102,10 @@ fira@cypress-io/fira#fb63362742eea8cdce0d90825ab9264d77719e3d:
version "1.0.0"
resolved "https://codeload.github.com/cypress-io/fira/tar.gz/fb63362742eea8cdce0d90825ab9264d77719e3d"
firefox-profile@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/firefox-profile/-/firefox-profile-1.3.1.tgz#1418d36b79c191984d6117bd772d3cbf9675a92b"
integrity sha512-8q7DnwVIXvuJuBm1shr5ivRh0Ih2ytWwOIMwHInDSlVyrjQVXy7Ik0frItDdWb/P5CIpQFcMk9fPsUwNqi2lyQ==
firefox-profile@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/firefox-profile/-/firefox-profile-2.0.0.tgz#9de0f1918e15f89b20827a7604384f1d99fb2300"
integrity sha512-BPfcUISOV6+UwF6uqo5QS8iuFL6XZvHCm+1iuynIJ7fe1zea69Is77/n/098fp0a9sZ94lvT8rpYB15S/riSaA==
dependencies:
adm-zip "~0.4.x"
archiver "~2.1.0"
@@ -13870,16 +13870,7 @@ http-proxy-agent@^2.1.0:
agent-base "4"
debug "3.1.0"
http-proxy@^1.17.0:
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
dependencies:
eventemitter3 "^4.0.0"
follow-redirects "^1.0.0"
requires-port "^1.0.0"
http-proxy@cypress-io/node-http-proxy#9322b4b69b34f13a6f3874e660a35df3305179c6:
http-proxy@^1.17.0, http-proxy@cypress-io/node-http-proxy#9322b4b69b34f13a6f3874e660a35df3305179c6:
version "1.18.0"
resolved "https://codeload.github.com/cypress-io/node-http-proxy/tar.gz/9322b4b69b34f13a6f3874e660a35df3305179c6"
dependencies:
@@ -17980,7 +17971,7 @@ module-lookup-amd@^6.1.0:
requirejs "^2.3.5"
requirejs-config-file "^3.1.1"
module-not-found-error@^1.0.0:
module-not-found-error@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0"
integrity sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=
@@ -19612,7 +19603,7 @@ path-key@^3.0.0, path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.5, path-parse@^1.0.6:
path-parse@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
@@ -20308,14 +20299,14 @@ proxy-from-env@1.1.0, proxy-from-env@^1.0.0:
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
proxyquire@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.0.tgz#c2263a38bf0725f2ae950facc130e27510edce8d"
integrity sha512-kptdFArCfGRtQFv3Qwjr10lwbEV0TBJYvfqzhwucyfEXqVgmnAkyEw/S3FYzR5HI9i5QOq4rcqQjZ6AlknlCDQ==
proxyquire@2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.3.tgz#2049a7eefa10a9a953346a18e54aab2b4268df39"
integrity sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==
dependencies:
fill-keys "^1.0.2"
module-not-found-error "^1.0.0"
resolve "~1.8.1"
module-not-found-error "^1.0.1"
resolve "^1.11.1"
prr@~1.0.1:
version "1.0.1"
@@ -21761,10 +21752,10 @@ resolve@1.1.7, resolve@1.1.x:
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
resolve@1.13.1:
version "1.13.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16"
integrity sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==
resolve@1.17.0, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1:
version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
dependencies:
path-parse "^1.0.6"
@@ -21773,20 +21764,6 @@ resolve@^0.6.3:
resolved "https://registry.yarnpkg.com/resolve/-/resolve-0.6.3.tgz#dd957982e7e736debdf53b58a4dd91754575dd46"
integrity sha1-3ZV5gufnNt699TtYpN2RdUV13UY=
resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1:
version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
dependencies:
path-parse "^1.0.6"
resolve@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==
dependencies:
path-parse "^1.0.5"
responselike@1.0.2, responselike@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
@@ -23788,10 +23765,10 @@ syntax-error@1.4.0, syntax-error@^1.1.1:
dependencies:
acorn-node "^1.2.0"
systeminformation@4.21.1:
version "4.21.1"
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.21.1.tgz#8394684676c9ca1da59d9f92458b166d9cd52cd6"
integrity sha512-IQMy+ieSThY+MfLZaCdQsGCteMah4nhsDQcnT9DhocoJnhMKVUqDY025j1i+MSm7qdUCMXS5oV7dvttr+pSodw==
systeminformation@4.26.9:
version "4.26.9"
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.26.9.tgz#ecc725a162c0c7d8d48226f97637a5f590042ffd"
integrity sha512-69MTIX9j//wnteJzQWuGL6FiMxlfS3wTWyhROI6pNUaQALYqJb3W9VtLVcEcKFOjO1vrGRgilJVFzJeRRi//Pg==
table@^5.2.3:
version "5.4.6"
@@ -24318,7 +24295,7 @@ traverse-chain@~0.1.0:
resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1"
integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE=
tree-kill@^1.1.0:
tree-kill@1.2.2, tree-kill@^1.1.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
@@ -25105,6 +25082,11 @@ uuid@3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
uuid@8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.2.0.tgz#cb10dd6b118e2dada7d0cd9730ba7417c93d920e"
integrity sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==
uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2, uuid@^3.3.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"