diff --git a/packages/server/lib/open_project.coffee b/packages/server/lib/open_project.coffee index 6debba933add..12f4ad210a83 100644 --- a/packages/server/lib/open_project.coffee +++ b/packages/server/lib/open_project.coffee @@ -2,6 +2,8 @@ _ = require("lodash") la = require("lazy-ass") debug = require("debug")("cypress:server:openproject") Promise = require("bluebird") +path = require("path") +chokidar = require("chokidar") files = require("./controllers/files") config = require("./config") Project = require("./project") @@ -11,8 +13,8 @@ preprocessor = require("./plugins/preprocessor") create = -> openProject = null - specIntervalId = null relaunchBrowser = null + specsWatcher = null reset = -> openProject = null @@ -126,17 +128,34 @@ create = -> currentSpecs = specs options.onChange(specs) - checkForSpecUpdates = => + checkForSpecUpdates = _.debounce => if not openProject - return @clearSpecInterval() + return @stopSpecsWatcher() + + debug("check for spec updates") get() .then(sendIfChanged) .catch(options.onError) + , 250, { leading: true } + + createSpecsWatcher = (cfg) -> + return if specsWatcher + + debug("watch test files: %s in %s", cfg.testFiles, cfg.integrationFolder) + + specsWatcher = chokidar.watch(cfg.testFiles, { + cwd: cfg.integrationFolder + ignored: cfg.ignoreTestFiles + ignoreInitial: true + }) + specsWatcher.on("add", checkForSpecUpdates) + specsWatcher.on("unlink", checkForSpecUpdates) get = -> openProject.getConfig() .then (cfg) -> + createSpecsWatcher(cfg) specsUtil.find(cfg) .then (specs = []) -> ## TODO: put back 'integration' property @@ -145,15 +164,13 @@ create = -> integration: specs } - specIntervalId = setInterval(checkForSpecUpdates, 2500) - ## immediately check the first time around checkForSpecUpdates() - clearSpecInterval: -> - if specIntervalId - clearInterval(specIntervalId) - specIntervalId = null + stopSpecsWatcher: -> + debug("stop spec watcher") + Promise.try -> + specsWatcher?.close() closeBrowser: -> browsers.close() @@ -171,7 +188,7 @@ create = -> close: -> debug("closing opened project") - @clearSpecInterval() + @stopSpecsWatcher() @closeOpenProjectAndBrowsers() create: (path, args = {}, options = {}) -> diff --git a/packages/server/lib/watchers.coffee b/packages/server/lib/watchers.coffee index a741e841d7e4..ccef3282598b 100644 --- a/packages/server/lib/watchers.coffee +++ b/packages/server/lib/watchers.coffee @@ -16,9 +16,7 @@ class Watchers watch: (filePath, options = {}) -> _.defaults options, - interval: 250 - usePolling: true - useFsEvents: false + useFsEvents: true ignored: null onChange: null onReady: null diff --git a/packages/server/test/unit/open_project_spec.coffee b/packages/server/test/unit/open_project_spec.coffee index 4b3d82ffbbb7..4328f1e75ce0 100644 --- a/packages/server/test/unit/open_project_spec.coffee +++ b/packages/server/test/unit/open_project_spec.coffee @@ -1,5 +1,6 @@ require("../spec_helper") +chokidar = require("chokidar") browsers = require("#{root}lib/browsers") Project = require("#{root}lib/project") openProject = require("#{root}lib/open_project") @@ -11,13 +12,18 @@ describe "lib/open_project", -> reset: sinon.stub() use: sinon.stub() } + @config = { + integrationFolder: "/user/foo/cypress/integration" + testFiles: "**/*.*" + ignoreTestFiles: "**/*.nope" + } sinon.stub(browsers, "get").resolves() sinon.stub(browsers, "open") sinon.stub(Project.prototype, "open").resolves() sinon.stub(Project.prototype, "reset").resolves() sinon.stub(Project.prototype, "getSpecUrl").resolves() - sinon.stub(Project.prototype, "getConfig").resolves({}) + sinon.stub(Project.prototype, "getConfig").resolves(@config) sinon.stub(Project.prototype, "getAutomation").returns(@automation) sinon.stub(preprocessor, "removeFile") @@ -64,3 +70,39 @@ describe "lib/open_project", -> .then => expect(@browser.isHeaded).to.be.true expect(@browser.isHeadless).to.be.false + + context "#getSpecChanges", -> + beforeEach -> + @watcherStub = { + on: sinon.stub() + } + sinon.stub(chokidar, "watch").returns(@watcherStub) + + it "watches spec files", -> + openProject.getSpecChanges({}).then => + expect(chokidar.watch).to.be.calledWith(@config.testFiles, { + cwd: @config.integrationFolder + ignored: @config.ignoreTestFiles + ignoreInitial: true + }) + + it "calls onChange callback when file is added", -> + onChange = sinon.spy() + @watcherStub.on.withArgs("add").yields() + openProject.getSpecChanges({ onChange }).then => + expect(onChange).to.be.called + + it "calls onChange callback when file is removed", -> + onChange = sinon.spy() + @watcherStub.on.withArgs("unlink").yields() + openProject.getSpecChanges({ onChange }).then => + expect(onChange).to.be.called + + it "only calls onChange once if there are multiple changes in a row", -> + onChange = sinon.spy() + @watcherStub.on.withArgs("unlink").yields() + @watcherStub.on.withArgs("add").yields() + @watcherStub.on.withArgs("unlink").yields() + @watcherStub.on.withArgs("add").yields() + openProject.getSpecChanges({ onChange }).then => + expect(onChange).to.be.calledOnce