diff --git a/.eslintrc b/.eslintrc index b32d48eecb..ae9c088641 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,6 +3,7 @@ "plugin:cypress-dev/general" ], "env": { + "es6": true, "node": true } } diff --git a/packages/desktop-gui/cypress/fixtures/config.json b/packages/desktop-gui/cypress/fixtures/config.json index bc9e278b7c..066aaf2b28 100644 --- a/packages/desktop-gui/cypress/fixtures/config.json +++ b/packages/desktop-gui/cypress/fixtures/config.json @@ -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": "" diff --git a/packages/desktop-gui/cypress/integration/settings_spec.coffee b/packages/desktop-gui/cypress/integration/settings_spec.coffee index 74b7ffb655..e6be5e80da 100644 --- a/packages/desktop-gui/cypress/integration/settings_spec.coffee +++ b/packages/desktop-gui/cypress/integration/settings_spec.coffee @@ -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 diff --git a/packages/desktop-gui/src/app/app.jsx b/packages/desktop-gui/src/app/app.jsx index 8c6bed32cb..4728f0ff17 100644 --- a/packages/desktop-gui/src/app/app.jsx +++ b/packages/desktop-gui/src/app/app.jsx @@ -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() }) diff --git a/packages/desktop-gui/src/lib/app-store.js b/packages/desktop-gui/src/lib/app-store.js index 220e869a99..e243d9cf56 100644 --- a/packages/desktop-gui/src/lib/app-store.js +++ b/packages/desktop-gui/src/lib/app-store.js @@ -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 diff --git a/packages/desktop-gui/src/settings/configuration.jsx b/packages/desktop-gui/src/settings/configuration.jsx index 3eb0ac18d5..aca3955b25 100644 --- a/packages/desktop-gui/src/settings/configuration.jsx +++ b/packages/desktop-gui/src/settings/configuration.jsx @@ -102,6 +102,10 @@ const Configuration = observer(({ project }) => (
diff --git a/packages/desktop-gui/src/settings/settings.scss b/packages/desktop-gui/src/settings/settings.scss
index 88014b298d..d350ddcf6b 100644
--- a/packages/desktop-gui/src/settings/settings.scss
+++ b/packages/desktop-gui/src/settings/settings.scss
@@ -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 {
diff --git a/packages/driver/src/cypress.coffee b/packages/driver/src/cypress.coffee
index 3f3c4f37a2..1b333cf872 100644
--- a/packages/driver/src/cypress.coffee
+++ b/packages/driver/src/cypress.coffee
@@ -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)
diff --git a/packages/driver/src/cypress/error_messages.coffee b/packages/driver/src/cypress/error_messages.coffee
index ced55e6faa..bc9380a4ec 100644
--- a/packages/driver/src/cypress/error_messages.coffee
+++ b/packages/driver/src/cypress/error_messages.coffee
@@ -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:
diff --git a/packages/driver/test/unit/cypress_spec.coffee b/packages/driver/test/unit/cypress_spec.coffee
index 36c0b209a5..4c401fba35 100644
--- a/packages/driver/test/unit/cypress_spec.coffee
+++ b/packages/driver/test/unit/cypress_spec.coffee
@@ -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"})
diff --git a/packages/runner/src/app/app.jsx b/packages/runner/src/app/app.jsx
index 7f2e5be2f0..b4cf14e6e8 100644
--- a/packages/runner/src/app/app.jsx
+++ b/packages/runner/src/app/app.jsx
@@ -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,
diff --git a/packages/runner/src/app/app.spec.jsx b/packages/runner/src/app/app.spec.jsx
index 9db076d740..87617e535a 100644
--- a/packages/runner/src/app/app.spec.jsx
+++ b/packages/runner/src/app/app.spec.jsx
@@ -13,7 +13,7 @@ import App from './app'
const createProps = () => ({
config: {
browsers: [],
- env: 'tst',
+ cypressEnv: 'tst',
integrationFolder: '',
numTestsKeptInMemory: 1,
projectName: '',
diff --git a/packages/runner/src/app/container.spec.jsx b/packages/runner/src/app/container.spec.jsx
index 213a49ba40..e95bb02071 100644
--- a/packages/runner/src/app/container.spec.jsx
+++ b/packages/runner/src/app/container.spec.jsx
@@ -14,7 +14,7 @@ import Container, { automationElementId } from './container'
const createProps = () => ({
config: {
browsers: [],
- env: 'test',
+ cypressEnv: 'test',
integrationFolder: '',
numTestsKeptInMemory: 1,
projectName: '',
diff --git a/packages/runner/src/lib/event-manager.js b/packages/runner/src/lib/event-manager.js
index 4de6531546..fbe2cb0cae 100644
--- a/packages/runner/src/lib/event-manager.js
+++ b/packages/runner/src/lib/event-manager.js
@@ -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
diff --git a/packages/server/__snapshots__/plugins_spec.coffee b/packages/server/__snapshots__/plugins_spec.coffee
index 75179c368d..2936c55b2c 100644
--- a/packages/server/__snapshots__/plugins_spec.coffee
+++ b/packages/server/__snapshots__/plugins_spec.coffee
@@ -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)
`
diff --git a/packages/server/lib/config.coffee b/packages/server/lib/config.coffee
index 3dc5e5fdd4..62b42b6556 100644
--- a/packages/server/lib/config.coffee
+++ b/packages/server/lib/config.coffee
@@ -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)
diff --git a/packages/server/lib/errors.coffee b/packages/server/lib/errors.coffee
index 009a37b492..92cdc4965d 100644
--- a/packages/server/lib/errors.coffee
+++ b/packages/server/lib/errors.coffee
@@ -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()
diff --git a/packages/server/lib/modes/headed.coffee b/packages/server/lib/modes/headed.coffee
index 3f0cca8147..df9833e178 100644
--- a/packages/server/lib/modes/headed.coffee
+++ b/packages/server/lib/modes/headed.coffee
@@ -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)
diff --git a/packages/server/lib/plugins/child/run_plugins.js b/packages/server/lib/plugins/child/run_plugins.js
index 332cc7888c..11fb7986b8 100644
--- a/packages/server/lib/plugins/child/run_plugins.js
+++ b/packages/server/lib/plugins/child/run_plugins.js
@@ -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) => {
diff --git a/packages/server/lib/plugins/index.coffee b/packages/server/lib/plugins/index.coffee
index 92832b07cc..c8bba92db8 100644
--- a/packages/server/lib/plugins/index.coffee
+++ b/packages/server/lib/plugins/index.coffee
@@ -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...))
diff --git a/packages/server/lib/plugins/wrap_ipc.coffee b/packages/server/lib/plugins/wrap_ipc.coffee
deleted file mode 100644
index f55408aefe..0000000000
--- a/packages/server/lib/plugins/wrap_ipc.coffee
+++ /dev/null
@@ -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)
- }
diff --git a/packages/server/lib/project.coffee b/packages/server/lib/project.coffee
index 3f77767e58..a23692f284 100644
--- a/packages/server/lib/project.coffee
+++ b/packages/server/lib/project.coffee
@@ -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)
diff --git a/packages/server/lib/server.coffee b/packages/server/lib/server.coffee
index a74e6d5725..c68f896968 100644
--- a/packages/server/lib/server.coffee
+++ b/packages/server/lib/server.coffee
@@ -439,7 +439,7 @@ class Server
if fullyQualifiedUrl is "" 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
diff --git a/packages/server/lib/util/args.coffee b/packages/server/lib/util/args.coffee
index 8e8cb25342..10aacdb0ca 100644
--- a/packages/server/lib/util/args.coffee
+++ b/packages/server/lib/util/args.coffee
@@ -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)
diff --git a/packages/server/package.json b/packages/server/package.json
index ebb9e46f34..c742fc999d 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -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",
diff --git a/packages/server/test/e2e/plugins_spec.coffee b/packages/server/test/e2e/plugins_spec.coffee
index 25b328e26a..24c5098691 100644
--- a/packages/server/test/e2e/plugins_spec.coffee
+++ b/packages/server/test/e2e/plugins_spec.coffee
@@ -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
+ })
diff --git a/packages/server/test/integration/cypress_spec.coffee b/packages/server/test/integration/cypress_spec.coffee
index edec4dfa65..75d27a3200 100644
--- a/packages/server/test/integration/cypress_spec.coffee
+++ b/packages/server/test/integration/cypress_spec.coffee
@@ -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"
})
diff --git a/packages/server/test/support/fixtures/projects/plugin-config/cypress.json b/packages/server/test/support/fixtures/projects/plugin-config/cypress.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/packages/server/test/support/fixtures/projects/plugin-config/cypress.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/server/test/support/fixtures/projects/plugin-config/cypress/integration/app_spec.coffee b/packages/server/test/support/fixtures/projects/plugin-config/cypress/integration/app_spec.coffee
new file mode 100644
index 0000000000..8b956d3b0b
--- /dev/null
+++ b/packages/server/test/support/fixtures/projects/plugin-config/cypress/integration/app_spec.coffee
@@ -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")
diff --git a/packages/server/test/support/fixtures/projects/plugin-config/cypress/plugins/index.js b/packages/server/test/support/fixtures/projects/plugin-config/cypress/plugins/index.js
new file mode 100644
index 0000000000..95461bde6d
--- /dev/null
+++ b/packages/server/test/support/fixtures/projects/plugin-config/cypress/plugins/index.js
@@ -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
+ })
+}
diff --git a/packages/server/test/support/helpers/e2e.coffee b/packages/server/test/support/helpers/e2e.coffee
index bf3c4ac194..b9ee52bcf1 100644
--- a/packages/server/test/support/helpers/e2e.coffee
+++ b/packages/server/test/support/helpers/e2e.coffee
@@ -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 = {}) ->
diff --git a/packages/server/test/unit/args_spec.coffee b/packages/server/test/unit/args_spec.coffee
index 5cede440c3..fa5e1b1d75 100644
--- a/packages/server/test/unit/args_spec.coffee
+++ b/packages/server/test/unit/args_spec.coffee
@@ -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"
diff --git a/packages/server/test/unit/config_spec.coffee b/packages/server/test/unit/config_spec.coffee
index 24ba2adcef..d62fa18115 100644
--- a/packages/server/test/unit/config_spec.coffee
+++ b/packages/server/test/unit/config_spec.coffee
@@ -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"
diff --git a/packages/server/test/unit/modes/headed_spec.coffee b/packages/server/test/unit/modes/headed_spec.coffee
index 74b7b86de6..ca16acdfc1 100644
--- a/packages/server/test/unit/modes/headed_spec.coffee
+++ b/packages/server/test/unit/modes/headed_spec.coffee
@@ -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 ->
diff --git a/packages/server/test/unit/modes/headless_spec.coffee b/packages/server/test/unit/modes/headless_spec.coffee
index 48abff1dcd..344bf537e8 100644
--- a/packages/server/test/unit/modes/headless_spec.coffee
+++ b/packages/server/test/unit/modes/headless_spec.coffee
@@ -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
diff --git a/packages/server/test/unit/plugins/child/run_plugins_spec.coffee b/packages/server/test/unit/plugins/child/run_plugins_spec.coffee
index f3ac401465..9fcc388037 100644
--- a/packages/server/test/unit/plugins/child/run_plugins_spec.coffee
+++ b/packages/server/test/unit/plugins/child/run_plugins_spec.coffee
@@ -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 ->
diff --git a/packages/server/test/unit/plugins/index_spec.coffee b/packages/server/test/unit/plugins/index_spec.coffee
index 6d88ac2b83..d7d50b5c9b 100644
--- a/packages/server/test/unit/plugins/index_spec.coffee
+++ b/packages/server/test/unit/plugins/index_spec.coffee
@@ -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 ->
diff --git a/packages/server/test/unit/project_spec.coffee b/packages/server/test/unit/project_spec.coffee
index 905ce658fb..e8d067d4af 100644
--- a/packages/server/test/unit/project_spec.coffee
+++ b/packages/server/test/unit/project_spec.coffee
@@ -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.")