From a4721ff5965f8c109d6cd0073bab55cab4e5b9be Mon Sep 17 00:00:00 2001 From: Paul Edwin Date: Fri, 26 Apr 2019 16:49:55 +0100 Subject: [PATCH 1/2] address #3069 by using chokidar fs events --- packages/server/lib/open_project.coffee | 21 +++++++++++++-------- packages/server/lib/watchers.coffee | 4 +--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/server/lib/open_project.coffee b/packages/server/lib/open_project.coffee index 6debba933add..f0532b5a5880 100644 --- a/packages/server/lib/open_project.coffee +++ b/packages/server/lib/open_project.coffee @@ -2,17 +2,19 @@ _ = require("lodash") la = require("lazy-ass") debug = require("debug")("cypress:server:openproject") Promise = require("bluebird") +path = require("path") files = require("./controllers/files") config = require("./config") Project = require("./project") browsers = require("./browsers") +Watchers = require("./watchers") specsUtil = require("./util/specs") preprocessor = require("./plugins/preprocessor") create = -> openProject = null - specIntervalId = null relaunchBrowser = null + watchers = null reset = -> openProject = null @@ -128,7 +130,7 @@ create = -> checkForSpecUpdates = => if not openProject - return @clearSpecInterval() + return @stopSpecWatcher() get() .then(sendIfChanged) @@ -137,6 +139,12 @@ create = -> get = -> openProject.getConfig() .then (cfg) -> + if !watchers + watchers = Watchers() + watchers.watch(path.join(cfg.integrationFolder, cfg.testFiles), { + onChange: => + checkForSpecUpdates() + }) specsUtil.find(cfg) .then (specs = []) -> ## TODO: put back 'integration' property @@ -145,15 +153,12 @@ create = -> integration: specs } - specIntervalId = setInterval(checkForSpecUpdates, 2500) ## immediately check the first time around checkForSpecUpdates() - clearSpecInterval: -> - if specIntervalId - clearInterval(specIntervalId) - specIntervalId = null + stopSpecWatcher: -> + watchers?.close() closeBrowser: -> browsers.close() @@ -171,7 +176,7 @@ create = -> close: -> debug("closing opened project") - @clearSpecInterval() + @stopSpecWatcher() @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 From bb74cf7ea2b509d4ab8357720c456822926db834 Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Tue, 7 May 2019 15:27:21 -0400 Subject: [PATCH 2/2] use chokidar directly for watching specs and add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the watchers lib watches for file contents changing and doesn’t watch for files being added or removed --- packages/server/lib/open_project.coffee | 40 +++++++++++------ .../server/test/unit/open_project_spec.coffee | 44 ++++++++++++++++++- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/packages/server/lib/open_project.coffee b/packages/server/lib/open_project.coffee index f0532b5a5880..12f4ad210a83 100644 --- a/packages/server/lib/open_project.coffee +++ b/packages/server/lib/open_project.coffee @@ -3,18 +3,18 @@ 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") browsers = require("./browsers") -Watchers = require("./watchers") specsUtil = require("./util/specs") preprocessor = require("./plugins/preprocessor") create = -> openProject = null relaunchBrowser = null - watchers = null + specsWatcher = null reset = -> openProject = null @@ -128,23 +128,34 @@ create = -> currentSpecs = specs options.onChange(specs) - checkForSpecUpdates = => + checkForSpecUpdates = _.debounce => if not openProject - return @stopSpecWatcher() + 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) -> - if !watchers - watchers = Watchers() - watchers.watch(path.join(cfg.integrationFolder, cfg.testFiles), { - onChange: => - checkForSpecUpdates() - }) + createSpecsWatcher(cfg) specsUtil.find(cfg) .then (specs = []) -> ## TODO: put back 'integration' property @@ -153,12 +164,13 @@ create = -> integration: specs } - ## immediately check the first time around checkForSpecUpdates() - stopSpecWatcher: -> - watchers?.close() + stopSpecsWatcher: -> + debug("stop spec watcher") + Promise.try -> + specsWatcher?.close() closeBrowser: -> browsers.close() @@ -176,7 +188,7 @@ create = -> close: -> debug("closing opened project") - @stopSpecWatcher() + @stopSpecsWatcher() @closeOpenProjectAndBrowsers() create: (path, args = {}, options = {}) -> 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