From b598106de1295f3e1e58338a8eca2b60f99175c3 Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Tue, 30 Jul 2013 18:10:31 -0700 Subject: [PATCH] feat(runner): allow passing changed/added/removed files This is a feature mostly for WebStorm, which does watch the FS already and so we don't wanna do it twice. WebStorm can pass list of changed files to Karma, so that Karma does not have to re-glob all the patterns. --- lib/cli.js | 11 ++++++++ lib/helper.js | 1 + lib/middleware/runner.js | 37 +++++++++++++++++++++---- lib/runner.js | 7 ++++- test/unit/cli.spec.coffee | 12 ++++++++ test/unit/middleware/runner.spec.coffee | 37 +++++++++++++++++++++++++ 6 files changed, 99 insertions(+), 6 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index a76ab37d3..cfb927bcd 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -51,6 +51,17 @@ var processArgs = function(argv, options) { options.reporters = options.reporters.split(','); } + if (helper.isString(options.removedFiles)) { + options.removedFiles = options.removedFiles.split(','); + } + + if (helper.isString(options.addedFiles)) { + options.addedFiles = options.addedFiles.split(','); + } + + if (helper.isString(options.changedFiles)) { + options.changedFiles = options.changedFiles.split(','); + } options.configFile = path.resolve(argv._.shift() || 'karma.conf.js'); return options; diff --git a/lib/helper.js b/lib/helper.js index 707b3329b..30fc409ea 100644 --- a/lib/helper.js +++ b/lib/helper.js @@ -17,6 +17,7 @@ exports.isDefined = function(value) { exports.isFunction = _.isFunction; exports.isString = _.isString; exports.isObject = _.isObject; +exports.isArray = _.isArray; var ABS_URL = /^https?:\/\//; exports.isUrlAbsolute = function(url) { diff --git a/lib/middleware/runner.js b/lib/middleware/runner.js index 1a195c3ff..d4b2f187d 100644 --- a/lib/middleware/runner.js +++ b/lib/middleware/runner.js @@ -4,6 +4,8 @@ * It basically triggers a test run and streams stdout back. */ +var path = require('path'); +var helper = require('../helper'); var log = require('../logger').create(); var constant = require('../constants'); var json = require('connect').json(); @@ -43,12 +45,37 @@ var createRunnerMiddleware = function(emitter, fileList, capturedBrowsers, repor }); }); - var clientArgs = request.body.args; - log.debug('Setting client.args to ', clientArgs); - config.client.args = clientArgs; + var data = request.body; + log.debug('Setting client.args to ', data.args); + config.client.args = data.args; - log.debug('Refreshing all the files / patterns'); - fileList.refresh(); + var fullRefresh = true; + + if (helper.isArray(data.changedFiles)) { + data.changedFiles.forEach(function(filepath) { + fileList.changeFile(path.resolve(config.basePath, filepath)); + fullRefresh = false; + }); + } + + if (helper.isArray(data.addedFiles)) { + data.addedFiles.forEach(function(filepath) { + fileList.addFile(path.resolve(config.basePath, filepath)); + fullRefresh = false; + }); + } + + if (helper.isArray(data.removedFiles)) { + data.removedFiles.forEach(function(filepath) { + fileList.removeFile(path.resolve(config.basePath, filepath)); + fullRefresh = false; + }); + } + + if (fullRefresh) { + log.debug('Refreshing all the files / patterns'); + fileList.refresh(); + } }); }; }; diff --git a/lib/runner.js b/lib/runner.js index c1814c7ed..bce673005 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -58,5 +58,10 @@ exports.run = function(config, done) { } }); - request.end(JSON.stringify({args: config.clientArgs})); + request.end(JSON.stringify({ + args: config.clientArgs, + removedFiles: config.removedFiles, + changedFiles: config.changedFiles, + addedFiles: config.addedFiles + })); }; diff --git a/test/unit/cli.spec.coffee b/test/unit/cli.spec.coffee index 18842f090..5dae993b0 100644 --- a/test/unit/cli.spec.coffee +++ b/test/unit/cli.spec.coffee @@ -81,6 +81,18 @@ describe 'cli', -> expect(options.reporters).to.deep.equal ['dots'] + it 'should parse removed/added/changed files to array', -> + options = processArgs [ + '--removed-files', 'r1.js,r2.js', + '--changed-files', 'ch1.js,ch2.js', + '--added-files', 'a1.js,a2.js' + ] + + expect(options.removedFiles).to.deep.equal ['r1.js', 'r2.js'] + expect(options.addedFiles).to.deep.equal ['a1.js', 'a2.js'] + expect(options.changedFiles).to.deep.equal ['ch1.js', 'ch2.js'] + + describe 'parseClientArgs', -> it 'should return arguments after --', -> args = cli.parseClientArgs ['node', 'karma.js', 'runArg', '--flag', '--', '--foo', '--bar', diff --git a/test/unit/middleware/runner.spec.coffee b/test/unit/middleware/runner.spec.coffee index 140ca6772..03d29693c 100644 --- a/test/unit/middleware/runner.spec.coffee +++ b/test/unit/middleware/runner.spec.coffee @@ -11,6 +11,7 @@ describe 'middleware.runner', -> createRunnerMiddleware = require('../../../lib/middleware/runner').create handler = nextSpy = response = mockReporter = capturedBrowsers = emitter = config = null + fileListMock = null beforeEach -> mockReporter = @@ -21,6 +22,10 @@ describe 'middleware.runner', -> capturedBrowsers = new BrowserCollection emitter fileListMock = refresh: -> emitter.emit 'run_start' + addFile: -> null + removeFile: -> null + changeFile: -> null + nextSpy = sinon.spy() response = new HttpResponseMock config = {client: {}} @@ -73,6 +78,38 @@ describe 'middleware.runner', -> request.emit 'end' + it 'should refresh explicit files if specified', (done) -> + capturedBrowsers.add new Browser + sinon.stub capturedBrowsers, 'areAllReady', -> true + sinon.stub fileListMock, 'refresh' + sinon.stub fileListMock, 'addFile' + sinon.stub fileListMock, 'changeFile' + sinon.stub fileListMock, 'removeFile' + + request = new HttpRequestMock '/__run__', { + 'content-type': 'application/json' + 'content-length': 1 + } + request.setEncoding = -> null + + handler request, response, nextSpy + message = + addedFiles: ['/new.js'] + removedFiles: ['/foo.js', '/bar.js'] + changedFiles: ['/changed.js'] + + request.emit 'data', JSON.stringify(message) + request.emit 'end' + + process.nextTick -> + expect(fileListMock.refresh).not.to.have.been.called + expect(fileListMock.addFile).to.have.been.calledWith '/new.js' + expect(fileListMock.removeFile).to.have.been.calledWith '/foo.js' + expect(fileListMock.removeFile).to.have.been.calledWith '/bar.js' + expect(fileListMock.changeFile).to.have.been.calledWith '/changed.js' + done() + + it 'should ignore other urls', (done) -> handler new HttpRequestMock('/something'), response, -> expect(response).to.beNotServed()