mirror of
https://github.com/cypress-io/cypress.git
synced 2026-03-13 12:59:07 -05:00
Issue 1042 (#1057)
* server: remove unused file * server: WIP, start implementing accepting plugin returning promise and yielding config overrides [skip ci] * server: cleanup the env + environentVariables disaster. simplify and only use 'env' * server, desktop-gum: rename 'env' to 'cypressEnv' to avoid conflicts, fix failing tests * server: modify plugins error content to be clearer * runner, driver: more environmentVariables -> env cleanup * fixes #509 return the complete configuration object to Cypress.config() * fixes #1042 enable plugins to return a promise and modify config * desktop-gui: add 'plugin' override to configuration display * server: bug fix when plugin cause a project not to open * desktop-gui: fix for failing e2e test * server: make errors clearer when plugins crash * server: fix bug with PLUGINS_FUNCTION_ERROR not sending right arguments - fix failing tests - improve plugin error content * server: fix failing snapshots, preprocessor is invoked for support + spec files * server: fix tests, don't watch the support file initially - this was causing a problem where unhandled preprocessor errors were causing the entire process to hang. - this was happening because we weren’t properly running support files through a custom preprocessor initially * fixes failing tests
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
"plugin:cypress-dev/general"
|
||||
],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
"clientUrlDisplay": "http://localhost:2020",
|
||||
"commandTimeout": 4000,
|
||||
"cypressHostUrl": "http://localhost:2020",
|
||||
"env": "development",
|
||||
"environmentVariables": {
|
||||
|
||||
"cypressEnv": "development",
|
||||
"env": {
|
||||
|
||||
},
|
||||
"execTimeout": 60000,
|
||||
"fileServerFolder": "/Users/jennifer/Dev/Projects/cypress-example-kitchensink",
|
||||
@@ -40,7 +40,7 @@
|
||||
"isHeadless": false,
|
||||
"isNewProject": false,
|
||||
"javascripts": [
|
||||
|
||||
|
||||
],
|
||||
"morgan": true,
|
||||
"namespace": "__cypress",
|
||||
@@ -108,7 +108,7 @@
|
||||
"from": "default",
|
||||
"value": 4000
|
||||
},
|
||||
"environmentVariables": {
|
||||
"env": {
|
||||
"fileServerFolder": {
|
||||
"from": "default",
|
||||
"value": ""
|
||||
|
||||
@@ -65,7 +65,7 @@ describe "Settings", ->
|
||||
cy.contains("Your project's configuration is displayed")
|
||||
|
||||
it "displays legend in table", ->
|
||||
cy.get("table>tbody>tr").should("have.length", 5)
|
||||
cy.get("table>tbody>tr").should("have.length", 6)
|
||||
|
||||
it "wraps config line in proper classes", ->
|
||||
cy
|
||||
|
||||
@@ -20,7 +20,7 @@ class App extends Component {
|
||||
appApi.listenForMenuClicks()
|
||||
|
||||
ipc.getOptions().then((options = {}) => {
|
||||
appStore.set(_.pick(options, 'env', 'os', 'projectPath', 'version'))
|
||||
appStore.set(_.pick(options, 'cypressEnv', 'os', 'projectPath', 'version'))
|
||||
viewStore.showApp()
|
||||
})
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { action, computed, observable } from 'mobx'
|
||||
import localData from '../lib/local-data'
|
||||
|
||||
class AppStore {
|
||||
@observable env
|
||||
@observable cypressEnv
|
||||
@observable os
|
||||
@observable projectPath = null
|
||||
@observable newVersion
|
||||
@@ -15,7 +15,7 @@ class AppStore {
|
||||
}
|
||||
|
||||
@computed get isDev () {
|
||||
return this.env === 'development'
|
||||
return this.cypressEnv === 'development'
|
||||
}
|
||||
|
||||
@computed get isGlobalMode () {
|
||||
@@ -27,7 +27,7 @@ class AppStore {
|
||||
}
|
||||
|
||||
@action set (props) {
|
||||
if (props.env != null) this.env = props.env
|
||||
if (props.cypressEnv != null) this.cypressEnv = props.cypressEnv
|
||||
if (props.os != null) this.os = props.os
|
||||
if (props.projectPath != null) this.projectPath = props.projectPath
|
||||
if (props.version != null) this.version = this.newVersion = props.version
|
||||
|
||||
@@ -102,6 +102,10 @@ const Configuration = observer(({ project }) => (
|
||||
<td><span className='cli'>CLI</span></td>
|
||||
<td>set from CLI arguments</td>
|
||||
</tr>
|
||||
<tr className='config-keys'>
|
||||
<td><span className='plugin'>plugin</span></td>
|
||||
<td>set from plugin file</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<pre className='config-vars'>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
padding: 10px;
|
||||
font-family: $font-mono;
|
||||
|
||||
.envFile:hover, .env:hover, .config:hover, .cli:hover, .default:hover {
|
||||
.envFile:hover, .env:hover, .config:hover, .cli:hover, .plugin:hover, .default:hover {
|
||||
border-bottom: 1px dotted #777;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.envFile, .env, .config, .cli, .default {
|
||||
.envFile, .env, .config, .cli, .plugin, .default {
|
||||
font-family: $font-mono;
|
||||
|
||||
padding: 2px;
|
||||
@@ -109,6 +109,11 @@
|
||||
color: #A21313;
|
||||
}
|
||||
|
||||
.plugin {
|
||||
background-color: #f0e7fc;
|
||||
color: #134aa2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.settings-wrapper {
|
||||
|
||||
@@ -119,13 +119,13 @@ class $Cypress
|
||||
longStackTraces: config.isInteractive
|
||||
})
|
||||
|
||||
{environmentVariables, remote} = config
|
||||
{env, remote} = config
|
||||
|
||||
config = _.omit(config, "environmentVariables", "remote")
|
||||
config = _.omit(config, "env", "remote", "resolved", "scaffoldedFiles", "javascripts", "state")
|
||||
|
||||
@state = $SetterGetter.create({})
|
||||
@config = $SetterGetter.create(config)
|
||||
@env = $SetterGetter.create(environmentVariables)
|
||||
@env = $SetterGetter.create(env)
|
||||
|
||||
@Cookies = $Cookies.create(config.namespace, d)
|
||||
|
||||
|
||||
@@ -204,9 +204,6 @@ module.exports = {
|
||||
invalid_argument: "#{cmd('each')} must be passed a callback function."
|
||||
non_array: "#{cmd('each')} can only operate on an array like subject. Your subject was: '{{subject}}'"
|
||||
|
||||
env:
|
||||
variables_missing: "Cypress.environmentVariables is not defined. Open an issue if you see this message."
|
||||
|
||||
exec:
|
||||
failed: """#{cmd('exec', '\'{{cmd}}\'')} failed with the following error:
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ describe "src/cypress", ->
|
||||
describe "#env", ->
|
||||
beforeEach ->
|
||||
@Cypress.setConfig({
|
||||
environmentVariables: {foo: "bar"}
|
||||
env: {foo: "bar"}
|
||||
})
|
||||
|
||||
it "acts as getter", ->
|
||||
@@ -108,9 +108,9 @@ describe "src/cypress", ->
|
||||
it "instantiates Cookies", ->
|
||||
expect(@Cypress.Cookies).to.be.an("object")
|
||||
|
||||
it "passes config.environmentVariables", ->
|
||||
it "passes config.env", ->
|
||||
@Cypress.setConfig({
|
||||
environmentVariables: {foo: "bar"}
|
||||
env: {foo: "bar"}
|
||||
})
|
||||
|
||||
expect(@Cypress.env()).to.deep.eq({foo: "bar"})
|
||||
|
||||
@@ -121,7 +121,7 @@ App.propTypes = {
|
||||
majorVersion: PropTypes.string.isRequired,
|
||||
version: PropTypes.string.isRequired,
|
||||
})).isRequired,
|
||||
env: PropTypes.string.isRequired,
|
||||
cypressEnv: PropTypes.string.isRequired,
|
||||
integrationFolder: PropTypes.string.isRequired,
|
||||
numTestsKeptInMemory: PropTypes.number.isRequired,
|
||||
projectName: PropTypes.string.isRequired,
|
||||
|
||||
@@ -13,7 +13,7 @@ import App from './app'
|
||||
const createProps = () => ({
|
||||
config: {
|
||||
browsers: [],
|
||||
env: 'tst',
|
||||
cypressEnv: 'tst',
|
||||
integrationFolder: '',
|
||||
numTestsKeptInMemory: 1,
|
||||
projectName: '',
|
||||
|
||||
@@ -14,7 +14,7 @@ import Container, { automationElementId } from './container'
|
||||
const createProps = () => ({
|
||||
config: {
|
||||
browsers: [],
|
||||
env: 'test',
|
||||
cypressEnv: 'test',
|
||||
integrationFolder: '',
|
||||
numTestsKeptInMemory: 1,
|
||||
projectName: '',
|
||||
|
||||
@@ -62,7 +62,7 @@ const eventManager = {
|
||||
})
|
||||
|
||||
_.each(socketRerunEvents, (event) => {
|
||||
channel.on(event, this._reRun.bind(this))
|
||||
channel.on(event, this._reRun.bind(this))
|
||||
})
|
||||
|
||||
reporterBus.on('runner:console:error', (testId) => {
|
||||
@@ -165,31 +165,7 @@ const eventManager = {
|
||||
},
|
||||
|
||||
setup (config, specPath) {
|
||||
Cypress = $Cypress.create(
|
||||
_.pick(config,
|
||||
'isTextTerminal',
|
||||
'numTestsKeptInMemory',
|
||||
'waitForAnimations',
|
||||
'animationDistanceThreshold',
|
||||
'defaultCommandTimeout',
|
||||
'pageLoadTimeout',
|
||||
'requestTimeout',
|
||||
'responseTimeout',
|
||||
'environmentVariables',
|
||||
'xhrUrl',
|
||||
'baseUrl',
|
||||
'viewportWidth',
|
||||
'viewportHeight',
|
||||
'execTimeout',
|
||||
'screenshotOnHeadlessFailure',
|
||||
'namespace',
|
||||
'remote',
|
||||
'version',
|
||||
'fixturesFolder',
|
||||
'platform',
|
||||
'arch'
|
||||
)
|
||||
)
|
||||
Cypress = $Cypress.create(config)
|
||||
|
||||
// expose Cypress globally
|
||||
window.Cypress = Cypress
|
||||
|
||||
@@ -3,7 +3,7 @@ Started video recording: /foo/bar/.projects/plugins-async-error/cypress/videos/a
|
||||
|
||||
(Tests Starting)
|
||||
|
||||
Error: The following error was thrown by a plugin. We've stopped running your tests because this likely interrupts behavior critical to them.
|
||||
Error: The following error was thrown by a plugin. We've stopped running your tests because a plugin crashed.
|
||||
|
||||
Error: Async error from plugins file
|
||||
at stack trace line
|
||||
@@ -50,15 +50,16 @@ Started video recording: /foo/bar/.projects/working-preprocessor/cypress/videos/
|
||||
(Tests Starting)
|
||||
|
||||
|
||||
✓ is another spec
|
||||
✓ is another spec
|
||||
|
||||
1 passing
|
||||
2 passing
|
||||
|
||||
|
||||
(Tests Finished)
|
||||
|
||||
- Tests: 1
|
||||
- Passes: 1
|
||||
- Tests: 2
|
||||
- Passes: 2
|
||||
- Failures: 0
|
||||
- Pending: 0
|
||||
- Duration: 10 seconds
|
||||
@@ -73,6 +74,40 @@ Started video recording: /foo/bar/.projects/working-preprocessor/cypress/videos/
|
||||
- Finished processing: /foo/bar/.projects/working-preprocessor/cypress/videos/abc123.mp4 (0 seconds)
|
||||
|
||||
|
||||
(All Done)
|
||||
|
||||
`
|
||||
|
||||
exports['e2e plugins can modify config from plugins 1'] = `
|
||||
Started video recording: /foo/bar/.projects/plugin-config/cypress/videos/abc123.mp4
|
||||
|
||||
(Tests Starting)
|
||||
|
||||
|
||||
✓ overrides config
|
||||
✓ overrides env
|
||||
|
||||
2 passing
|
||||
|
||||
|
||||
(Tests Finished)
|
||||
|
||||
- Tests: 2
|
||||
- Passes: 2
|
||||
- Failures: 0
|
||||
- Pending: 0
|
||||
- Duration: 10 seconds
|
||||
- Screenshots: 0
|
||||
- Video Recorded: true
|
||||
- Cypress Version: 1.2.3
|
||||
|
||||
|
||||
(Video)
|
||||
|
||||
- Started processing: Compressing to 20 CRF
|
||||
- Finished processing: /foo/bar/.projects/plugin-config/cypress/videos/abc123.mp4 (0 seconds)
|
||||
|
||||
|
||||
(All Done)
|
||||
|
||||
`
|
||||
|
||||
@@ -2,6 +2,7 @@ _ = require("lodash")
|
||||
path = require("path")
|
||||
Promise = require("bluebird")
|
||||
fs = require("fs-extra")
|
||||
deepDiff = require("return-deep-diff")
|
||||
errors = require("./errors")
|
||||
scaffold = require("./scaffold")
|
||||
errors = require("./errors")
|
||||
@@ -12,11 +13,16 @@ v = require("./util/validation")
|
||||
log = require("debug")("cypress:server:config")
|
||||
pathHelpers = require("./util/path_helpers")
|
||||
|
||||
## cypress following by _
|
||||
## cypress followed by _
|
||||
cypressEnvRe = /^(cypress_)/i
|
||||
dashesOrUnderscoresRe = /^(_-)+/
|
||||
oneOrMoreSpacesRe = /\s+/
|
||||
|
||||
toWords = (str) -> str.trim().split(/\s+/)
|
||||
toWords = (str) ->
|
||||
str.trim().split(oneOrMoreSpacesRe)
|
||||
|
||||
isCypressEnvLike = (key) ->
|
||||
cypressEnvRe.test(key) and key isnt "CYPRESS_ENV"
|
||||
|
||||
folders = toWords """
|
||||
fileServerFolder fixturesFolder integrationFolder screenshotsFolder
|
||||
@@ -27,7 +33,7 @@ configKeys = toWords """
|
||||
animationDistanceThreshold fileServerFolder
|
||||
baseUrl fixturesFolder
|
||||
chromeWebSecurity integrationFolder
|
||||
environmentVariables pluginsFile
|
||||
env pluginsFile
|
||||
hosts screenshotsFolder
|
||||
numTestsKeptInMemory supportFile
|
||||
port supportFolder
|
||||
@@ -45,16 +51,13 @@ configKeys = toWords """
|
||||
waitForAnimations
|
||||
"""
|
||||
|
||||
isCypressEnvLike = (key) ->
|
||||
cypressEnvRe.test(key) and key isnt "CYPRESS_ENV"
|
||||
|
||||
defaults = {
|
||||
port: null
|
||||
hosts: null
|
||||
morgan: true
|
||||
baseUrl: null
|
||||
socketId: null
|
||||
isTextTerminal: false
|
||||
isTextTerminal: false
|
||||
reporter: "spec"
|
||||
reporterOptions: null
|
||||
clientRoute: "/__/"
|
||||
@@ -184,21 +187,25 @@ module.exports = {
|
||||
|
||||
_.extend config, _.pick(options, "morgan", "isTextTerminal", "socketId", "report", "browsers")
|
||||
|
||||
_.each @whitelist(options), (val, key) ->
|
||||
_
|
||||
.chain(@whitelist(options))
|
||||
.omit("env")
|
||||
.each (val, key) ->
|
||||
resolved[key] = "cli"
|
||||
config[key] = val
|
||||
return
|
||||
.value()
|
||||
|
||||
if url = config.baseUrl
|
||||
## always strip trailing slashes
|
||||
config.baseUrl = _.trimEnd(url, "/")
|
||||
|
||||
_.defaults config, defaults
|
||||
_.defaults(config, defaults)
|
||||
|
||||
## split out our own app wide env from user env variables
|
||||
## and delete envFile
|
||||
config.environmentVariables = @parseEnv(config, resolved)
|
||||
config.env = process.env["CYPRESS_ENV"]
|
||||
config.env = @parseEnv(config, options.env, resolved)
|
||||
config.cypressEnv = process.env["CYPRESS_ENV"]
|
||||
delete config.envFile
|
||||
|
||||
## when headless
|
||||
@@ -230,6 +237,32 @@ module.exports = {
|
||||
|
||||
return obj
|
||||
|
||||
updateWithPluginValues: (cfg, overrides = {}) ->
|
||||
## diff the overrides with cfg
|
||||
## including nested objects (env)
|
||||
diffs = deepDiff(cfg, overrides, true)
|
||||
|
||||
setResolvedOn = (resolvedObj, obj) ->
|
||||
_.each obj, (val, key) ->
|
||||
if _.isObject(val)
|
||||
## recurse setting overrides
|
||||
## inside of this nested objected
|
||||
setResolvedOn(resolvedObj[key], val)
|
||||
else
|
||||
## override the resolved value
|
||||
resolvedObj[key] = {
|
||||
value: val
|
||||
from: "plugin"
|
||||
}
|
||||
|
||||
## for each override go through
|
||||
## and change the resolved values of cfg
|
||||
## to point to the plugin
|
||||
setResolvedOn(cfg.resolved, diffs)
|
||||
|
||||
## merge cfg into overrides
|
||||
_.defaultsDeep(diffs, cfg)
|
||||
|
||||
resolveConfigValues: (config, defaults, resolved = {}) ->
|
||||
## pick out only the keys found in configKeys
|
||||
_
|
||||
@@ -418,8 +451,8 @@ module.exports = {
|
||||
|
||||
return obj
|
||||
|
||||
parseEnv: (cfg, resolved = {}) ->
|
||||
envVars = resolved.environmentVariables = {}
|
||||
parseEnv: (cfg, envCLI, resolved = {}) ->
|
||||
envVars = resolved.env = {}
|
||||
|
||||
resolveFrom = (from, obj = {}) ->
|
||||
_.each obj, (val, key) ->
|
||||
@@ -431,7 +464,7 @@ module.exports = {
|
||||
envCfg = cfg.env ? {}
|
||||
envFile = cfg.envFile ? {}
|
||||
envProc = @getProcessEnvVars(process.env) ? {}
|
||||
envCLI = cfg.environmentVariables ? {}
|
||||
envCLI = envCLI ? {}
|
||||
|
||||
matchesConfigKey = (key) ->
|
||||
if _.has(cfg, key)
|
||||
|
||||
@@ -251,7 +251,7 @@ API = {
|
||||
|
||||
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.
|
||||
|
||||
Correct your cypress.json, create or fix the file, or set pluginsFile to false if a plugins file is not necessary for your project.
|
||||
Please fix this, or set 'pluginsFile' to 'false' if a plugins file is not necessary for your project.
|
||||
|
||||
#{if arg2 then "The following error was thrown:" else ""}
|
||||
|
||||
@@ -273,13 +273,13 @@ API = {
|
||||
|
||||
We invoked the function exported by '#{arg1}', but it threw an error.
|
||||
|
||||
This is likely an error in the code of the plugins file itself.
|
||||
The following error was thrown:
|
||||
|
||||
#{chalk.yellow(arg2)}
|
||||
""".trim()
|
||||
when "PLUGINS_ERROR"
|
||||
"""
|
||||
The following error was thrown by a plugin. We've stopped running your tests because this likely interrupts behavior critical to them.
|
||||
The following error was thrown by a plugin. We've stopped running your tests because a plugin crashed.
|
||||
|
||||
#{chalk.yellow(arg1)}
|
||||
""".trim()
|
||||
|
||||
@@ -88,7 +88,6 @@ module.exports = {
|
||||
Windows.open(@getWindowArgs(state, options))
|
||||
.then (win) =>
|
||||
Events.start(_.extend({}, options, {
|
||||
env: process.env["CYPRESS_ENV"]
|
||||
onFocusTests: -> win.focus()
|
||||
os: os.platform()
|
||||
}), bus)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const log = require('debug')('cypress:server:plugins:child')
|
||||
const Promise = require('bluebird')
|
||||
const preprocessor = require('./preprocessor')
|
||||
const util = require('../util')
|
||||
|
||||
@@ -20,7 +21,7 @@ const sendError = (ipc, err) => {
|
||||
|
||||
let plugins
|
||||
|
||||
const load = (ipc, config) => {
|
||||
const load = (ipc, config, pluginsFile) => {
|
||||
log('run plugins function')
|
||||
|
||||
let callbackIdCount = 0
|
||||
@@ -40,12 +41,16 @@ const load = (ipc, config) => {
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
plugins(register, config)
|
||||
ipc.send('loaded', registrations)
|
||||
} catch (err) {
|
||||
ipc.send('load:error', 'PLUGINS_FUNCTION_ERROR', util.serializeError(err))
|
||||
}
|
||||
Promise
|
||||
.try(() => {
|
||||
return plugins(register, config)
|
||||
})
|
||||
.then((modifiedCfg) => {
|
||||
ipc.send('loaded', modifiedCfg, registrations)
|
||||
})
|
||||
.catch((err) => {
|
||||
ipc.send('load:error', 'PLUGINS_FUNCTION_ERROR', pluginsFile, err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
const execute = (ipc, event, ids, args = []) => {
|
||||
@@ -93,7 +98,7 @@ module.exports = (ipc, pluginsFile) => {
|
||||
}
|
||||
|
||||
ipc.on('load', (config) => {
|
||||
load(ipc, config)
|
||||
load(ipc, config, pluginsFile)
|
||||
})
|
||||
|
||||
ipc.on('execute', (event, ids, args) => {
|
||||
|
||||
@@ -44,7 +44,7 @@ module.exports = {
|
||||
|
||||
ipc.send("load", config)
|
||||
|
||||
ipc.on "loaded", (registrations) ->
|
||||
ipc.on "loaded", (newCfg, registrations) ->
|
||||
_.each registrations, (registration) ->
|
||||
log("register plugins process event", registration.event, "with id", registration.callbackId)
|
||||
register registration.event, (args...) ->
|
||||
@@ -55,7 +55,8 @@ module.exports = {
|
||||
invocationId: invocationId
|
||||
}
|
||||
ipc.send("execute", registration.event, ids, args)
|
||||
resolve()
|
||||
|
||||
resolve(newCfg)
|
||||
|
||||
ipc.on "load:error", (type, args...) ->
|
||||
reject(errors.get(type, args...))
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
EE = require("events")
|
||||
|
||||
module.exports = (aProcess) ->
|
||||
emitter = new EE()
|
||||
|
||||
aProcess.on "message", (message) ->
|
||||
emitter.emit(message.event, message.args...)
|
||||
|
||||
return {
|
||||
send: (event, args...) ->
|
||||
return if aProcess.killed
|
||||
|
||||
aProcess.send({
|
||||
event: event
|
||||
args
|
||||
})
|
||||
|
||||
on: emitter.on.bind(emitter)
|
||||
removeListener: emitter.removeListener.bind(emitter)
|
||||
}
|
||||
@@ -28,7 +28,7 @@ preprocessor = require("./plugins/preprocessor")
|
||||
settings = require("./util/settings")
|
||||
browsers = require("./browsers")
|
||||
scaffoldLog = require("debug")("cypress:server:scaffold")
|
||||
log = require("debug")("cypress:server:project")
|
||||
debug = require("debug")("cypress:server:project")
|
||||
|
||||
fs = Promise.promisifyAll(fs)
|
||||
glob = Promise.promisify(glob)
|
||||
@@ -53,10 +53,10 @@ class Project extends EE
|
||||
@cfg = null
|
||||
@memoryCheck = null
|
||||
@automation = null
|
||||
log("Project created %s", @projectRoot)
|
||||
debug("Project created %s", @projectRoot)
|
||||
|
||||
open: (options = {}) ->
|
||||
log("opening project instance %s", @projectRoot)
|
||||
debug("opening project instance %s", @projectRoot)
|
||||
@server = Server()
|
||||
|
||||
_.defaults options, {
|
||||
@@ -68,14 +68,27 @@ class Project extends EE
|
||||
|
||||
if process.env.CYPRESS_MEMORY
|
||||
logMemory = ->
|
||||
console.log("memory info", process.memoryUsage())
|
||||
console.debug("memory info", process.memoryUsage())
|
||||
|
||||
@memoryCheck = setInterval(logMemory, 1000)
|
||||
|
||||
@getConfig(options)
|
||||
.then (cfg) =>
|
||||
.tap (cfg) =>
|
||||
process.chdir(@projectRoot)
|
||||
|
||||
## TODO: we currently always scaffold the plugins file
|
||||
## even when headlessly or else it will cause an error when
|
||||
## we try to load it and it's not there. We must do this here
|
||||
## else initialing the plugins will instantly fail.
|
||||
if cfg.pluginsFile
|
||||
scaffold.plugins(path.dirname(cfg.pluginsFile), cfg)
|
||||
.then (cfg) =>
|
||||
@_initPlugins(cfg, options)
|
||||
.then (modifiedCfg) ->
|
||||
debug("plugin config yielded", modifiedCfg)
|
||||
|
||||
return config.updateWithPluginValues(cfg, modifiedCfg)
|
||||
.then (cfg) =>
|
||||
@server.open(cfg, @)
|
||||
.spread (port, warning) =>
|
||||
## if we didnt have a cfg.port
|
||||
@@ -103,18 +116,24 @@ class Project extends EE
|
||||
)
|
||||
.then =>
|
||||
Promise.join(
|
||||
@watchSupportFile(cfg)
|
||||
@checkSupportFile(cfg)
|
||||
@watchPluginsFile(cfg, options)
|
||||
)
|
||||
.then =>
|
||||
@_initPlugins(cfg, options)
|
||||
|
||||
# return our project instance
|
||||
.return(@)
|
||||
|
||||
_initPlugins: (config, options) ->
|
||||
plugins.init(config, {
|
||||
_initPlugins: (cfg, options) ->
|
||||
## only init plugins with the
|
||||
## whitelisted config values to
|
||||
## prevent tampering with the
|
||||
## internals and breaking cypress
|
||||
cfg = config.whitelist(cfg)
|
||||
|
||||
plugins.init(cfg, {
|
||||
onError: (err) ->
|
||||
debug('got plugins error', err.stack)
|
||||
|
||||
browsers.close()
|
||||
options.onError(err)
|
||||
})
|
||||
@@ -128,13 +147,13 @@ class Project extends EE
|
||||
api.getProjectRuns(projectId, authToken)
|
||||
|
||||
reset: ->
|
||||
log("resetting project instance %s", @projectRoot)
|
||||
debug("resetting project instance %s", @projectRoot)
|
||||
|
||||
Promise.try =>
|
||||
@server?.reset()
|
||||
|
||||
close: ->
|
||||
log("closing project instance %s", @projectRoot)
|
||||
debug("closing project instance %s", @projectRoot)
|
||||
|
||||
if @memoryCheck
|
||||
clearInterval(@memoryCheck)
|
||||
@@ -149,43 +168,31 @@ class Project extends EE
|
||||
.then ->
|
||||
process.chdir(localCwd)
|
||||
|
||||
watchSupportFile: (config) ->
|
||||
if supportFile = config.supportFile
|
||||
checkSupportFile: (cfg) ->
|
||||
if supportFile = cfg.supportFile
|
||||
fs.pathExists(supportFile)
|
||||
.then (found) =>
|
||||
if not found
|
||||
errors.throw("SUPPORT_FILE_NOT_FOUND", supportFile)
|
||||
|
||||
relativePath = path.relative(config.projectRoot, config.supportFile)
|
||||
if config.watchForFileChanges isnt false
|
||||
options = {
|
||||
onChange: _.bind(@server.onTestFileChange, @server, relativePath)
|
||||
}
|
||||
preprocessor.getFile(relativePath, config, options)
|
||||
## ignore errors b/c we're just setting up the watching. errors
|
||||
## are handled by the spec controller
|
||||
.catch ->
|
||||
else
|
||||
Promise.resolve()
|
||||
|
||||
watchPluginsFile: (config, options) ->
|
||||
log("attempt watch plugins file: #{config.pluginsFile}")
|
||||
if not config.pluginsFile
|
||||
watchPluginsFile: (cfg, options) ->
|
||||
debug("attempt watch plugins file: #{cfg.pluginsFile}")
|
||||
if not cfg.pluginsFile
|
||||
return Promise.resolve()
|
||||
|
||||
fs.pathExists(config.pluginsFile)
|
||||
fs.pathExists(cfg.pluginsFile)
|
||||
.then (found) =>
|
||||
log("plugins file found? #{found}")
|
||||
debug("plugins file found? #{found}")
|
||||
## ignore if not found. plugins#init will throw the right error
|
||||
return if not found
|
||||
|
||||
log("watch plugins file")
|
||||
@watchers.watch(config.pluginsFile, {
|
||||
debug("watch plugins file")
|
||||
@watchers.watch(cfg.pluginsFile, {
|
||||
onChange: =>
|
||||
## TODO: completely re-open project instead?
|
||||
log("plugins file changed")
|
||||
debug("plugins file changed")
|
||||
## re-init plugins after a change
|
||||
@_initPlugins(config, options)
|
||||
@_initPlugins(cfg, options)
|
||||
.catch (err) ->
|
||||
options.onError(err)
|
||||
})
|
||||
@@ -209,21 +216,21 @@ class Project extends EE
|
||||
|
||||
@watchers.watch(settings.pathToCypressJson(@projectRoot), obj)
|
||||
|
||||
watchSettingsAndStartWebsockets: (options = {}, config = {}) ->
|
||||
watchSettingsAndStartWebsockets: (options = {}, cfg = {}) ->
|
||||
@watchSettings(options.onSettingsChanged)
|
||||
|
||||
## if we've passed down reporter
|
||||
## then record these via mocha reporter
|
||||
if config.report
|
||||
if not Reporter.isValidReporterName(config.reporter, config.projectRoot)
|
||||
paths = Reporter.getSearchPathsForReporter(config.reporter, config.projectRoot)
|
||||
errors.throw("INVALID_REPORTER_NAME", config.reporter, paths)
|
||||
if cfg.report
|
||||
if not Reporter.isValidReporterName(cfg.reporter, cfg.projectRoot)
|
||||
paths = Reporter.getSearchPathsForReporter(cfg.reporter, cfg.projectRoot)
|
||||
errors.throw("INVALID_REPORTER_NAME", cfg.reporter, paths)
|
||||
|
||||
reporter = Reporter.create(config.reporter, config.reporterOptions, config.projectRoot)
|
||||
reporter = Reporter.create(cfg.reporter, cfg.reporterOptions, cfg.projectRoot)
|
||||
|
||||
@automation = Automation.create(config.namespace, config.socketIoCookie, config.screenshotsFolder)
|
||||
@automation = Automation.create(cfg.namespace, cfg.socketIoCookie, cfg.screenshotsFolder)
|
||||
|
||||
@server.startWebsockets(@automation, config, {
|
||||
@server.startWebsockets(@automation, cfg, {
|
||||
onReloadBrowser: options.onReloadBrowser
|
||||
|
||||
onFocusTests: options.onFocusTests
|
||||
@@ -236,12 +243,12 @@ class Project extends EE
|
||||
@emit("socket:connected", id)
|
||||
|
||||
onSetRunnables: (runnables) ->
|
||||
log("onSetRunnables")
|
||||
log("runnables", runnables)
|
||||
debug("onSetRunnables")
|
||||
debug("runnables", runnables)
|
||||
reporter?.setRunnables(runnables)
|
||||
|
||||
onMocha: (event, runnable) =>
|
||||
log("onMocha", event)
|
||||
debug("onMocha", event)
|
||||
## bail if we dont have a
|
||||
## reporter instance
|
||||
return if not reporter
|
||||
@@ -365,8 +372,8 @@ class Project extends EE
|
||||
|
||||
[browserUrl, "#/tests", specUrl].join("/").replace(multipleForwardSlashesRe, replacer)
|
||||
|
||||
scaffold: (config) ->
|
||||
log("scaffolding project %s", @projectRoot)
|
||||
scaffold: (cfg) ->
|
||||
debug("scaffolding project %s", @projectRoot)
|
||||
|
||||
scaffolds = []
|
||||
|
||||
@@ -380,19 +387,13 @@ class Project extends EE
|
||||
##
|
||||
## ensure support dir is created
|
||||
## and example support file if dir doesnt exist
|
||||
push(scaffold.support(config.supportFolder, config))
|
||||
|
||||
## TODO: we currently always scaffold the plugins file
|
||||
## even when headlessly or else it will cause an error when
|
||||
## we try to load it and it's not there
|
||||
if config.pluginsFile
|
||||
push(scaffold.plugins(path.dirname(config.pluginsFile), config))
|
||||
push(scaffold.support(cfg.supportFolder, cfg))
|
||||
|
||||
## if we're in headed mode add these other scaffolding
|
||||
## tasks
|
||||
if not config.isTextTerminal
|
||||
push(scaffold.integration(config.integrationFolder, config))
|
||||
push(scaffold.fixture(config.fixturesFolder, config))
|
||||
if not cfg.isTextTerminal
|
||||
push(scaffold.integration(cfg.integrationFolder, cfg))
|
||||
push(scaffold.fixture(cfg.fixturesFolder, cfg))
|
||||
|
||||
Promise.all(scaffolds)
|
||||
|
||||
@@ -472,13 +473,13 @@ class Project extends EE
|
||||
_.extend({}, clientProject, {state: state})
|
||||
|
||||
@_getProject = (clientProject, authToken) ->
|
||||
log("get project from api", clientProject.id, clientProject.path)
|
||||
debug("get project from api", clientProject.id, clientProject.path)
|
||||
api.getProject(clientProject.id, authToken)
|
||||
.then (project) ->
|
||||
log("got project from api")
|
||||
debug("got project from api")
|
||||
Project._mergeDetails(clientProject, project)
|
||||
.catch (err) ->
|
||||
log("failed to get project from api", err.statusCode)
|
||||
debug("failed to get project from api", err.statusCode)
|
||||
switch err.statusCode
|
||||
when 404
|
||||
## project doesn't exist
|
||||
@@ -490,40 +491,40 @@ class Project extends EE
|
||||
throw err
|
||||
|
||||
@getProjectStatuses = (clientProjects = []) ->
|
||||
log("get project statuses for #{clientProjects.length} projects")
|
||||
debug("get project statuses for #{clientProjects.length} projects")
|
||||
user.ensureAuthToken()
|
||||
.then (authToken) ->
|
||||
log("got auth token #{authToken}")
|
||||
debug("got auth token #{authToken}")
|
||||
api.getProjects(authToken).then (projects = []) ->
|
||||
log("got #{projects.length} projects")
|
||||
debug("got #{projects.length} projects")
|
||||
projectsIndex = _.keyBy(projects, "id")
|
||||
Promise.all(_.map clientProjects, (clientProject) ->
|
||||
log("looking at", clientProject.path)
|
||||
debug("looking at", clientProject.path)
|
||||
## not a CI project, just mark as valid and return
|
||||
if not clientProject.id
|
||||
log("no project id")
|
||||
debug("no project id")
|
||||
return Project._mergeState(clientProject, "VALID")
|
||||
|
||||
if project = projectsIndex[clientProject.id]
|
||||
log("found matching:", project)
|
||||
debug("found matching:", project)
|
||||
## merge in details for matching project
|
||||
Project._mergeDetails(clientProject, project)
|
||||
else
|
||||
log("did not find matching:", project)
|
||||
debug("did not find matching:", project)
|
||||
## project has id, but no matching project found
|
||||
## check if it doesn't exist or if user isn't authorized
|
||||
Project._getProject(clientProject, authToken)
|
||||
)
|
||||
|
||||
@getProjectStatus = (clientProject) ->
|
||||
log("get project status for", clientProject.id, clientProject.path)
|
||||
debug("get project status for", clientProject.id, clientProject.path)
|
||||
|
||||
if not clientProject.id
|
||||
log("no project id")
|
||||
debug("no project id")
|
||||
return Promise.resolve(Project._mergeState(clientProject, "VALID"))
|
||||
|
||||
user.ensureAuthToken().then (authToken) ->
|
||||
log("got auth token #{authToken}")
|
||||
debug("got auth token #{authToken}")
|
||||
Project._getProject(clientProject, authToken)
|
||||
|
||||
@remove = (path) ->
|
||||
@@ -579,7 +580,7 @@ class Project extends EE
|
||||
# Given a path to the project, finds all specs
|
||||
# returns list of specs with respect to the project root
|
||||
@findSpecs = (projectPath, specPattern) ->
|
||||
log("finding specs for project %s", projectPath)
|
||||
debug("finding specs for project %s", projectPath)
|
||||
la(check.unemptyString(projectPath), "missing project path", projectPath)
|
||||
la(check.maybe.unemptyString(specPattern), "invalid spec pattern", specPattern)
|
||||
|
||||
|
||||
@@ -439,7 +439,7 @@ class Server
|
||||
if fullyQualifiedUrl is "<root>" or not fullyQualifiedRe.test(fullyQualifiedUrl)
|
||||
@_remoteOrigin = "http://#{DEFAULT_DOMAIN_NAME}:#{@_port()}"
|
||||
@_remoteStrategy = "file"
|
||||
@_remoteFileServer = "http://#{DEFAULT_DOMAIN_NAME}:#{@_fileServer.port()}"
|
||||
@_remoteFileServer = "http://#{DEFAULT_DOMAIN_NAME}:#{@_fileServer?.port()}"
|
||||
@_remoteDomainName = DEFAULT_DOMAIN_NAME
|
||||
@_remoteProps = null
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ module.exports = {
|
||||
"run-project": "runProject"
|
||||
"return-pkg": "returnPkg"
|
||||
"auto-open": "autoOpen"
|
||||
"env": "environmentVariables"
|
||||
"headless": "isTextTerminal"
|
||||
"exit-with-code": "exitWithCode"
|
||||
"reporter-options": "reporterOptions"
|
||||
@@ -89,7 +88,6 @@ module.exports = {
|
||||
options = _
|
||||
.chain(options)
|
||||
.defaults(whitelisted)
|
||||
.extend({env: process.env["CYPRESS_ENV"]})
|
||||
.mapValues(coerce)
|
||||
.value()
|
||||
|
||||
@@ -107,9 +105,9 @@ module.exports = {
|
||||
backup("hosts", options)
|
||||
options.hosts = parseNestedValues(hosts)
|
||||
|
||||
if envs = options.environmentVariables
|
||||
backup("environmentVariables", options)
|
||||
options.environmentVariables = parseNestedValues(envs)
|
||||
if envs = options.env
|
||||
backup("env", options)
|
||||
options.env = parseNestedValues(envs)
|
||||
|
||||
if ro = options.reporterOptions
|
||||
backup("reporterOptions", options)
|
||||
|
||||
@@ -138,6 +138,7 @@
|
||||
"randomstring": "^1.1.5",
|
||||
"request": "2.79.0",
|
||||
"request-promise": "4.1.1",
|
||||
"return-deep-diff": "^0.2.9",
|
||||
"sanitize-filename": "^1.6.1",
|
||||
"semver": "^5.3.0",
|
||||
"send": "^0.14.1",
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
e2e = require("../support/helpers/e2e")
|
||||
Fixtures = require("../support/helpers/fixtures")
|
||||
|
||||
pluginConfig = Fixtures.projectPath("plugin-config")
|
||||
workingPreprocessor = Fixtures.projectPath("working-preprocessor")
|
||||
pluginsAsyncError = Fixtures.projectPath("plugins-async-error")
|
||||
|
||||
describe "e2e plugins", ->
|
||||
e2e.setup({npmInstall: true})
|
||||
e2e.setup()
|
||||
|
||||
it "passes", ->
|
||||
e2e.exec(@, {
|
||||
@@ -22,3 +23,13 @@ describe "e2e plugins", ->
|
||||
snapshot: true
|
||||
expectedExitCode: 1
|
||||
})
|
||||
|
||||
it "can modify config from plugins", ->
|
||||
e2e.exec(@, {
|
||||
spec: "app_spec.coffee"
|
||||
env: "foo=foo,bar=bar"
|
||||
config: "pageLoadTimeout=10000"
|
||||
project: pluginConfig
|
||||
snapshot: true
|
||||
expectedExitCode: 0
|
||||
})
|
||||
|
||||
@@ -77,6 +77,7 @@ describe "lib/cypress", ->
|
||||
@todosPath = Fixtures.projectPath("todos")
|
||||
@pristinePath = Fixtures.projectPath("pristine")
|
||||
@noScaffolding = Fixtures.projectPath("no-scaffolding")
|
||||
@pluginConfig = Fixtures.projectPath("plugin-config")
|
||||
@idsPath = Fixtures.projectPath("ids")
|
||||
|
||||
## force cypress to call directly into main without
|
||||
@@ -630,6 +631,40 @@ describe "lib/cypress", ->
|
||||
|
||||
@expectExitWith(0)
|
||||
|
||||
it "can override values in plugins", ->
|
||||
cypress.start([
|
||||
"--run-project=#{@pluginConfig}", "--config=requestTimeout=1234,videoCompression=false"
|
||||
"--env=foo=foo,bar=bar"
|
||||
])
|
||||
.then =>
|
||||
cfg = openProject.getProject().cfg
|
||||
|
||||
expect(cfg.videoCompression).to.eq(20)
|
||||
expect(cfg.defaultCommandTimeout).to.eq(500)
|
||||
expect(cfg.env).to.deep.eq({
|
||||
foo: "bar"
|
||||
bar: "bar"
|
||||
})
|
||||
|
||||
expect(cfg.resolved.videoCompression).to.deep.eq({
|
||||
value: 20
|
||||
from: "plugin"
|
||||
})
|
||||
expect(cfg.resolved.requestTimeout).to.deep.eq({
|
||||
value: 1234
|
||||
from: "cli"
|
||||
})
|
||||
expect(cfg.resolved.env.foo).to.deep.eq({
|
||||
value: "bar"
|
||||
from: "plugin"
|
||||
})
|
||||
expect(cfg.resolved.env.bar).to.deep.eq({
|
||||
value: "bar"
|
||||
from: "cli"
|
||||
})
|
||||
|
||||
@expectExitWith(0)
|
||||
|
||||
describe "--port", ->
|
||||
beforeEach ->
|
||||
headless.listenForProjectEnd.resolves({failures: 0})
|
||||
@@ -675,7 +710,7 @@ describe "lib/cypress", ->
|
||||
"version=0.12.1,foo=bar,host=http://localhost:8888,baz=quux=dolor"
|
||||
])
|
||||
.then =>
|
||||
expect(openProject.getProject().cfg.environmentVariables).to.deep.eq({
|
||||
expect(openProject.getProject().cfg.env).to.deep.eq({
|
||||
version: "0.12.1"
|
||||
foo: "bar"
|
||||
host: "http://localhost:8888"
|
||||
@@ -1070,7 +1105,7 @@ describe "lib/cypress", ->
|
||||
port: 2121
|
||||
pageLoadTimeout: 1000
|
||||
report: false
|
||||
environmentVariables: { baz: "baz" }
|
||||
env: { baz: "baz" }
|
||||
})
|
||||
|
||||
cfg = open.getCall(0).args[0]
|
||||
@@ -1081,12 +1116,12 @@ describe "lib/cypress", ->
|
||||
expect(cfg.baseUrl).to.eq("localhost")
|
||||
expect(cfg.watchForFileChanges).to.be.false
|
||||
expect(cfg.responseTimeout).to.eq(5555)
|
||||
expect(cfg.environmentVariables.baz).to.eq("baz")
|
||||
expect(cfg.environmentVariables).not.to.have.property("fileServerFolder")
|
||||
expect(cfg.environmentVariables).not.to.have.property("port")
|
||||
expect(cfg.environmentVariables).not.to.have.property("BASE_URL")
|
||||
expect(cfg.environmentVariables).not.to.have.property("watchForFileChanges")
|
||||
expect(cfg.environmentVariables).not.to.have.property("responseTimeout")
|
||||
expect(cfg.env.baz).to.eq("baz")
|
||||
expect(cfg.env).not.to.have.property("fileServerFolder")
|
||||
expect(cfg.env).not.to.have.property("port")
|
||||
expect(cfg.env).not.to.have.property("BASE_URL")
|
||||
expect(cfg.env).not.to.have.property("watchForFileChanges")
|
||||
expect(cfg.env).not.to.have.property("responseTimeout")
|
||||
|
||||
expect(cfg.resolved.fileServerFolder).to.deep.eq({
|
||||
value: "foo"
|
||||
@@ -1112,7 +1147,7 @@ describe "lib/cypress", ->
|
||||
value: 5555
|
||||
from: "env"
|
||||
})
|
||||
expect(cfg.resolved.environmentVariables.baz).to.deep.eq({
|
||||
expect(cfg.resolved.env.baz).to.deep.eq({
|
||||
value: "baz"
|
||||
from: "cli"
|
||||
})
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,14 @@
|
||||
it "overrides config", ->
|
||||
## overrides come from plugins
|
||||
expect(Cypress.config("defaultCommandTimeout")).to.eq(500)
|
||||
expect(Cypress.config("videoCompression")).to.eq(20)
|
||||
|
||||
## overrides come from CLI
|
||||
expect(Cypress.config("pageLoadTimeout")).to.eq(10000)
|
||||
|
||||
it "overrides env", ->
|
||||
## overrides come from plugins
|
||||
expect(Cypress.env("foo")).to.eq("bar")
|
||||
|
||||
## overrides come from CLI
|
||||
expect(Cypress.env("bar")).to.eq("bar")
|
||||
@@ -0,0 +1,12 @@
|
||||
module.exports = (on, config) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, 100)
|
||||
})
|
||||
.then(() => {
|
||||
config.defaultCommandTimeout = 500
|
||||
config.videoCompression = 20
|
||||
config.env.foo = 'bar'
|
||||
|
||||
return config
|
||||
})
|
||||
}
|
||||
@@ -150,7 +150,7 @@ module.exports = {
|
||||
args.push("--hosts=#{options.hosts}")
|
||||
|
||||
if options.debug
|
||||
args.push("--show-headless-gui")
|
||||
args.push("--headed")
|
||||
|
||||
if options.reporter
|
||||
args.push("--reporter=#{options.reporter}")
|
||||
@@ -161,6 +161,12 @@ module.exports = {
|
||||
if browser = (env.BROWSER or options.browser)
|
||||
args.push("--browser=#{browser}")
|
||||
|
||||
if options.config
|
||||
args.push("--config", options.config)
|
||||
|
||||
if options.env
|
||||
args.push("--env", options.env)
|
||||
|
||||
return args
|
||||
|
||||
start: (ctx, options = {}) ->
|
||||
|
||||
@@ -43,7 +43,7 @@ describe "lib/util/args", ->
|
||||
context "--env", ->
|
||||
it "converts to object literal", ->
|
||||
options = @setup("--env", "foo=bar,version=0.12.1,host=localhost:8888,bar=qux=")
|
||||
expect(options.environmentVariables).to.deep.eq({
|
||||
expect(options.env).to.deep.eq({
|
||||
foo: "bar"
|
||||
version: "0.12.1"
|
||||
host: "localhost:8888"
|
||||
@@ -86,16 +86,15 @@ describe "lib/util/args", ->
|
||||
expect(@setup("--no-record").record).to.be.false
|
||||
expect(@setup("--record=false").record).to.be.false
|
||||
|
||||
it "backs up hosts + environmentVariables", ->
|
||||
it "backs up hosts + env", ->
|
||||
expect(@obj).to.deep.eq({
|
||||
_: []
|
||||
env: process.env.NODE_ENV
|
||||
"get-key": true
|
||||
getKey: true
|
||||
_hosts: "*.foobar.com=127.0.0.1"
|
||||
hosts: {"*.foobar.com": "127.0.0.1"}
|
||||
_environmentVariables: "foo=bar,baz=quux,bar=foo=quz"
|
||||
environmentVariables: {
|
||||
_env: "foo=bar,baz=quux,bar=foo=quz"
|
||||
env: {
|
||||
foo: "bar"
|
||||
baz: "quux"
|
||||
bar: "foo=quz"
|
||||
@@ -114,7 +113,7 @@ describe "lib/util/args", ->
|
||||
"--getKey=true"
|
||||
"--config=requestTimeout=1234,responseTimeout=9876"
|
||||
"--hosts=*.foobar.com=127.0.0.1"
|
||||
"--environmentVariables=foo=bar,baz=quux,bar=foo=quz"
|
||||
"--env=foo=bar,baz=quux,bar=foo=quz"
|
||||
"--requestTimeout=1234"
|
||||
"--responseTimeout=9876"
|
||||
])
|
||||
@@ -137,7 +136,6 @@ describe "lib/util/args", ->
|
||||
"/Applications/Cypress.app"
|
||||
"/Applications/Cypress.app"
|
||||
]
|
||||
env: process.env.NODE_ENV
|
||||
appPath: "/Applications/Cypress.app"
|
||||
execPath: "/Applications/Cypress.app"
|
||||
updating: true
|
||||
@@ -159,7 +157,6 @@ describe "lib/util/args", ->
|
||||
"/Applications/Cypress.app1"
|
||||
"/Applications/Cypress.app2"
|
||||
]
|
||||
env: process.env.NODE_ENV
|
||||
appPath: "a"
|
||||
execPath: "e"
|
||||
"app-path": "a"
|
||||
|
||||
@@ -29,7 +29,7 @@ describe "lib/config", ->
|
||||
config.get(@projectPath)
|
||||
.then (obj) =>
|
||||
expect(obj.projectRoot).to.eq(@projectPath)
|
||||
expect(obj.environmentVariables).to.deep.eq({foo: "bar"})
|
||||
expect(obj.env).to.deep.eq({foo: "bar"})
|
||||
|
||||
it "sets projectName", ->
|
||||
@setup({}, {foo: "bar"})
|
||||
@@ -481,9 +481,6 @@ describe "lib/config", ->
|
||||
it "baseUrl=null", ->
|
||||
@defaults "baseUrl", null
|
||||
|
||||
it "env=CYPRESS_ENV", ->
|
||||
@defaults "env", process.env["CYPRESS_ENV"]
|
||||
|
||||
it "defaultCommandTimeout=4000", ->
|
||||
@defaults "defaultCommandTimeout", 4000
|
||||
|
||||
@@ -581,12 +578,12 @@ describe "lib/config", ->
|
||||
|
||||
config.mergeDefaults(obj)
|
||||
.then (cfg) ->
|
||||
expect(cfg.environmentVariables).to.deep.eq({
|
||||
expect(cfg.env).to.deep.eq({
|
||||
foo: "bar"
|
||||
bar: "baz"
|
||||
version: "1.0.1"
|
||||
})
|
||||
expect(cfg.env).to.eq(process.env["CYPRESS_ENV"])
|
||||
expect(cfg.cypressEnv).to.eq(process.env["CYPRESS_ENV"])
|
||||
expect(cfg).not.to.have.property("envFile")
|
||||
|
||||
it "merges env into @config.env", ->
|
||||
@@ -600,7 +597,7 @@ describe "lib/config", ->
|
||||
}
|
||||
|
||||
options = {
|
||||
environmentVariables: {
|
||||
env: {
|
||||
version: "0.13.1"
|
||||
foo: "bar"
|
||||
}
|
||||
@@ -608,7 +605,7 @@ describe "lib/config", ->
|
||||
|
||||
config.mergeDefaults(obj, options)
|
||||
.then (cfg) ->
|
||||
expect(cfg.environmentVariables).to.deep.eq({
|
||||
expect(cfg.env).to.deep.eq({
|
||||
host: "localhost"
|
||||
user: "brian"
|
||||
version: "0.13.1"
|
||||
@@ -629,6 +626,7 @@ describe "lib/config", ->
|
||||
config.mergeDefaults(obj, options)
|
||||
.then (cfg) ->
|
||||
expect(cfg.resolved).to.deep.eq({
|
||||
env: { }
|
||||
port: { value: 1234, from: "cli" },
|
||||
hosts: { value: null, from: "default" }
|
||||
reporter: { value: "json", from: "cli" },
|
||||
@@ -651,14 +649,13 @@ describe "lib/config", ->
|
||||
fileServerFolder: { value: "", from: "default" },
|
||||
videoRecording: { value: true, from: "default" }
|
||||
videoCompression: { value: 32, from: "default" }
|
||||
videoUploadOnPasses: { value: true, from: "default" }
|
||||
videoUploadOnPasses: { value: true, from: "default" }
|
||||
videosFolder: { value: "cypress/videos", from: "default" },
|
||||
supportFile: { value: "cypress/support", from: "default" },
|
||||
pluginsFile: { value: "cypress/plugins", from: "default" },
|
||||
fixturesFolder: { value: "cypress/fixtures", from: "default" },
|
||||
integrationFolder: { value: "cypress/integration", from: "default" },
|
||||
screenshotsFolder: { value: "cypress/screenshots", from: "default" },
|
||||
environmentVariables: { }
|
||||
testFiles: { value: "**/*.*", from: "default" }
|
||||
})
|
||||
|
||||
@@ -675,13 +672,14 @@ describe "lib/config", ->
|
||||
envFile: {
|
||||
bar: "bar"
|
||||
}
|
||||
environmentVariables: {
|
||||
}
|
||||
|
||||
options = {
|
||||
env: {
|
||||
baz: "baz"
|
||||
}
|
||||
}
|
||||
|
||||
options = {}
|
||||
|
||||
config.mergeDefaults(obj, options)
|
||||
.then (cfg) ->
|
||||
expect(cfg.resolved).to.deep.eq({
|
||||
@@ -715,7 +713,7 @@ describe "lib/config", ->
|
||||
integrationFolder: { value: "cypress/integration", from: "default" },
|
||||
screenshotsFolder: { value: "cypress/screenshots", from: "default" },
|
||||
testFiles: { value: "**/*.*", from: "default" }
|
||||
environmentVariables: {
|
||||
env: {
|
||||
foo: {
|
||||
value: "foo"
|
||||
from: "config"
|
||||
@@ -735,6 +733,61 @@ describe "lib/config", ->
|
||||
}
|
||||
})
|
||||
|
||||
context ".updateWithPluginValues", ->
|
||||
it "is noop when no overrides", ->
|
||||
expect(config.updateWithPluginValues({foo: 'bar'}, null)).to.deep.eq({
|
||||
foo: 'bar'
|
||||
})
|
||||
|
||||
it "updates resolved config values and returns config with overrides", ->
|
||||
cfg = {
|
||||
foo: "bar"
|
||||
baz: "quux"
|
||||
lol: 1234
|
||||
env: {
|
||||
a: "a"
|
||||
b: "b"
|
||||
}
|
||||
resolved: {
|
||||
foo: { value: "bar", from: "default" }
|
||||
baz: { value: "quux", from: "cli" }
|
||||
lol: { value: 1234, from: "env" }
|
||||
env: {
|
||||
a: { value: "a", from: "config" }
|
||||
b: { value: "b", from: "config" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
overrides = {
|
||||
baz: "baz"
|
||||
env: {
|
||||
b: "bb"
|
||||
c: "c"
|
||||
}
|
||||
}
|
||||
|
||||
expect(config.updateWithPluginValues(cfg, overrides)).to.deep.eq({
|
||||
foo: "bar"
|
||||
baz: "baz"
|
||||
lol: 1234
|
||||
env: {
|
||||
a: "a"
|
||||
b: "bb"
|
||||
c: "c"
|
||||
}
|
||||
resolved: {
|
||||
foo: { value: "bar", from: "default" }
|
||||
baz: { value: "baz", from: "plugin" }
|
||||
lol: { value: 1234, from: "env" }
|
||||
env: {
|
||||
a: { value: "a", from: "config" }
|
||||
b: { value: "bb", from: "plugin" }
|
||||
c: { value: "c", from: "plugin" }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
context ".parseEnv", ->
|
||||
it "merges together env from config, env from file, env from process, and env from CLI", ->
|
||||
@sandbox.stub(config, "getProcessEnvVars").returns({version: "0.12.1", user: "bob"})
|
||||
@@ -752,14 +805,14 @@ describe "lib/config", ->
|
||||
user: "brian"
|
||||
foo: "bar"
|
||||
}
|
||||
|
||||
environmentVariables: {
|
||||
version: "0.14.0"
|
||||
project: "pie"
|
||||
}
|
||||
}
|
||||
|
||||
expect(config.parseEnv(obj)).to.deep.eq({
|
||||
envCLI = {
|
||||
version: "0.14.0"
|
||||
project: "pie"
|
||||
}
|
||||
|
||||
expect(config.parseEnv(obj, envCLI)).to.deep.eq({
|
||||
version: "0.14.0"
|
||||
project: "pie"
|
||||
host: "http://localhost:8888"
|
||||
|
||||
@@ -99,17 +99,13 @@ describe "gui/headed", ->
|
||||
@sandbox.stub(state, "get").resolves(@state)
|
||||
|
||||
it "calls Events.start with options, adding env, onFocusTests, and os", ->
|
||||
env = process.env["CYPRESS_ENV"]
|
||||
process.env["CYPRESS_ENV"] = "development"
|
||||
@sandbox.stub(os, "platform").returns("someOs")
|
||||
opts = {}
|
||||
|
||||
headed.ready(opts).then ->
|
||||
expect(Events.start).to.be.called
|
||||
expect(Events.start.lastCall.args[0].env).to.equal("development")
|
||||
expect(Events.start.lastCall.args[0].onFocusTests).to.be.a("function")
|
||||
expect(Events.start.lastCall.args[0].os).to.equal("someOs")
|
||||
process.env["CYPRESS_ENV"] = env
|
||||
|
||||
it "calls menu.set", ->
|
||||
headed.ready({}).then ->
|
||||
|
||||
@@ -33,7 +33,7 @@ describe "lib/modes/headless", ->
|
||||
|
||||
options = {
|
||||
port: 8080
|
||||
environmentVariables: {foo: "bar"}
|
||||
env: {foo: "bar"}
|
||||
projectPath: "/_test-output/path/to/project/foo"
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ describe "lib/modes/headless", ->
|
||||
expect(openProject.create).to.be.calledWithMatch("/_test-output/path/to/project/foo", {
|
||||
port: 8080
|
||||
projectPath: "/_test-output/path/to/project/foo"
|
||||
environmentVariables: {foo: "bar"}
|
||||
env: {foo: "bar"}
|
||||
}, {
|
||||
morgan: false
|
||||
socketId: 1234
|
||||
|
||||
@@ -55,6 +55,21 @@ describe "lib/plugins/child/run_plugins", ->
|
||||
snapshot(JSON.stringify(@ipc.send.lastCall.args[3]))
|
||||
|
||||
describe "on 'load' message", ->
|
||||
it "sends error if pluginsFile function rejects the promise", (done) ->
|
||||
err = new Error('foo')
|
||||
pluginsFn = @sandbox.stub().rejects(err)
|
||||
|
||||
mockery.registerMock("plugins-file", pluginsFn)
|
||||
@ipc.on.withArgs("load").yields({})
|
||||
runPlugins(@ipc, "plugins-file")
|
||||
|
||||
@ipc.send = (event, errorType, pluginsFile, stack) ->
|
||||
expect(event).to.eq("load:error")
|
||||
expect(errorType).to.eq("PLUGINS_FUNCTION_ERROR")
|
||||
expect(pluginsFile).to.eq("plugins-file")
|
||||
expect(stack).to.eq(err.stack)
|
||||
done()
|
||||
|
||||
it "calls function exported by pluginsFile with register function and config", ->
|
||||
pluginsFn = @sandbox.spy()
|
||||
mockery.registerMock("plugins-file", pluginsFn)
|
||||
@@ -65,12 +80,19 @@ describe "lib/plugins/child/run_plugins", ->
|
||||
expect(pluginsFn.lastCall.args[0]).to.be.a("function")
|
||||
expect(pluginsFn.lastCall.args[1]).to.equal(config)
|
||||
|
||||
it "sends error if pluginsFile function throws an error", ->
|
||||
mockery.registerMock("plugins-file", -> foo.bar())
|
||||
it "sends error if pluginsFile function throws an error", (done) ->
|
||||
err = new Error('foo')
|
||||
|
||||
mockery.registerMock "plugins-file", -> throw err
|
||||
runPlugins(@ipc, "plugins-file")
|
||||
@ipc.on.withArgs("load").yield()
|
||||
expect(@ipc.send).to.be.called
|
||||
snapshot(withoutStack(@ipc.send.lastCall.args[2]))
|
||||
|
||||
@ipc.send = (event, errorType, pluginsFile, stack) ->
|
||||
expect(event).to.eq("load:error")
|
||||
expect(errorType).to.eq("PLUGINS_FUNCTION_ERROR")
|
||||
expect(pluginsFile).to.eq("plugins-file")
|
||||
expect(stack).to.eq(err.stack)
|
||||
done()
|
||||
|
||||
describe "on 'execute' message", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -61,7 +61,9 @@ describe "lib/plugins/index", ->
|
||||
|
||||
describe "loaded message", ->
|
||||
beforeEach ->
|
||||
@ipc.on.withArgs("loaded").yields([{
|
||||
@config = {}
|
||||
|
||||
@ipc.on.withArgs("loaded").yields(@config, [{
|
||||
event: "some:event"
|
||||
callbackId: 0
|
||||
}])
|
||||
@@ -81,16 +83,29 @@ describe "lib/plugins/index", ->
|
||||
expect(value).to.equal("value")
|
||||
|
||||
describe "load:error message", ->
|
||||
beforeEach ->
|
||||
@ipc.on.withArgs("load:error").yields("PLUGINS_FILE_ERROR", "path/to/pluginsFile.js", "error message")
|
||||
context "PLUGINS_FILE_ERROR", ->
|
||||
beforeEach ->
|
||||
@ipc.on.withArgs("load:error").yields("PLUGINS_FILE_ERROR", "path/to/pluginsFile.js", "error message stack")
|
||||
|
||||
it "rejects plugins.init", ->
|
||||
plugins.init({ pluginsFile: "cypress-plugin" })
|
||||
.catch (err) =>
|
||||
expect(err.message).to.contain("The plugins file is missing or invalid")
|
||||
expect(err.message).to.contain("path/to/pluginsFile.js")
|
||||
expect(err.message).to.contain("The following error was thrown")
|
||||
expect(err.message).to.contain("error message")
|
||||
it "rejects plugins.init", ->
|
||||
plugins.init({ pluginsFile: "cypress-plugin" })
|
||||
.catch (err) =>
|
||||
expect(err.message).to.contain("The plugins file is missing or invalid")
|
||||
expect(err.message).to.contain("path/to/pluginsFile.js")
|
||||
expect(err.message).to.contain("The following error was thrown")
|
||||
expect(err.message).to.contain("error message stack")
|
||||
|
||||
context "PLUGINS_FUNCTION_ERROR", ->
|
||||
beforeEach ->
|
||||
@ipc.on.withArgs("load:error").yields("PLUGINS_FUNCTION_ERROR", "path/to/pluginsFile.js", "error message stack")
|
||||
|
||||
it "rejects plugins.init", ->
|
||||
plugins.init({ pluginsFile: "cypress-plugin" })
|
||||
.catch (err) =>
|
||||
expect(err.message).to.contain("The function exported by the plugins file threw an error.")
|
||||
expect(err.message).to.contain("path/to/pluginsFile.js")
|
||||
expect(err.message).to.contain("The following error was thrown:")
|
||||
expect(err.message).to.contain("error message stack")
|
||||
|
||||
describe "error message", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -139,11 +139,13 @@ describe "lib/project", ->
|
||||
context "#open", ->
|
||||
beforeEach ->
|
||||
@sandbox.stub(@project, "watchSettingsAndStartWebsockets").resolves()
|
||||
@sandbox.stub(@project, "watchSupportFile").resolves()
|
||||
@sandbox.stub(@project, "checkSupportFile").resolves()
|
||||
@sandbox.stub(@project, "scaffold").resolves()
|
||||
@sandbox.stub(@project, "getConfig").resolves(@config)
|
||||
@sandbox.stub(Server.prototype, "open").resolves([])
|
||||
@sandbox.stub(Server.prototype, "reset")
|
||||
@sandbox.stub(config, "updateWithPluginValues").returns(@config)
|
||||
@sandbox.stub(scaffold, "plugins").resolves()
|
||||
@sandbox.stub(plugins, "init").resolves()
|
||||
|
||||
it "calls #watchSettingsAndStartWebsockets with options + config", ->
|
||||
@@ -156,9 +158,9 @@ describe "lib/project", ->
|
||||
@project.open().then =>
|
||||
expect(@project.scaffold).to.be.calledWith(@config)
|
||||
|
||||
it "calls #watchSupportFile with server config when scaffolding is finished", ->
|
||||
it "calls #checkSupportFile with server config when scaffolding is finished", ->
|
||||
@project.open().then =>
|
||||
expect(@project.watchSupportFile).to.be.calledWith(@config)
|
||||
expect(@project.checkSupportFile).to.be.calledWith(@config)
|
||||
|
||||
it "calls #getConfig options", ->
|
||||
opts = {}
|
||||
@@ -167,7 +169,11 @@ describe "lib/project", ->
|
||||
|
||||
it "initializes the plugins", ->
|
||||
@project.open({}).then =>
|
||||
expect(plugins.init).to.be.calledWith(@config)
|
||||
expect(plugins.init).to.be.called
|
||||
|
||||
it "calls support.plugins with pluginsFile directory", ->
|
||||
@project.open({}).then =>
|
||||
expect(scaffold.plugins).to.be.calledWith(path.dirname(@config.pluginsFile))
|
||||
|
||||
it "calls options.onError with plugins error when there is a plugins error", ->
|
||||
onError = @sandbox.spy()
|
||||
@@ -263,10 +269,6 @@ describe "lib/project", ->
|
||||
@project.scaffold(@obj).then =>
|
||||
expect(scaffold.support).to.be.calledWith(@obj.supportFolder)
|
||||
|
||||
it "calls support.plugins with pluginsFile directory", ->
|
||||
@project.scaffold(@obj).then =>
|
||||
expect(scaffold.plugins).to.be.calledWith("pf")
|
||||
|
||||
it "does not call support.plugins if config.pluginsFile is falsey", ->
|
||||
@obj.pluginsFile = false
|
||||
@project.scaffold(@obj).then =>
|
||||
@@ -314,7 +316,7 @@ describe "lib/project", ->
|
||||
|
||||
expect(stub).to.be.calledOnce
|
||||
|
||||
context "#watchSupportFile", ->
|
||||
context "#checkSupportFile", ->
|
||||
beforeEach ->
|
||||
@sandbox.stub(fs, "pathExists").resolves(true)
|
||||
@project = Project("/_test-output/path/to/project")
|
||||
@@ -326,30 +328,13 @@ describe "lib/project", ->
|
||||
}
|
||||
|
||||
it "does nothing when {supportFile: false}", ->
|
||||
@project.watchSupportFile({supportFile: false})
|
||||
ret = @project.checkSupportFile({supportFile: false})
|
||||
|
||||
expect(preprocessor.getFile).not.to.be.called
|
||||
|
||||
it "calls preprocessor.getFile with relative path to file", ->
|
||||
@project.watchSupportFile(@config)
|
||||
.then () =>
|
||||
expect(preprocessor.getFile).to.be.calledWith("foo/bar.js", @config)
|
||||
|
||||
it "calls server.onTestFileChange when file changes", ->
|
||||
@project.watchSupportFile(@config)
|
||||
.then () =>
|
||||
preprocessor.getFile.firstCall.args[2].onChange()
|
||||
expect(@project.server.onTestFileChange).to.be.calledWith("foo/bar.js")
|
||||
|
||||
it "does not add change listener when {watchForFileChanges: false}", ->
|
||||
@config.watchForFileChanges = false
|
||||
@project.watchSupportFile(@config)
|
||||
.then () =>
|
||||
expect(preprocessor.getFile.firstCall.args[2]).to.be.undefined
|
||||
expect(ret).to.be.undefined
|
||||
|
||||
it "throws when support file does not exist", ->
|
||||
fs.pathExists.resolves(false)
|
||||
@project.watchSupportFile(@config)
|
||||
@project.checkSupportFile(@config)
|
||||
.catch (e) ->
|
||||
expect(e.message).to.include("The support file is missing or invalid.")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user