mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-22 15:12:27 -05:00
Merge branch 'develop' into v5.0-release
# Conflicts: # circle.yml # packages/server/test/unit/browsers/firefox_spec.ts
This commit is contained in:
+29
@@ -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
@@ -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')
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Vendored
+25
@@ -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
|
||||
|
||||
Vendored
+64
-6
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user