diff --git a/packages/server/lib/browsers/electron.coffee b/packages/server/lib/browsers/electron.coffee index 7ad6ae24db..076f1a84de 100644 --- a/packages/server/lib/browsers/electron.coffee +++ b/packages/server/lib/browsers/electron.coffee @@ -85,7 +85,9 @@ module.exports = { }, resolve) open: (browserName, url, options = {}, automation) -> - savedState(options.projectPath).get() + savedState(options.projectPath) + .then (state) -> + state.get() .then (state) => @_render(url, state, options) .then (win) => diff --git a/packages/server/lib/config.coffee b/packages/server/lib/config.coffee index ae8689c2f5..d11e9e3d82 100644 --- a/packages/server/lib/config.coffee +++ b/packages/server/lib/config.coffee @@ -1,7 +1,7 @@ _ = require("lodash") path = require("path") Promise = require("bluebird") -fs = require("fs") +fs = require("fs-extra") errors = require("./errors") scaffold = require("./scaffold") errors = require("./errors") @@ -187,11 +187,8 @@ module.exports = { config = @setParentTestsPaths(config) - config = @setSupportFileAndFolder(config) - - config = @setScaffoldPaths(config) - - return config + @setSupportFileAndFolder(config) + .then @setScaffoldPaths setResolvedConfigValues: (config, defaults, resolved) -> obj = _.clone(config) @@ -235,18 +232,22 @@ module.exports = { return obj + # async function setSupportFileAndFolder: (obj) -> - return obj if not obj.supportFile + return Promise.resolve(obj) if not obj.supportFile + obj = _.clone(obj) ## TODO move this logic to find support file into util/path_helpers - sf = obj.supportFile log "setting support file #{sf}" log "for project root #{obj.projectRoot}" - try + + Promise + .try -> ## resolve full path with extension obj.supportFile = require.resolve(sf) + .then () -> if pathHelpers.checkIfResolveChangedRootFolder(obj.supportFile, sf) log("require.resolve switched support folder from %s to %s", sf, obj.supportFile) @@ -255,36 +256,38 @@ module.exports = { # which can confuse the rest of the code # switch it back to "normal" file obj.supportFile = path.join(sf, path.basename(obj.supportFile)) - if not fs.existsSync(obj.supportFile) - errors.throw("SUPPORT_FILE_NOT_FOUND", obj.supportFile) - log("switching to found file %s", obj.supportFile) - catch err - if err.code isnt "MODULE_NOT_FOUND" - throw err - - log("support file does not exist") + return fs.pathExists(obj.supportFile) + .then (found) -> + if not found + errors.throw("SUPPORT_FILE_NOT_FOUND", obj.supportFile) + log("switching to found file %s", obj.supportFile) + .catch({code: "MODULE_NOT_FOUND"}, -> + log("support file %s does not exist", sf) ## supportFile doesn't exist on disk if sf is path.resolve(obj.projectRoot, defaults.supportFile) log("support file is default, check if #{path.dirname(sf)} exists") - if fs.existsSync(sf) - log("support folder exists, set supportFile to false") - ## if the directory exists, set it to false so it's ignored - obj.supportFile = false + return fs.pathExists(sf) + .then (found) -> + if (found) + log("support folder exists, set supportFile to false") + ## if the directory exists, set it to false so it's ignored + obj.supportFile = false + else + log("support folder does not exist, set to default index.js") + ## otherwise, set it up to be scaffolded later + obj.supportFile = path.join(sf, "index.js") return obj - else - log("support folder does not exist, set to default index.js") - ## otherwise, set it up to be scaffolded later - obj.supportFile = path.join(sf, "index.js") else log("support file is not default") ## they have it explicitly set, so it should be there errors.throw("SUPPORT_FILE_NOT_FOUND", path.resolve(obj.projectRoot, sf)) - - ## set config.supportFolder to its directory - obj.supportFolder = path.dirname(obj.supportFile) - log "set support folder #{obj.supportFolder}" - - return obj + ) + .then () -> + if obj.supportFile + ## set config.supportFolder to its directory + obj.supportFolder = path.dirname(obj.supportFile) + log "set support folder #{obj.supportFolder}" + obj setParentTestsPaths: (obj) -> ## projectRoot: "/path/to/project" diff --git a/packages/server/lib/environment.coffee b/packages/server/lib/environment.coffee index f91823b470..6d70f9a345 100644 --- a/packages/server/lib/environment.coffee +++ b/packages/server/lib/environment.coffee @@ -1,7 +1,7 @@ require("./util/http_overrides") +require("./fs_warn")(require("fs-extra")) os = require("os") -fs = require("fs-extra") cwd = require("./cwd") Promise = require("bluebird") diff --git a/packages/server/lib/fs_warn.coffee b/packages/server/lib/fs_warn.coffee new file mode 100644 index 0000000000..3d4ec9a355 --- /dev/null +++ b/packages/server/lib/fs_warn.coffee @@ -0,0 +1,35 @@ +{T, F} = require("ramda") + +## warn users if somehow synchronous file methods are invoked +## these methods due to "too many files" errors are a huge pain +warnOnSyncFileSystem = -> + console.error "WARNING: fs sync methods can fail due to EMFILE errors" + console.error "Cypress only works reliably when ALL fs calls are async" + console.error "You should modify these sync calls to be async" + +topLines = (from, n) -> (text) -> + text.split("\n").slice(from, n).join("\n") + +# just hide this function itself +# stripping top few lines of the stack +getStack = () -> + err = new Error() + topLines(3, 10)(err.stack) + +addSyncFileSystemWarnings = (fs) -> + oldExistsSync = fs.existsSync + fs.existsSync = (filename) -> + warnOnSyncFileSystem() + console.error(getStack()) + oldExistsSync(filename) + + if not fs.pathExists + # pathExists was introduced to fs-extra@3.0.0 + # if it does not exist mimic it using async methods + # and convert result into boolean + fs.pathExists = (filename) -> + fs.statAsync(filename) + .then(T) + .catch({code: "ENOENT"}, F) + +module.exports = addSyncFileSystemWarnings diff --git a/packages/server/lib/gui/windows.coffee b/packages/server/lib/gui/windows.coffee index a4d2a27e76..78ed3b70ee 100644 --- a/packages/server/lib/gui/windows.coffee +++ b/packages/server/lib/gui/windows.coffee @@ -295,7 +295,9 @@ module.exports = { newState[keys.height] = height newState[keys.x] = x newState[keys.y] = y - savedState(projectPath).set(newState) + savedState(projectPath) + .then (state) -> + state.set(newState) , 500 win.on "moved", _.debounce -> @@ -305,17 +307,23 @@ module.exports = { newState = {} newState[keys.x] = x newState[keys.y] = y - savedState(projectPath).set(newState) + savedState(projectPath) + .then (state) -> + state.set(newState) , 500 win.webContents.on "devtools-opened", -> newState = {} newState[keys.devTools] = true - savedState(projectPath).set(newState) + savedState(projectPath) + .then (state) -> + state.set(newState) win.webContents.on "devtools-closed", -> newState = {} newState[keys.devTools] = false - savedState(projectPath).set(newState) + savedState(projectPath) + .then (state) -> + state.set(newState) } diff --git a/packages/server/lib/modes/headed.coffee b/packages/server/lib/modes/headed.coffee index 8abe9958ef..3f0cca8147 100644 --- a/packages/server/lib/modes/headed.coffee +++ b/packages/server/lib/modes/headed.coffee @@ -82,7 +82,8 @@ module.exports = { bus.emit("menu:item:clicked", "log:out") }) - savedState(options.projectPath).get() + savedState(options.projectPath) + .then (state) -> state.get() .then (state) => Windows.open(@getWindowArgs(state, options)) .then (win) => diff --git a/packages/server/lib/project.coffee b/packages/server/lib/project.coffee index 32e1c3dbc1..09a754744d 100644 --- a/packages/server/lib/project.coffee +++ b/packages/server/lib/project.coffee @@ -121,18 +121,22 @@ class Project extends EE watchSupportFile: (config) -> if supportFile = config.supportFile - if not fs.existsSync(supportFile) - errors.throw("SUPPORT_FILE_NOT_FOUND", 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) - } - @watchers.watchBundle(relativePath, config, options) - ## ignore errors b/c we're just setting up the watching. errors - ## are handled by the spec controller - .catch -> + relativePath = path.relative(config.projectRoot, config.supportFile) + if config.watchForFileChanges isnt false + options = { + onChange: _.bind(@server.onTestFileChange, @server, relativePath) + } + @watchers.watchBundle(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() watchSettings: (onSettingsChanged) -> ## bail if we havent been told to @@ -244,13 +248,16 @@ class Project extends EE throw new Error("Missing project config") if not @cfg throw new Error("Missing project root") if not @projectRoot newState = _.merge({}, @cfg.state, stateChanges) - savedState(@projectRoot).set(newState) + savedState(@projectRoot) + .then (state) -> + state.set(newState) .then => @cfg.state = newState newState _setSavedState: (cfg) -> - savedState(@projectRoot).get() + savedState(@projectRoot) + .then (state) -> state.get() .then (state) -> cfg.state = state cfg diff --git a/packages/server/lib/routes.coffee b/packages/server/lib/routes.coffee index aaf1c2c9ed..15e5708334 100644 --- a/packages/server/lib/routes.coffee +++ b/packages/server/lib/routes.coffee @@ -12,6 +12,8 @@ files = require("./controllers/files") proxy = require("./controllers/proxy") driver = require("./controllers/driver") staticCtrl = require("./controllers/static") +la = require("lazy-ass") +check = require("check-more-types") module.exports = (app, config, request, getRemoteState, watchers, project) -> ## routing for the actual specs which are processed automatically @@ -63,6 +65,7 @@ module.exports = (app, config, request, getRemoteState, watchers, project) -> ## TODO: we should additionally send config for the socket.io route, etc ## and any other __cypress namespaced files so that the runner does ## not have to be aware of anything + la(check.unemptyString(config.clientRoute), "missing client route in config", config) app.get config.clientRoute, (req, res) -> runner.serve(req, res, config, getRemoteState) diff --git a/packages/server/lib/saved_state.coffee b/packages/server/lib/saved_state.coffee index a8f3c5a550..8faffe8c3d 100644 --- a/packages/server/lib/saved_state.coffee +++ b/packages/server/lib/saved_state.coffee @@ -20,17 +20,19 @@ stateFiles = {} # project.open().then(project.state).then(state) # state should have width = 200 +# async promise-returning function findSavedSate = (projectPath) -> - statePath = savedStateUtil.formStatePath(projectPath) - fullStatePath = appData.projectsPath(statePath) - log('full state path %s', fullStatePath) - return stateFiles[fullStatePath] if stateFiles[fullStatePath] + savedStateUtil.formStatePath(projectPath) + .then (statePath) -> + fullStatePath = appData.projectsPath(statePath) + log('full state path %s', fullStatePath) + return stateFiles[fullStatePath] if stateFiles[fullStatePath] - log('making new state file around %s', fullStatePath) - stateFile = new FileUtil({ - path: fullStatePath - }) - stateFiles[fullStatePath] = stateFile - stateFile + log('making new state file around %s', fullStatePath) + stateFile = new FileUtil({ + path: fullStatePath + }) + stateFiles[fullStatePath] = stateFile + stateFile module.exports = findSavedSate diff --git a/packages/server/lib/server.coffee b/packages/server/lib/server.coffee index 6b40ac5775..26fab65d25 100644 --- a/packages/server/lib/server.coffee +++ b/packages/server/lib/server.coffee @@ -8,6 +8,8 @@ express = require("express") Promise = require("bluebird") evilDns = require("evil-dns") httpProxy = require("http-proxy") +la = require("lazy-ass") +check = require("check-more-types") httpsProxy = require("@packages/https-proxy") log = require("debug")("cypress:server:server") cors = require("./util/cors") @@ -102,6 +104,7 @@ class Server e open: (config = {}, project) -> + la(_.isPlainObject(config), "expected plain config object", config) Promise.try => ## always reset any buffers ## TODO: change buffers to be an instance diff --git a/packages/server/lib/util/saved_state.coffee b/packages/server/lib/util/saved_state.coffee index e4e1a72b7e..21da202841 100644 --- a/packages/server/lib/util/saved_state.coffee +++ b/packages/server/lib/util/saved_state.coffee @@ -1,9 +1,10 @@ -log = require('../log') -cwd = require('../cwd') -fs = require('fs') +log = require('../log') +cwd = require('../cwd') +fs = require('fs-extra') +md5 = require('md5') +sanitize = require("sanitize-filename") +Promise = require("bluebird") { basename, join, isAbsolute } = require('path') -md5 = require('md5') -sanitize = require("sanitize-filename") toHashName = (projectPath) -> throw new Error("Missing project path") unless projectPath @@ -12,26 +13,34 @@ toHashName = (projectPath) -> hash = md5(projectPath) "#{name}-#{hash}" +# async promise-returning method formStatePath = (projectPath) -> - log('making saved state from %s', cwd()) - if projectPath - log('for project path %s', projectPath) - else - log('missing project path, looking for project here') - cypressJsonPath = cwd('cypress.json') - if fs.existsSync(cypressJsonPath) - log('found cypress file %s', cypressJsonPath) - projectPath = cwd() + Promise.resolve() + .then -> + log('making saved state from %s', cwd()) + if projectPath + log('for project path %s', projectPath) + else + log('missing project path, looking for project here') - fileName = "state.json" - if projectPath - log("state path for project #{projectPath}") - statePath = join(toHashName(projectPath), fileName) - else - log("state path for global mode") - statePath = join("__global__", fileName) + cypressJsonPath = cwd('cypress.json') + fs.pathExists(cypressJsonPath) + .then (found) -> + if found + log('found cypress file %s', cypressJsonPath) + projectPath = cwd() + return projectPath - return statePath + .then (projectPath) -> + fileName = "state.json" + if projectPath + log("state path for project #{projectPath}") + statePath = join(toHashName(projectPath), fileName) + else + log("state path for global mode") + statePath = join("__global__", fileName) + + return statePath module.exports = { toHashName: toHashName, diff --git a/packages/server/package.json b/packages/server/package.json index 1b8e15a276..cb356fd489 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -39,6 +39,7 @@ "cors": "^2.8.3", "coveralls": "^2.11.8", "electron-osx-sign": "^0.3.0", + "execa": "^0.8.0", "express-session": "^1.14.1", "express-useragent": "^1.0.4", "https-proxy-agent": "^1.0.0", @@ -88,6 +89,7 @@ "cookie-parser": "^1.3.3", "data-uri-to-buffer": "0.0.4", "debug": "^2.6.8", + "delay": "^2.0.0", "electron-context-menu": "^0.8.0", "electron-positioner": "3.0.0", "errorhandler": "1.1.1", diff --git a/packages/server/readme.md b/packages/server/readme.md index e3c98dd1a0..20874a3bcc 100644 --- a/packages/server/readme.md +++ b/packages/server/readme.md @@ -207,3 +207,13 @@ socket.emit("remote:response", "123-a-guid-as-an-id", {a: "new response data obj - `npm i` - Open new tab: `npm run watch` - `node_modules/.bin/nw .` + +## Misc + +**important** do not use sync file system methods to work with files. They can fail if +there are too many files (the `EMILE` exception). Asynchronous file system methods +all use [graceful-fs](https://github.com/isaacs/node-graceful-fs#readme) to retry and +get around this problem. + +* there is `fs.pathExists(filename)` method that is returning a promise, use that + instead of `fs.exists` or `fs.existsSync`. diff --git a/packages/server/test/integration/cli_spec.coffee b/packages/server/test/integration/cli_spec.coffee index ccfbf8d73f..cf628a9062 100644 --- a/packages/server/test/integration/cli_spec.coffee +++ b/packages/server/test/integration/cli_spec.coffee @@ -1,10 +1,13 @@ require("../spec_helper") _ = require("lodash") +R = require("ramda") cp = require("child_process") pr = require("../support/helpers/process") pkg = require("../../package.json") root = require("@packages/root") +execa = require("execa") +semver = require("semver") anyLineWithCaret = (str) -> str[0] is ">" @@ -52,31 +55,46 @@ describe "CLI Interface", -> ## caused false-positives in CI because tests were failing ## but the exit code was always zero context "exit codes", -> - beforeEach -> - ## run the start script directly - ## instead of going through npm wrapper - @start = pkg.scripts.start + describe "from start script command", -> + beforeEach -> + ## run the start script directly + ## instead of going through npm wrapper + @start = pkg.scripts.start - it "exits with code 22", (done) -> - s = cp.exec("#{@start} --exit-with-code=22") - s.on "close", (code) -> - expect(code).to.eq(22) - done() + it "exits with code 22", (done) -> + s = cp.exec("#{@start} --exit-with-code=22") + s.on "close", (code) -> + expect(code).to.eq(22) + done() - it "exits with code 0", (done) -> - s = cp.exec("#{@start} --exit-with-code=0") - s.on "close", (code) -> - expect(code).to.eq(0) - done() + it "exits with code 0", (done) -> + s = cp.exec("#{@start} --exit-with-code=0") + s.on "close", (code) -> + expect(code).to.eq(0) + done() - it "npm slurps up exit value and exits with 1 on failure", (done) -> - s = cp.exec("npm start -- --exit-with-code=10") - s.on "close", (code) -> - expect(code).to.eq(1) - done() + describe "through NPM script", -> + npmVersion = null - it "npm passes on 0 exit code", (done) -> - s = cp.exec("npm start -- --exit-with-code=0") - s.on "close", (code) -> - expect(code).to.eq(0) - done() + isNpmSlurpingCode = () -> + semver.lt(npmVersion, '4.0.0') + + beforeEach -> + execa("npm", ["-version"]) + .then R.prop("stdout") + .then (version) -> + npmVersion = version + expect(npmVersion).to.be.a.string + + it "npm slurps up or not exit value on failure", (done) -> + expectedCode = if isNpmSlurpingCode() then 1 else 10 + s = cp.exec("npm start -- --exit-with-code=10") + s.on "close", (code) -> + expect(code).to.eq(expectedCode) + done() + + it "npm passes on 0 exit code", (done) -> + s = cp.exec("npm start -- --exit-with-code=0") + s.on "close", (code) -> + expect(code).to.eq(0) + done() diff --git a/packages/server/test/integration/cypress_spec.coffee b/packages/server/test/integration/cypress_spec.coffee index fd68998e9f..8fa6323b44 100644 --- a/packages/server/test/integration/cypress_spec.coffee +++ b/packages/server/test/integration/cypress_spec.coffee @@ -5,7 +5,6 @@ os = require("os") cp = require("child_process") path = require("path") { EventEmitter } = require("events") -{ unlinkSync: rm, existsSync: exists } = require("fs") http = require("http") Promise = require("bluebird") electron = require("electron") @@ -210,12 +209,16 @@ describe "lib/cypress", -> context "state", -> statePath = null beforeEach -> - # TODO switch to async file system calls - statePath = appData.projectsPath(formStatePath(@todosPath)) - rm(statePath) if exists(statePath) + formStatePath(@todosPath) + .then (statePathStart) -> + statePath = appData.projectsPath(statePathStart) + fs.pathExists(statePath) + .then (found) -> + if found + fs.unlink(statePath) afterEach -> - rm(statePath) + fs.unlink(statePath) it "saves project state", -> Project.add(@todosPath) @@ -225,8 +228,10 @@ describe "lib/cypress", -> @expectExitWith(0) .then -> openProject.getProject().saveState() - .then (state) -> - expect(exists(statePath), "Finds saved stage file #{statePath}").to.be.true + .then () -> + fs.pathExists(statePath) + .then (found) -> + expect(found, "Finds saved stage file #{statePath}").to.be.true it "runs project headlessly and exits with exit code 0 and yells about old version of CLI", -> Project.add(@todosPath) diff --git a/packages/server/test/integration/http_requests_spec.coffee b/packages/server/test/integration/http_requests_spec.coffee index 962e3a9dff..8507bb39f4 100644 --- a/packages/server/test/integration/http_requests_spec.coffee +++ b/packages/server/test/integration/http_requests_spec.coffee @@ -65,59 +65,59 @@ describe "Routes", -> ## get all the config defaults ## and allow us to override them ## for each test - cfg = config.set(obj) + config.set(obj) + .then (cfg) => + ## use a jar for each test + ## but reset it automatically + ## between test + jar = rp.jar() - ## use a jar for each test - ## but reset it automatically - ## between test - jar = rp.jar() + ## use a custom request promise + ## to automatically backfill these + ## options including our proxy + @rp = (options = {}) => + if _.isString(options) + url = options + options = {} - ## use a custom request promise - ## to automatically backfill these - ## options including our proxy - @rp = (options = {}) => - if _.isString(options) - url = options - options = {} + _.defaults options, { + url, + proxy: @proxy, + jar, + simple: false, + followRedirect: false, + resolveWithFullResponse: true + } + rp(options) - _.defaults options, { - url, - proxy: @proxy, - jar, - simple: false, - followRedirect: false, - resolveWithFullResponse: true - } - rp(options) + open = => + Promise.all([ + ## open our https server + httpsServer.start(8443), - open = => - Promise.all([ - ## open our https server - httpsServer.start(8443), + ## and open our cypress server + @server = Server(Watchers()) - ## and open our cypress server - @server = Server(Watchers()) + @server.open(cfg) + .spread (port) => + if initialUrl + @server._onDomainSet(initialUrl) - @server.open(cfg) - .spread (port) => - if initialUrl - @server._onDomainSet(initialUrl) + @srv = @server.getHttpServer() - @srv = @server.getHttpServer() + @session = new (Session({app: @srv})) - @session = new (Session({app: @srv})) + @proxy = "http://localhost:" + port + ]) - @proxy = "http://localhost:" + port - ]) - - if @server - Promise.join( - httpsServer.stop() - @server.close() - ) - .then(open) - else - open() + if @server + Promise.join( + httpsServer.stop() + @server.close() + ) + .then(open) + else + open() afterEach -> evilDns.clear() diff --git a/packages/server/test/integration/server_spec.coffee b/packages/server/test/integration/server_spec.coffee index 3a5cabe6c9..f4a4a33fd7 100644 --- a/packages/server/test/integration/server_spec.coffee +++ b/packages/server/test/integration/server_spec.coffee @@ -27,61 +27,61 @@ describe "Server", -> ## get all the config defaults ## and allow us to override them ## for each test - cfg = config.set(obj) + config.set(obj) + .then (cfg) => + ## use a jar for each test + ## but reset it automatically + ## between test + jar = rp.jar() - ## use a jar for each test - ## but reset it automatically - ## between test - jar = rp.jar() + ## use a custom request promise + ## to automatically backfill these + ## options including our proxy + @rp = (options = {}) => + if _.isString(options) + url = options + options = {} - ## use a custom request promise - ## to automatically backfill these - ## options including our proxy - @rp = (options = {}) => - if _.isString(options) - url = options - options = {} + _.defaults options, { + url + proxy: @proxy + jar + simple: false + followRedirect: false + resolveWithFullResponse: true + } + rp(options) - _.defaults options, { - url - proxy: @proxy - jar - simple: false - followRedirect: false - resolveWithFullResponse: true - } - rp(options) + open = => + Promise.all([ + ## open our https server + httpsServer.start(8443), - open = => - Promise.all([ - ## open our https server - httpsServer.start(8443), + ## and open our cypress server + @server = Server() - ## and open our cypress server - @server = Server() + @server.open(cfg) + .spread (port) => + if initialUrl + @server._onDomainSet(initialUrl) - @server.open(cfg) - .spread (port) => - if initialUrl - @server._onDomainSet(initialUrl) + @srv = @server.getHttpServer() - @srv = @server.getHttpServer() + # @session = new (Session({app: @srv})) - # @session = new (Session({app: @srv})) + @proxy = "http://localhost:" + port - @proxy = "http://localhost:" + port + @fileServer = @server._fileServer.address() + ]) - @fileServer = @server._fileServer.address() - ]) - - if @server - Promise.join( - httpsServer.stop() - @server.close() - ) - .then(open) - else - open() + if @server + Promise.join( + httpsServer.stop() + @server.close() + ) + .then(open) + else + open() afterEach -> nock.cleanAll() diff --git a/packages/server/test/unit/browsers/electron_spec.coffee b/packages/server/test/unit/browsers/electron_spec.coffee index 2066151c51..dc7c1c19fa 100644 --- a/packages/server/test/unit/browsers/electron_spec.coffee +++ b/packages/server/test/unit/browsers/electron_spec.coffee @@ -2,6 +2,9 @@ require("../../spec_helper") _ = require("lodash") EE = require("events") +la = require("lazy-ass") +check = require("check-more-types") + menu = require("#{root}../lib/gui/menu") Windows = require("#{root}../lib/gui/windows") electron = require("#{root}../lib/browsers/electron") @@ -31,8 +34,10 @@ describe "lib/browsers/electron", -> context ".open", -> beforeEach -> @sandbox.stub(electron, "_render").resolves(@win) - state = savedState() - @sandbox.stub(state, "get").resolves(@state) + savedState() + .then (state) => + la(check.fn(state.get), "state is missing .get to stub", state) + @sandbox.stub(state, "get").resolves(@state) it "calls render with url, state, and options", -> electron.open("electron", @url, @options, @automation) diff --git a/packages/server/test/unit/config_spec.coffee b/packages/server/test/unit/config_spec.coffee index d53cf12064..dccded8593 100644 --- a/packages/server/test/unit/config_spec.coffee +++ b/packages/server/test/unit/config_spec.coffee @@ -2,6 +2,7 @@ require("../spec_helper") _ = require("lodash") path = require("path") +R = require("ramda") config = require("#{root}lib/config") configUtil = require("#{root}lib/util/config") scaffold = require("#{root}lib/scaffold") @@ -412,7 +413,10 @@ describe "lib/config", -> beforeEach -> @defaults = (prop, value, cfg = {}, options = {}) => cfg.projectRoot = "/foo/bar/" - expect(config.mergeDefaults(cfg, options)[prop]).to.deep.eq(value) + config.mergeDefaults(cfg, options) + .then R.prop(prop) + .then (result) -> + expect(result).to.deep.eq(value) it "port=null", -> @defaults "port", null @@ -504,29 +508,29 @@ describe "lib/config", -> @defaults "supportFile", false, {supportFile: false} it "resets numTestsKeptInMemory to 0 when headless", -> - cfg = config.mergeDefaults({projectRoot: "/foo/bar/"}, {isTextTerminal: true}) - - expect(cfg.numTestsKeptInMemory).to.eq(0) + config.mergeDefaults({projectRoot: "/foo/bar/"}, {isTextTerminal: true}) + .then (cfg) -> + expect(cfg.numTestsKeptInMemory).to.eq(0) it "resets watchForFileChanges to false when headless", -> - cfg = config.mergeDefaults({projectRoot: "/foo/bar/"}, {isTextTerminal: true}) - - expect(cfg.watchForFileChanges).to.be.false + config.mergeDefaults({projectRoot: "/foo/bar/"}, {isTextTerminal: true}) + .then (cfg) -> + expect(cfg.watchForFileChanges).to.be.false it "can override morgan in options", -> - cfg = config.mergeDefaults({projectRoot: "/foo/bar/"}, {morgan: false}) - - expect(cfg.morgan).to.be.false + config.mergeDefaults({projectRoot: "/foo/bar/"}, {morgan: false}) + .then (cfg) -> + expect(cfg.morgan).to.be.false it "can override isTextTerminal in options", -> - cfg = config.mergeDefaults({projectRoot: "/foo/bar/"}, {isTextTerminal: true}) - - expect(cfg.isTextTerminal).to.be.true + config.mergeDefaults({projectRoot: "/foo/bar/"}, {isTextTerminal: true}) + .then (cfg) -> + expect(cfg.isTextTerminal).to.be.true it "can override socketId in options", -> - cfg = config.mergeDefaults({projectRoot: "/foo/bar/"}, {socketId: 1234}) - - expect(cfg.socketId).to.eq(1234) + config.mergeDefaults({projectRoot: "/foo/bar/"}, {socketId: 1234}) + .then (cfg) -> + expect(cfg.socketId).to.eq(1234) it "deletes envFile", -> obj = { @@ -541,15 +545,15 @@ describe "lib/config", -> } } - cfg = config.mergeDefaults(obj) - - expect(cfg.environmentVariables).to.deep.eq({ - foo: "bar" - bar: "baz" - version: "1.0.1" - }) - expect(cfg.env).to.eq(process.env["CYPRESS_ENV"]) - expect(cfg).not.to.have.property("envFile") + config.mergeDefaults(obj) + .then (cfg) -> + expect(cfg.environmentVariables).to.deep.eq({ + foo: "bar" + bar: "baz" + version: "1.0.1" + }) + expect(cfg.env).to.eq(process.env["CYPRESS_ENV"]) + expect(cfg).not.to.have.property("envFile") it "merges env into @config.env", -> obj = { @@ -568,14 +572,14 @@ describe "lib/config", -> } } - cfg = config.mergeDefaults(obj, options) - - expect(cfg.environmentVariables).to.deep.eq({ - host: "localhost" - user: "brian" - version: "0.13.1" - foo: "bar" - }) + config.mergeDefaults(obj, options) + .then (cfg) -> + expect(cfg.environmentVariables).to.deep.eq({ + host: "localhost" + user: "brian" + version: "0.13.1" + foo: "bar" + }) describe ".resolved", -> it "sets reporter and port to cli", -> @@ -588,38 +592,38 @@ describe "lib/config", -> port: 1234 } - cfg = config.mergeDefaults(obj, options) - - expect(cfg.resolved).to.deep.eq({ - port: { value: 1234, from: "cli" }, - hosts: { value: null, from: "default" } - reporter: { value: "json", from: "cli" }, - reporterOptions: { value: null, from: "default" }, - baseUrl: { value: null, from: "default" }, - defaultCommandTimeout: { value: 4000, from: "default" }, - pageLoadTimeout: { value: 60000, from: "default" }, - requestTimeout: { value: 5000, from: "default" }, - responseTimeout: { value: 30000, from: "default" }, - execTimeout: { value: 60000, from: "default" }, - screenshotOnHeadlessFailure:{ value: true, from: "default" }, - numTestsKeptInMemory: { value: 50, from: "default" }, - waitForAnimations: { value: true, from: "default" }, - animationDistanceThreshold: { value: 5, from: "default" }, - trashAssetsBeforeHeadlessRuns: { value: true, from: "default" }, - watchForFileChanges: { value: true, from: "default" }, - chromeWebSecurity: { value: true, from: "default" }, - viewportWidth: { value: 1000, from: "default" }, - viewportHeight: { value: 660, from: "default" }, - fileServerFolder: { value: "", from: "default" }, - videoRecording: { value: true, from: "default" } - videoCompression: { value: 32, from: "default" } - videosFolder: { value: "cypress/videos", from: "default" }, - supportFile: { value: "cypress/support", from: "default" }, - fixturesFolder: { value: "cypress/fixtures", from: "default" }, - integrationFolder: { value: "cypress/integration", from: "default" }, - screenshotsFolder: { value: "cypress/screenshots", from: "default" }, - environmentVariables: { } - }) + config.mergeDefaults(obj, options) + .then (cfg) -> + expect(cfg.resolved).to.deep.eq({ + port: { value: 1234, from: "cli" }, + hosts: { value: null, from: "default" } + reporter: { value: "json", from: "cli" }, + reporterOptions: { value: null, from: "default" }, + baseUrl: { value: null, from: "default" }, + defaultCommandTimeout: { value: 4000, from: "default" }, + pageLoadTimeout: { value: 60000, from: "default" }, + requestTimeout: { value: 5000, from: "default" }, + responseTimeout: { value: 30000, from: "default" }, + execTimeout: { value: 60000, from: "default" }, + screenshotOnHeadlessFailure:{ value: true, from: "default" }, + numTestsKeptInMemory: { value: 50, from: "default" }, + waitForAnimations: { value: true, from: "default" }, + animationDistanceThreshold: { value: 5, from: "default" }, + trashAssetsBeforeHeadlessRuns: { value: true, from: "default" }, + watchForFileChanges: { value: true, from: "default" }, + chromeWebSecurity: { value: true, from: "default" }, + viewportWidth: { value: 1000, from: "default" }, + viewportHeight: { value: 660, from: "default" }, + fileServerFolder: { value: "", from: "default" }, + videoRecording: { value: true, from: "default" } + videoCompression: { value: 32, from: "default" } + videosFolder: { value: "cypress/videos", from: "default" }, + supportFile: { value: "cypress/support", from: "default" }, + fixturesFolder: { value: "cypress/fixtures", from: "default" }, + integrationFolder: { value: "cypress/integration", from: "default" }, + screenshotsFolder: { value: "cypress/screenshots", from: "default" }, + environmentVariables: { } + }) it "sets config, envFile and env", -> @sandbox.stub(config, "getProcessEnvVars").returns({quux: "quux"}) @@ -641,55 +645,55 @@ describe "lib/config", -> options = {} - cfg = config.mergeDefaults(obj, options) - - expect(cfg.resolved).to.deep.eq({ - port: { value: 2020, from: "config" }, - hosts: { value: null, from: "default" } - reporter: { value: "spec", from: "default" }, - reporterOptions: { value: null, from: "default" }, - baseUrl: { value: "http://localhost:8080", from: "config" }, - defaultCommandTimeout: { value: 4000, from: "default" }, - pageLoadTimeout: { value: 60000, from: "default" }, - requestTimeout: { value: 5000, from: "default" }, - responseTimeout: { value: 30000, from: "default" }, - execTimeout: { value: 60000, from: "default" }, - numTestsKeptInMemory: { value: 50, from: "default" }, - waitForAnimations: { value: true, from: "default" }, - animationDistanceThreshold: { value: 5, from: "default" }, - screenshotOnHeadlessFailure:{ value: true, from: "default" }, - trashAssetsBeforeHeadlessRuns: { value: true, from: "default" }, - watchForFileChanges: { value: true, from: "default" }, - chromeWebSecurity: { value: true, from: "default" }, - viewportWidth: { value: 1000, from: "default" }, - viewportHeight: { value: 660, from: "default" }, - fileServerFolder: { value: "", from: "default" }, - videoRecording: { value: true, from: "default" } - videoCompression: { value: 32, from: "default" } - videosFolder: { value: "cypress/videos", from: "default" }, - supportFile: { value: "cypress/support", from: "default" }, - fixturesFolder: { value: "cypress/fixtures", from: "default" }, - integrationFolder: { value: "cypress/integration", from: "default" }, - screenshotsFolder: { value: "cypress/screenshots", from: "default" }, - environmentVariables: { - foo: { - value: "foo" - from: "config" + config.mergeDefaults(obj, options) + .then (cfg) -> + expect(cfg.resolved).to.deep.eq({ + port: { value: 2020, from: "config" }, + hosts: { value: null, from: "default" } + reporter: { value: "spec", from: "default" }, + reporterOptions: { value: null, from: "default" }, + baseUrl: { value: "http://localhost:8080", from: "config" }, + defaultCommandTimeout: { value: 4000, from: "default" }, + pageLoadTimeout: { value: 60000, from: "default" }, + requestTimeout: { value: 5000, from: "default" }, + responseTimeout: { value: 30000, from: "default" }, + execTimeout: { value: 60000, from: "default" }, + numTestsKeptInMemory: { value: 50, from: "default" }, + waitForAnimations: { value: true, from: "default" }, + animationDistanceThreshold: { value: 5, from: "default" }, + screenshotOnHeadlessFailure:{ value: true, from: "default" }, + trashAssetsBeforeHeadlessRuns: { value: true, from: "default" }, + watchForFileChanges: { value: true, from: "default" }, + chromeWebSecurity: { value: true, from: "default" }, + viewportWidth: { value: 1000, from: "default" }, + viewportHeight: { value: 660, from: "default" }, + fileServerFolder: { value: "", from: "default" }, + videoRecording: { value: true, from: "default" } + videoCompression: { value: 32, from: "default" } + videosFolder: { value: "cypress/videos", from: "default" }, + supportFile: { value: "cypress/support", from: "default" }, + fixturesFolder: { value: "cypress/fixtures", from: "default" }, + integrationFolder: { value: "cypress/integration", from: "default" }, + screenshotsFolder: { value: "cypress/screenshots", from: "default" }, + environmentVariables: { + foo: { + value: "foo" + from: "config" + } + bar: { + value: "bar" + from: "envFile" + } + baz: { + value: "baz" + from: "cli" + } + quux: { + value: "quux" + from: "env" + } } - bar: { - value: "bar" - from: "envFile" - } - baz: { - value: "baz" - from: "cli" - } - quux: { - value: "quux" - from: "env" - } - } - }) + }) context ".parseEnv", -> it "merges together env from config, env from file, env from process, and env from CLI", -> @@ -799,8 +803,9 @@ describe "lib/config", -> obj = { projectRoot: "/_test-output/path/to/project" } - - expect(config.setSupportFileAndFolder(obj)).to.eql(obj) + config.setSupportFileAndFolder(obj) + .then (result) -> + expect(result).to.eql(obj) it "sets the full path to the supportFile and supportFolder if it exists", -> projectRoot = process.cwd() @@ -810,14 +815,15 @@ describe "lib/config", -> supportFile: "test/unit/config_spec.coffee" }) - expect(config.setSupportFileAndFolder(obj)).to.eql({ - projectRoot: projectRoot - supportFile: "#{projectRoot}/test/unit/config_spec.coffee" - supportFolder: "#{projectRoot}/test/unit" - }) + config.setSupportFileAndFolder(obj) + .then (result) -> + expect(result).to.eql({ + projectRoot: projectRoot + supportFile: "#{projectRoot}/test/unit/config_spec.coffee" + supportFolder: "#{projectRoot}/test/unit" + }) it "sets the supportFile to default index.js if it does not exist, support folder does not exist, and supportFile is the default", -> - @sandbox.stub(fs, "existsSync").returns(false) projectRoot = process.cwd() obj = config.setAbsolutePaths({ @@ -825,11 +831,13 @@ describe "lib/config", -> supportFile: "cypress/support" }) - expect(config.setSupportFileAndFolder(obj)).to.eql({ - projectRoot: projectRoot - supportFile: "#{projectRoot}/cypress/support/index.js" - supportFolder: "#{projectRoot}/cypress/support" - }) + config.setSupportFileAndFolder(obj) + .then (result) -> + expect(result).to.eql({ + projectRoot: projectRoot + supportFile: "#{projectRoot}/cypress/support/index.js" + supportFolder: "#{projectRoot}/cypress/support" + }) it "sets the supportFile to false if it does not exist, support folder exists, and supportFile is the default", -> projectRoot = path.join(process.cwd(), "test/support/fixtures/projects/blank-support") @@ -839,10 +847,12 @@ describe "lib/config", -> supportFile: "cypress/support" }) - expect(config.setSupportFileAndFolder(obj)).to.eql({ - projectRoot: projectRoot - supportFile: false - }) + config.setSupportFileAndFolder(obj) + .then (result) -> + expect(result).to.eql({ + projectRoot: projectRoot + supportFile: false + }) it "throws error if supportFile is not default and does not exist", -> projectRoot = process.cwd() @@ -852,7 +862,9 @@ describe "lib/config", -> supportFile: "does/not/exist" }) - expect(-> config.setSupportFileAndFolder(obj)).to.throw("Support file missing or invalid.") + config.setSupportFileAndFolder(obj) + .catch (err) -> + expect(err.message).to.include("Support file missing or invalid.") context ".setParentTestsPaths", -> it "sets parentTestsFolder and parentTestsFolderDisplay", -> diff --git a/packages/server/test/unit/gui/windows_spec.coffee b/packages/server/test/unit/gui/windows_spec.coffee index ff793b1e26..4ae55dc50d 100644 --- a/packages/server/test/unit/gui/windows_spec.coffee +++ b/packages/server/test/unit/gui/windows_spec.coffee @@ -1,6 +1,7 @@ require("../../spec_helper") _ = require("lodash") +delay = require("delay") EE = require("events").EventEmitter BrowserWindow = require("electron").BrowserWindow Windows = require("#{root}../lib/gui/windows") @@ -48,17 +49,18 @@ describe "lib/gui/windows", -> context ".trackState", -> beforeEach -> - @state = savedState() - @sandbox.stub(@state, "set") + savedState() + .then (@state) => + @sandbox.stub(@state, "set") - @projectPath = undefined - @keys = { - width: "theWidth" - height: "someHeight" - x: "anX" - y: "aY" - devTools: "whatsUpWithDevTools" - } + @projectPath = undefined + @keys = { + width: "theWidth" + height: "someHeight" + x: "anX" + y: "aY" + devTools: "whatsUpWithDevTools" + } it "saves size and position when window resizes, debounced", -> ## tried using useFakeTimers here, but it didn't work for some @@ -69,20 +71,23 @@ describe "lib/gui/windows", -> @win.emit("resize") expect(_.debounce).to.be.called - expect(@state.set).to.be.calledWith({ - theWidth: 1 - someHeight: 2 - anX: 3 - aY: 4 - }) + delay(100) + .then () => + expect(@state.set).to.be.calledWith({ + theWidth: 1 + someHeight: 2 + anX: 3 + aY: 4 + }) it "returns if window isDestroyed on resize", -> @win.isDestroyed.returns(true) Windows.trackState(@projectPath, @win, @keys) @win.emit("resize") - - expect(@state.set).not.to.be.called + delay(100) + .then () => + expect(@state.set).not.to.be.called it "saves position when window moves, debounced", -> ## tried using useFakeTimers here, but it didn't work for some @@ -91,10 +96,12 @@ describe "lib/gui/windows", -> Windows.trackState(@projectPath, @win, @keys) @win.emit("moved") - expect(@state.set).to.be.calledWith({ - anX: 3 - aY: 4 - }) + delay(100) + .then () => + expect(@state.set).to.be.calledWith({ + anX: 3 + aY: 4 + }) it "returns if window isDestroyed on moved", -> @win.isDestroyed.returns(true) @@ -102,19 +109,25 @@ describe "lib/gui/windows", -> Windows.trackState(@projectPath, @win, @keys) @win.emit("moved") - expect(@state.set).not.to.be.called + delay(100) + .then () => + expect(@state.set).not.to.be.called it "saves dev tools state when opened", -> Windows.trackState(@projectPath, @win, @keys) @win.webContents.emit("devtools-opened") - expect(@state.set).to.be.calledWith({whatsUpWithDevTools: true}) + delay(100) + .then () => + expect(@state.set).to.be.calledWith({whatsUpWithDevTools: true}) it "saves dev tools state when closed", -> Windows.trackState(@projectPath, @win, @keys) @win.webContents.emit("devtools-closed") - expect(@state.set).to.be.calledWith({whatsUpWithDevTools: false}) + delay(100) + .then () => + expect(@state.set).to.be.calledWith({whatsUpWithDevTools: false}) context ".automation", -> beforeEach -> diff --git a/packages/server/test/unit/misc_spec.coffee b/packages/server/test/unit/misc_spec.coffee new file mode 100644 index 0000000000..89f8147ad7 --- /dev/null +++ b/packages/server/test/unit/misc_spec.coffee @@ -0,0 +1,22 @@ +require("../spec_helper") + +describe "misc tests", -> + beforeEach () -> + @sandbox.spy(console, "error") + + it "warns when trying to use fs.existsSync", -> + fs.existsSync(__filename) + warning = "WARNING: fs sync methods can fail due to EMFILE errors" + expect(console.error).to.be.calledWith(warning) + # also print stack trace, maybe check that + + context "fs.pathExists", -> + it "finds this file", -> + fs.pathExists(__filename) + .then (found) -> + expect(found).to.be.true + + it "does not find non-existent file", -> + fs.pathExists('does-not-exist') + .then (found) -> + expect(found).to.be.false diff --git a/packages/server/test/unit/project_spec.coffee b/packages/server/test/unit/project_spec.coffee index 9cda2b0d29..d9650b30ab 100644 --- a/packages/server/test/unit/project_spec.coffee +++ b/packages/server/test/unit/project_spec.coffee @@ -29,8 +29,9 @@ describe "lib/project", -> settings.read(@todosPath).then (obj = {}) => {@projectId} = obj - @config = config.set({projectName: "project", projectRoot: "/foo/bar"}) - @project = Project(@todosPath) + config.set({projectName: "project", projectRoot: "/foo/bar"}) + .then (@config) => + @project = Project(@todosPath) afterEach -> Fixtures.remove() @@ -51,10 +52,12 @@ describe "lib/project", -> @sandbox.stub(config, "get").withArgs(@todosPath).resolves({ integrationFolder }) @sandbox.stub(@project, "determineIsNewProject").withArgs(integrationFolder).resolves(false) @project.cfg = { integrationFolder } - savedState(@project.projectRoot).remove() + savedState(@project.projectRoot) + .then (state) -> state.remove() afterEach -> - savedState(@project.projectRoot).remove() + savedState(@project.projectRoot) + .then (state) -> state.remove() it "saves state without modification", -> @project.saveState() @@ -88,18 +91,19 @@ describe "lib/project", -> @sandbox.stub(@project, "determineIsNewProject").withArgs(integrationFolder).resolves(false) it "calls config.get with projectRoot + options + saved state", -> - state = savedState(@todosPath) - @sandbox.stub(state, "get").resolves({ reporterWidth: 225 }) - @project.getConfig({foo: "bar"}) - .then (cfg) -> - expect(cfg).to.deep.eq({ - integrationFolder - isNewProject: false - baz: "quux" - state: { - reporterWidth: 225 - } - }) + savedState(@todosPath) + .then (state) => + @sandbox.stub(state, "get").resolves({ reporterWidth: 225 }) + @project.getConfig({foo: "bar"}) + .then (cfg) -> + expect(cfg).to.deep.eq({ + integrationFolder + isNewProject: false + baz: "quux" + state: { + reporterWidth: 225 + } + }) it "resolves if cfg is already set", -> @project.cfg = { @@ -115,19 +119,20 @@ describe "lib/project", -> }) it "sets cfg.isNewProject to true when state.showedOnBoardingModal is true", -> - state = savedState(@todosPath) - @sandbox.stub(state, "get").resolves({ showedOnBoardingModal: true }) + savedState(@todosPath) + .then (state) => + @sandbox.stub(state, "get").resolves({ showedOnBoardingModal: true }) - @project.getConfig({foo: "bar"}) - .then (cfg) -> - expect(cfg).to.deep.eq({ - integrationFolder - isNewProject: false - baz: "quux" - state: { - showedOnBoardingModal: true - } - }) + @project.getConfig({foo: "bar"}) + .then (cfg) -> + expect(cfg).to.deep.eq({ + integrationFolder + isNewProject: false + baz: "quux" + state: { + showedOnBoardingModal: true + } + }) context "#open", -> beforeEach -> @@ -143,7 +148,7 @@ describe "lib/project", -> @project.open(opts).then => expect(@project.watchSettingsAndStartWebsockets).to.be.calledWith(opts, @project.cfg) - it "calls #scaffold with server config", -> + it "calls #scaffold with server config promise", -> @project.open().then => expect(@project.scaffold).to.be.calledWith(@config) @@ -281,7 +286,7 @@ describe "lib/project", -> context "#watchSupportFile", -> beforeEach -> - @sandbox.stub(fs, "existsSync").returns(true) + @sandbox.stub(fs, "pathExists").resolves(true) @project = Project("/_test-output/path/to/project") @project.server = {onTestFileChange: @sandbox.spy()} @watchBundle = @sandbox.stub(@project.watchers, "watchBundle").resolves() @@ -297,24 +302,26 @@ describe "lib/project", -> it "calls watchers.watchBundle with relative path to file", -> @project.watchSupportFile(@config) - - expect(@watchBundle).to.be.calledWith("foo/bar.js", @config) + .then () => + expect(@watchBundle).to.be.calledWith("foo/bar.js", @config) it "calls server.onTestFileChange when file changes", -> @project.watchSupportFile(@config) - @watchBundle.firstCall.args[2].onChange() - - expect(@project.server.onTestFileChange).to.be.calledWith("foo/bar.js") + .then () => + @watchBundle.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) - - expect(@watchBundle.firstCall.args[2]).to.be.undefined + .then () => + expect(@watchBundle.firstCall.args[2]).to.be.undefined it "throws when support file does not exist", -> - fs.existsSync.returns(false) - expect(=> @project.watchSupportFile(@config)).to.throw("Support file missing or invalid.") + fs.pathExists.resolves(false) + @project.watchSupportFile(@config) + .catch (e) -> + expect(e.message).to.include("Support file missing or invalid.") context "#watchSettingsAndStartWebsockets", -> beforeEach -> diff --git a/packages/server/test/unit/saved_state_spec.coffee b/packages/server/test/unit/saved_state_spec.coffee index ec13230011..33a7966f04 100644 --- a/packages/server/test/unit/saved_state_spec.coffee +++ b/packages/server/test/unit/saved_state_spec.coffee @@ -35,18 +35,24 @@ describe "lib/saved_state", -> it "is a function", -> expect(savedState).to.be.a("function") - it "returns an instance of FileUtil", -> - expect(savedState()).to.be.instanceof(FileUtil) + it "resolves with an instance of FileUtil", -> + savedState() + .then (state) -> + expect(state).to.be.instanceof(FileUtil) it "sets file path to app data path and file name to 'state'", -> - statePath = savedState().path - expected = path.join(appData.path(), "projects", "__global__", "state.json") - expect(statePath).to.equal(expected) + savedState() + .then (state) -> + statePath = state.path + expected = path.join(appData.path(), "projects", "__global__", "state.json") + expect(statePath).to.equal(expected) it "caches state file instance per path", -> - a = savedState("/foo/bar") - b = savedState("/foo/bar") - expect(a).to.equal(b) + Promise.all([ + savedState("/foo/bar"), + savedState("/foo/bar") + ]).spread (a, b) -> + expect(a).to.equal(b) it "returns different state file for different path", -> a = savedState("/foo/bar") diff --git a/packages/server/test/unit/server_spec.coffee b/packages/server/test/unit/server_spec.coffee index 1fd19af6d0..67e8e7ec1c 100644 --- a/packages/server/test/unit/server_spec.coffee +++ b/packages/server/test/unit/server_spec.coffee @@ -16,8 +16,10 @@ mockery.registerMock("morgan", -> morganFn) describe "lib/server", -> beforeEach -> - @config = config.set({projectRoot: "/foo/bar/"}) - @server = Server() + config.set({projectRoot: "/foo/bar/"}) + .then (cfg) => + @config = cfg + @server = Server() afterEach -> @server and @server.close() diff --git a/packages/server/test/unit/settings_spec.coffee b/packages/server/test/unit/settings_spec.coffee index 011197bca1..e9c6f8a5de 100644 --- a/packages/server/test/unit/settings_spec.coffee +++ b/packages/server/test/unit/settings_spec.coffee @@ -1,6 +1,7 @@ require("../spec_helper") path = require("path") +R = require("ramda") settings = require("#{root}lib/util/settings") projectRoot = process.cwd() @@ -46,11 +47,16 @@ describe "lib/settings", -> expect(err.message).to.include("SyntaxError") expect(err.message).to.include(projectRoot) + noArguments = R.nAry(0) + it "does not write initial file", -> settings.readEnv(projectRoot) .then (obj) -> expect(obj).to.deep.eq({}) - expect(fs.existsSync("cypress.env.json")).to.be.false + .then () -> + fs.pathExists("cypress.env.json") + .then (found) -> + expect(found).to.be.false context ".id", -> beforeEach ->