mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-05 14:30:32 -05:00
Merge branch 'develop' into v4.0-release
This commit is contained in:
+25
@@ -55,9 +55,30 @@ executors:
|
||||
PLATFORM: mac
|
||||
|
||||
commands:
|
||||
install-latest-chrome:
|
||||
description: Install latest Google Chrome (stable)
|
||||
parameters:
|
||||
browser:
|
||||
default: "electron"
|
||||
description: browser shortname to target
|
||||
type: string
|
||||
steps:
|
||||
- run:
|
||||
name: Install latest Google Chrome (stable)
|
||||
command: |
|
||||
if [ << parameters.browser >> == "chrome" ]; then
|
||||
echo "**** Running Chrome tests. Installing latest stable version of Google Chrome. ****"
|
||||
apt-get update
|
||||
apt-get install google-chrome-stable -y
|
||||
echo "**** Location of Google Chrome Installation: "`which google-chrome`" ****"
|
||||
echo "**** Google Chrome Version: "`google-chrome --version`" ****"
|
||||
else
|
||||
echo "**** Not updating Chrome. Running tests in '<< parameters.browser >>' ****"
|
||||
fi
|
||||
run-e2e-tests:
|
||||
parameters:
|
||||
browser:
|
||||
default: "electron"
|
||||
description: browser shortname to target
|
||||
type: string
|
||||
chunk:
|
||||
@@ -66,6 +87,8 @@ commands:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- install-latest-chrome:
|
||||
browser: << parameters.browser >>
|
||||
- run:
|
||||
command: npm run test-e2e -- --chunk << parameters.chunk >> --browser << parameters.browser >>
|
||||
working_directory: packages/server
|
||||
@@ -493,6 +516,8 @@ jobs:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- install-latest-chrome:
|
||||
browser: chrome
|
||||
- run:
|
||||
command: npm start
|
||||
background: true
|
||||
|
||||
@@ -47,9 +47,7 @@
|
||||
font-size: 13px;
|
||||
color: #444;
|
||||
padding: 1px 20px 0 20px;
|
||||
line-height: 35px;
|
||||
|
||||
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
> a {
|
||||
@@ -164,14 +162,24 @@
|
||||
}
|
||||
|
||||
.browsers-list {
|
||||
margin: 5px;
|
||||
border: 1px solid #c7c7c7;
|
||||
border-radius: 4px;
|
||||
|
||||
li {
|
||||
padding: 9px 15px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dropdown-chosen img {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
&>a.dropdown-chosen {
|
||||
border-radius: 4px;
|
||||
line-height: 28px !important;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.browser-icon {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
exports['invalid env error'] = `
|
||||
Cypress encountered an error while parsing the argument env
|
||||
|
||||
You passed: nonono
|
||||
|
||||
The error was: Cannot read property 'split' of undefined
|
||||
`
|
||||
|
||||
exports['invalid reporter options error'] = `
|
||||
Cypress encountered an error while parsing the argument reporterOptions
|
||||
|
||||
You passed: abc
|
||||
|
||||
The error was: Cannot read property 'split' of undefined
|
||||
`
|
||||
|
||||
exports['invalid config error'] = `
|
||||
Cypress encountered an error while parsing the argument config
|
||||
|
||||
You passed: xyz
|
||||
|
||||
The error was: Cannot read property 'split' of undefined
|
||||
`
|
||||
@@ -264,3 +264,27 @@ These flags can only be used when recording to the Cypress Dashboard service.
|
||||
|
||||
https://on.cypress.io/record-params-without-recording
|
||||
`
|
||||
|
||||
exports['could not parse config error'] = `
|
||||
Cypress encountered an error while parsing the argument config
|
||||
|
||||
You passed: xyz
|
||||
|
||||
The error was: Cannot read property 'split' of undefined
|
||||
`
|
||||
|
||||
exports['could not parse env error'] = `
|
||||
Cypress encountered an error while parsing the argument env
|
||||
|
||||
You passed: a123
|
||||
|
||||
The error was: Cannot read property 'split' of undefined
|
||||
`
|
||||
|
||||
exports['could not parse reporter options error'] = `
|
||||
Cypress encountered an error while parsing the argument reporterOptions
|
||||
|
||||
You passed: nonono
|
||||
|
||||
The error was: Cannot read property 'split' of undefined
|
||||
`
|
||||
|
||||
@@ -39,6 +39,8 @@ const exitErr = (err) => {
|
||||
|
||||
return require('./errors').logException(err)
|
||||
.then(() => {
|
||||
debug('calling exit 1')
|
||||
|
||||
return exit(1)
|
||||
})
|
||||
}
|
||||
@@ -109,7 +111,16 @@ module.exports = {
|
||||
// for https://github.com/cypress-io/cypress/issues/5466
|
||||
argv = R.without('--', argv)
|
||||
|
||||
const options = argsUtils.toObject(argv)
|
||||
let options
|
||||
|
||||
try {
|
||||
options = argsUtils.toObject(argv)
|
||||
} catch (argumentsError) {
|
||||
debug('could not parse CLI arguments: %o', argv)
|
||||
|
||||
// note - this is promise-returned call
|
||||
return exitErr(argumentsError)
|
||||
}
|
||||
|
||||
debug('from argv %o got options %o', argv, options)
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ trimMultipleNewLines = (str) ->
|
||||
isCypressErr = (err) ->
|
||||
Boolean(err.isCypressErr)
|
||||
|
||||
getMsgByType = (type, arg1 = {}, arg2) ->
|
||||
getMsgByType = (type, arg1 = {}, arg2, arg3) ->
|
||||
switch type
|
||||
when "CANNOT_TRASH_ASSETS"
|
||||
"""
|
||||
@@ -525,7 +525,6 @@ getMsgByType = (type, arg1 = {}, arg2) ->
|
||||
|
||||
#{chalk.blue(arg2)}
|
||||
"""
|
||||
|
||||
when "RENDERER_CRASHED"
|
||||
"""
|
||||
We detected that the Chromium Renderer process just crashed.
|
||||
@@ -881,9 +880,17 @@ getMsgByType = (type, arg1 = {}, arg2) ->
|
||||
"""
|
||||
Failed to connect to Chrome, retrying in 1 second (attempt #{chalk.yellow(arg1)}/32)
|
||||
"""
|
||||
when "COULD_NOT_PARSE_ARGUMENTS"
|
||||
"""
|
||||
Cypress encountered an error while parsing the argument #{chalk.gray(arg1)}
|
||||
|
||||
get = (type, arg1, arg2) ->
|
||||
msg = getMsgByType(type, arg1, arg2)
|
||||
You passed: #{arg2}
|
||||
|
||||
The error was: #{arg3}
|
||||
"""
|
||||
|
||||
get = (type, arg1, arg2, arg3) ->
|
||||
msg = getMsgByType(type, arg1, arg2, arg3)
|
||||
|
||||
if _.isObject(msg)
|
||||
details = msg.details
|
||||
@@ -904,8 +911,8 @@ warning = (type, arg1, arg2) ->
|
||||
|
||||
return null
|
||||
|
||||
throwErr = (type, arg1, arg2) ->
|
||||
throw get(type, arg1, arg2)
|
||||
throwErr = (type, arg1, arg2, arg3) ->
|
||||
throw get(type, arg1, arg2, arg3)
|
||||
|
||||
clone = (err, options = {}) ->
|
||||
_.defaults options, {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
const _ = require('lodash')
|
||||
const la = require('lazy-ass')
|
||||
const is = require('check-more-types')
|
||||
const path = require('path')
|
||||
const debug = require('debug')('cypress:server:args')
|
||||
const minimist = require('minimist')
|
||||
const coerceUtil = require('./coerce')
|
||||
const configUtil = require('../config')
|
||||
const proxyUtil = require('./proxy')
|
||||
const errors = require('../errors')
|
||||
|
||||
const nestedObjectsInCurlyBracesRe = /\{(.+?)\}/g
|
||||
const nestedArraysInSquareBracketsRe = /\[(.+?)\]/g
|
||||
@@ -107,32 +110,41 @@ const JSONOrCoerce = (str) => {
|
||||
return coerceUtil(str)
|
||||
}
|
||||
|
||||
const sanitizeAndConvertNestedArgs = (str) => {
|
||||
const sanitizeAndConvertNestedArgs = (str, argname) => {
|
||||
la(is.unemptyString(argname), 'missing config argname to be parsed')
|
||||
|
||||
try {
|
||||
// if this is valid JSON then just
|
||||
// parse it and call it a day
|
||||
const parsed = tryJSONParse(str)
|
||||
const parsed = tryJSONParse(str)
|
||||
|
||||
if (parsed) {
|
||||
return parsed
|
||||
if (parsed) {
|
||||
return parsed
|
||||
}
|
||||
|
||||
// invalid JSON, so assume mixed usage
|
||||
// first find foo={a:b,b:c} and bar=[1,2,3]
|
||||
// syntax and turn those into
|
||||
// foo: a:b|b:c
|
||||
// bar: 1|2|3
|
||||
|
||||
return _
|
||||
.chain(str)
|
||||
.replace(nestedObjectsInCurlyBracesRe, commasToPipes)
|
||||
.replace(nestedArraysInSquareBracketsRe, commasToPipes)
|
||||
.split(',')
|
||||
.map((pair) => {
|
||||
return pair.split(everythingAfterFirstEqualRe)
|
||||
})
|
||||
.fromPairs()
|
||||
.mapValues(JSONOrCoerce)
|
||||
.value()
|
||||
} catch (err) {
|
||||
debug('could not pass config %s value %s', argname, str)
|
||||
debug('error %o', err)
|
||||
|
||||
return errors.throw('COULD_NOT_PARSE_ARGUMENTS', argname, str, err.message)
|
||||
}
|
||||
|
||||
// invalid JSON, so assume mixed usage
|
||||
// first find foo={a:b,b:c} and bar=[1,2,3]
|
||||
// syntax and turn those into
|
||||
// foo: a:b|b:c
|
||||
// bar: 1|2|3
|
||||
|
||||
return _
|
||||
.chain(str)
|
||||
.replace(nestedObjectsInCurlyBracesRe, commasToPipes)
|
||||
.replace(nestedArraysInSquareBracketsRe, commasToPipes)
|
||||
.split(',')
|
||||
.map((pair) => {
|
||||
return pair.split(everythingAfterFirstEqualRe)
|
||||
})
|
||||
.fromPairs()
|
||||
.mapValues(JSONOrCoerce)
|
||||
.value()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -219,7 +231,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
if (env) {
|
||||
options.env = sanitizeAndConvertNestedArgs(env)
|
||||
options.env = sanitizeAndConvertNestedArgs(env, 'env')
|
||||
}
|
||||
|
||||
const proxySource = proxyUtil.loadSystemProxySettings()
|
||||
@@ -234,13 +246,13 @@ module.exports = {
|
||||
}
|
||||
|
||||
if (reporterOptions) {
|
||||
options.reporterOptions = sanitizeAndConvertNestedArgs(reporterOptions)
|
||||
options.reporterOptions = sanitizeAndConvertNestedArgs(reporterOptions, 'reporterOptions')
|
||||
}
|
||||
|
||||
if (config) {
|
||||
// convert config to an object
|
||||
// annd store the config
|
||||
options.config = sanitizeAndConvertNestedArgs(config)
|
||||
// and store the config
|
||||
options.config = sanitizeAndConvertNestedArgs(config, 'config')
|
||||
}
|
||||
|
||||
// get a list of the available config keys
|
||||
|
||||
@@ -143,14 +143,22 @@ describe('lib/cypress', () => {
|
||||
expect(process.exit).to.be.calledWith(code)
|
||||
}
|
||||
|
||||
this.expectExitWithErr = (type, msg) => {
|
||||
expect(errors.log).to.be.calledWithMatch({ type })
|
||||
expect(process.exit).to.be.calledWith(1)
|
||||
if (msg) {
|
||||
const err = errors.log.getCall(0).args[0]
|
||||
// returns error object
|
||||
this.expectExitWithErr = (type, msg1, msg2) => {
|
||||
expect(errors.log, 'error was logged').to.be.calledWithMatch({ type })
|
||||
expect(process.exit, 'process.exit was called').to.be.calledWith(1)
|
||||
|
||||
expect(err.message).to.include(msg)
|
||||
const err = errors.log.getCall(0).args[0]
|
||||
|
||||
if (msg1) {
|
||||
expect(err.message, 'error text').to.include(msg1)
|
||||
}
|
||||
|
||||
if (msg2) {
|
||||
expect(err.message, 'second error text').to.include(msg2)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
@@ -193,6 +201,35 @@ describe('lib/cypress', () => {
|
||||
})
|
||||
})
|
||||
|
||||
context('error handling', function () {
|
||||
it('exits if config cannot be parsed', function () {
|
||||
return cypress.start(['--config', 'xyz'])
|
||||
.then(() => {
|
||||
const err = this.expectExitWithErr('COULD_NOT_PARSE_ARGUMENTS')
|
||||
|
||||
snapshot('could not parse config error', stripAnsi(err.message))
|
||||
})
|
||||
})
|
||||
|
||||
it('exits if env cannot be parsed', function () {
|
||||
return cypress.start(['--env', 'a123'])
|
||||
.then(() => {
|
||||
const err = this.expectExitWithErr('COULD_NOT_PARSE_ARGUMENTS')
|
||||
|
||||
snapshot('could not parse env error', stripAnsi(err.message))
|
||||
})
|
||||
})
|
||||
|
||||
it('exits if reporter options cannot be parsed', function () {
|
||||
return cypress.start(['--reporterOptions', 'nonono'])
|
||||
.then(() => {
|
||||
const err = this.expectExitWithErr('COULD_NOT_PARSE_ARGUMENTS')
|
||||
|
||||
snapshot('could not parse reporter options error', stripAnsi(err.message))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('--get-key', () => {
|
||||
it('writes out key and exits on success', function () {
|
||||
return Promise.all([
|
||||
|
||||
@@ -3,7 +3,9 @@ require("../spec_helper")
|
||||
_ = require("lodash")
|
||||
path = require("path")
|
||||
os = require("os")
|
||||
argsUtil = require("#{root}lib/util/args")
|
||||
snapshot = require("snap-shot-it")
|
||||
stripAnsi = require("strip-ansi")
|
||||
argsUtil = require("#{root}lib/util/args")
|
||||
proxyUtil = require("#{root}lib/util/proxy")
|
||||
getWindowsProxyUtil = require("#{root}lib/util/get-windows-proxy")
|
||||
|
||||
@@ -74,6 +76,17 @@ describe "lib/util/args", ->
|
||||
bar: "qux="
|
||||
})
|
||||
|
||||
it "throws if env string cannot be parsed", ->
|
||||
expect () =>
|
||||
@setup("--env", "nonono")
|
||||
.to.throw
|
||||
|
||||
# now look at the error
|
||||
try
|
||||
@setup("--env", "nonono")
|
||||
catch err
|
||||
snapshot("invalid env error", stripAnsi(err.message))
|
||||
|
||||
context "--reporterOptions", ->
|
||||
it "converts to object literal", ->
|
||||
reporterOpts = {
|
||||
@@ -112,6 +125,17 @@ describe "lib/util/args", ->
|
||||
}
|
||||
})
|
||||
|
||||
it "throws if reporter string cannot be parsed", ->
|
||||
expect () =>
|
||||
@setup("--reporterOptions", "abc")
|
||||
.to.throw
|
||||
|
||||
# now look at the error
|
||||
try
|
||||
@setup("--reporterOptions", "abc")
|
||||
catch err
|
||||
snapshot("invalid reporter options error", stripAnsi(err.message))
|
||||
|
||||
context "--config", ->
|
||||
it "converts to object literal", ->
|
||||
options = @setup("--config", "pageLoadTimeout=10000,waitForAnimations=false")
|
||||
@@ -172,6 +196,17 @@ describe "lib/util/args", ->
|
||||
options = @setup("--port", 2222)
|
||||
expect(options.config.port).to.eq(2222)
|
||||
|
||||
it "throws if config string cannot be parsed", ->
|
||||
expect () =>
|
||||
@setup("--config", "xyz")
|
||||
.to.throw
|
||||
|
||||
# now look at the error
|
||||
try
|
||||
@setup("--config", "xyz")
|
||||
catch err
|
||||
snapshot("invalid config error", stripAnsi(err.message))
|
||||
|
||||
context ".toArray", ->
|
||||
beforeEach ->
|
||||
@obj = {config: {foo: "bar"}, project: "foo/bar"}
|
||||
|
||||
Reference in New Issue
Block a user