Comparing version 0.11.10 to 0.11.11-dev
@@ -0,1 +1,12 @@ | ||
<a name="v0.11.11"></a> | ||
### v0.11.11 (2013-12-23) | ||
#### Bug Fixes | ||
* **events:** resolve async events without any listener ([4e4bba88](http://github.com/karma-runner/karma/commit/4e4bba8803d1e4f461e568cc2e2ccf82e369721d)) | ||
* **launcher:** | ||
* compatibility with Node v0.8 ([6a46be96](http://github.com/karma-runner/karma/commit/6a46be96499876e9aa0892325d783627bd1c535d)) | ||
* compatibility with old launchers ([ffb74800](http://github.com/karma-runner/karma/commit/ffb74800638417910f453e108c8a4c6ffabaee29)) | ||
<a name="v0.11.10"></a> | ||
@@ -2,0 +13,0 @@ ### v0.11.10 (2013-12-22) |
@@ -173,2 +173,6 @@ var stringify = require('./stringify'); | ||
var UNIMPLEMENTED_START = function() { | ||
this.error('You need to include some adapter that implements __karma__.start method!'); | ||
}; | ||
// all files loaded, let's start the execution | ||
@@ -182,3 +186,3 @@ this.loaded = function() { | ||
// remove reference to child iframe | ||
this.start = null; | ||
this.start = UNIMPLEMENTED_START; | ||
}; | ||
@@ -204,5 +208,3 @@ | ||
// TODO(vojta): support multiple callbacks (queue) | ||
this.start = function() { | ||
this.error('You need to include some adapter that implements __karma__.start method!'); | ||
}; | ||
this.start = UNIMPLEMENTED_START; | ||
@@ -209,0 +211,0 @@ socket.on('execute', function(cfg) { |
@@ -199,3 +199,3 @@ var path = require('path'); | ||
this.autoWatchBatchDelay = 250; | ||
this.usePolling = false; | ||
this.usePolling = process.platform === 'darwin'; | ||
this.reporters = ['progress']; | ||
@@ -202,0 +202,0 @@ this.singleRun = false; |
@@ -78,2 +78,6 @@ var events = require('events'); | ||
if (!pending) { | ||
deferred.resolve(); | ||
} | ||
return deferred.promise; | ||
@@ -80,0 +84,0 @@ }; |
@@ -71,2 +71,4 @@ var fs = require('fs'); | ||
var errors = []; | ||
var resolveFiles = function(buckets) { | ||
@@ -103,7 +105,12 @@ var uniqueMap = {}; | ||
pendingDeferred.resolve(files || resolveFiles(self.buckets)); | ||
if (!errors.length) { | ||
pendingDeferred.resolve(files || resolveFiles(self.buckets)); | ||
} else { | ||
pendingDeferred.reject(); | ||
} | ||
pendingDeferred = pendingTimeout = null; | ||
}; | ||
var fireEventAndDefer = function() { | ||
var fireEventAndDefer = function(error) { | ||
if (pendingTimeout) { | ||
@@ -148,2 +155,4 @@ clearTimeout(pendingTimeout); | ||
errors = []; | ||
if (!pendingDeferred) { | ||
@@ -199,4 +208,9 @@ pendingDeferred = q.defer(); | ||
preprocess(file, function() { | ||
preprocess(file, function(err) { | ||
buckets[i].push(file); | ||
if (err) { | ||
errors.push(path); | ||
} | ||
finish(); | ||
@@ -286,5 +300,12 @@ }); | ||
return preprocess(addedFile, function() { | ||
return preprocess(addedFile, function(err) { | ||
// TODO(vojta): ignore if refresh/reload happens | ||
log.info('Added file "%s".', path); | ||
if (err) { | ||
if (errors.indexOf(path) === -1) { | ||
errors.push(path); | ||
} | ||
} | ||
fireEventAndDefer(); | ||
@@ -344,3 +365,14 @@ done(); | ||
// 2/ delay the promise resolution - wait for any changeFile operations to finish | ||
return preprocess(changedFile, function() { | ||
return preprocess(changedFile, function(err) { | ||
if (err) { | ||
if (errors.indexOf(path) === -1) { | ||
errors.push(path); | ||
} | ||
} else { | ||
var idx = errors.indexOf(path); | ||
if (idx !== -1) { | ||
errors.splice(idx, 1); | ||
} | ||
} | ||
// TODO(vojta): ignore if refresh/reload happens | ||
@@ -373,2 +405,9 @@ fireEventAndDefer(); | ||
log.info('Removed file "%s".', path); | ||
var idx = errors.indexOf(path); | ||
if (idx !== -1) { | ||
errors.splice(idx, 1); | ||
} | ||
fireEventAndDefer(); | ||
@@ -375,0 +414,0 @@ return done(); |
var log = require('./logger').create('launcher'); | ||
var baseBrowserDecoratorFactory = require('./launchers/base').decoratorFactory; | ||
var q = require('q'); | ||
var baseDecorator = require('./launchers/base').decoratorFactory; | ||
var captureTimeoutDecorator = require('./launchers/capture_timeout').decoratorFactory; | ||
var retryDecorator = require('./launchers/retry').decoratorFactory; | ||
var processDecorator = require('./launchers/process').decoratorFactory; | ||
// TODO(vojta): remove once nobody uses it | ||
var baseBrowserDecoratorFactory = function(baseLauncherDecorator, captureTimeoutLauncherDecorator, | ||
retryLauncherDecorator, processLauncherDecorator) { | ||
return function(launcher) { | ||
baseLauncherDecorator(launcher); | ||
captureTimeoutLauncherDecorator(launcher); | ||
retryLauncherDecorator(launcher); | ||
processLauncherDecorator(launcher); | ||
}; | ||
}; | ||
var Launcher = function(emitter, injector) { | ||
var browsers = []; | ||
var lastUrl; | ||
var lastStartTime; | ||
@@ -22,3 +37,3 @@ | ||
var browser; | ||
var url = (lastUrl = 'http://' + hostname + ':' + port + urlRoot); | ||
var url = 'http://' + hostname + ':' + port + urlRoot; | ||
@@ -31,2 +46,6 @@ lastStartTime = Date.now(); | ||
name: ['value', name], | ||
baseLauncherDecorator: ['factory', baseDecorator], | ||
captureTimeoutLauncherDecorator: ['factory', captureTimeoutDecorator], | ||
retryLauncherDecorator: ['factory', retryDecorator], | ||
processLauncherDecorator: ['factory', processDecorator], | ||
baseBrowserDecorator: ['factory', baseBrowserDecoratorFactory] | ||
@@ -53,2 +72,13 @@ }; | ||
// TODO(vojta): remove in v1.0 (BC for old launchers) | ||
if (!browser.forceKill) { | ||
browser.forceKill = function() { | ||
var deferred = q.defer(); | ||
this.kill(function() { | ||
deferred.resolve(); | ||
}); | ||
return deferred.promise; | ||
}; | ||
} | ||
log.info('Starting browser %s', browser.name); | ||
@@ -74,3 +104,3 @@ browser.start(url); | ||
browser.kill(callback); | ||
browser.forceKill().then(callback); | ||
return true; | ||
@@ -87,5 +117,3 @@ }; | ||
browser.kill(function() { | ||
browser.start(lastUrl); | ||
}); | ||
browser.restart(); | ||
return true; | ||
@@ -112,3 +140,3 @@ }; | ||
remaining++; | ||
browser.kill(finish); | ||
browser.forceKill().then(finish); | ||
}); | ||
@@ -115,0 +143,0 @@ }; |
@@ -1,8 +0,5 @@ | ||
var spawn = require('child_process').spawn; | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var rimraf = require('rimraf'); | ||
var KarmaEventEmitter = require('../events').EventEmitter; | ||
var EventEmitter = require('events').EventEmitter; | ||
var q = require('q'); | ||
var log = require('../logger').create('launcher'); | ||
var env = process.env; | ||
@@ -13,204 +10,122 @@ var BEING_CAPTURED = 1; | ||
var FINISHED = 4; | ||
var BEING_TIMEOUTED = 5; | ||
var RESTARTING = 5; | ||
var BEING_FORCE_KILLED = 6; | ||
var BaseBrowser = function(id, emitter, captureTimeout, retryLimit) { | ||
var self = this; | ||
var capturingUrl; | ||
var exitCallbacks = []; | ||
/** | ||
* Base launcher that any custom launcher extends. | ||
*/ | ||
var BaseLauncher = function(id, emitter) { | ||
if (this.start) { | ||
return; | ||
} | ||
this.killTimeout = 2000; | ||
// TODO(vojta): figure out how to do inheritance with DI | ||
Object.keys(EventEmitter.prototype).forEach(function(method) { | ||
this[method] = EventEmitter.prototype[method]; | ||
}, this); | ||
KarmaEventEmitter.call(this); | ||
this.id = id; | ||
this.state = null; | ||
this._tempDir = path.normalize((env.TMPDIR || env.TMP || env.TEMP || '/tmp') + '/karma-' + | ||
id.toString()); | ||
this.error = null; | ||
var self = this; | ||
var killingPromise; | ||
var previousUrl; | ||
this.start = function(url) { | ||
capturingUrl = url; | ||
self.state = BEING_CAPTURED; | ||
previousUrl = url; | ||
try { | ||
log.debug('Creating temp dir at ' + self._tempDir); | ||
fs.mkdirSync(self._tempDir); | ||
} catch (e) {} | ||
self._start(capturingUrl + '?id=' + self.id); | ||
if (captureTimeout) { | ||
setTimeout(self._onTimeout, captureTimeout); | ||
} | ||
this.error = null; | ||
this.state = BEING_CAPTURED; | ||
this.emit('start', url + '?id=' + this.id); | ||
}; | ||
this._start = function(url) { | ||
self._execCommand(self._getCommand(), self._getOptions(url)); | ||
}; | ||
this.markCaptured = function() { | ||
if (self.state === BEING_CAPTURED) { | ||
self.state = CAPTURED; | ||
this.kill = function() { | ||
// Already killed, or being killed. | ||
if (killingPromise) { | ||
return killingPromise; | ||
} | ||
}; | ||
killingPromise = this.emitAsync('kill').then(function() { | ||
self.state = FINISHED; | ||
}); | ||
this.isCaptured = function() { | ||
return self.state === CAPTURED; | ||
}; | ||
this.state = BEING_KILLED; | ||
this.kill = function(callback) { | ||
var exitCallback = callback || function() {}; | ||
log.debug('Killing %s', self.name); | ||
if (self.state === FINISHED) { | ||
process.nextTick(exitCallback); | ||
} else if (self.state === BEING_KILLED) { | ||
exitCallbacks.push(exitCallback); | ||
} else { | ||
self.state = BEING_KILLED; | ||
self._process.kill(); | ||
exitCallbacks.push(exitCallback); | ||
setTimeout(self._onKillTimeout, self.killTimeout); | ||
} | ||
return killingPromise; | ||
}; | ||
this.forceKill = function() { | ||
this.kill(); | ||
this.state = BEING_FORCE_KILLED; | ||
this._onKillTimeout = function() { | ||
if (self.state !== BEING_KILLED) { | ||
return; | ||
} | ||
log.warn('%s was not killed in %d ms, sending SIGKILL.', self.name, self.killTimeout); | ||
self._process.kill('SIGKILL'); | ||
return killingPromise; | ||
}; | ||
this._onTimeout = function() { | ||
if (self.state !== BEING_CAPTURED) { | ||
this.restart = function() { | ||
if (this.state === BEING_FORCE_KILLED) { | ||
return; | ||
} | ||
log.warn('%s have not captured in %d ms, killing.', self.name, captureTimeout); | ||
self.state = BEING_TIMEOUTED; | ||
self._process.kill(); | ||
}; | ||
this.toString = function() { | ||
return self.name; | ||
}; | ||
this._getCommand = function() { | ||
var cmd = path.normalize(env[self.ENV_CMD] || self.DEFAULT_CMD[process.platform]); | ||
if (!cmd) { | ||
log.error('No binary for %s browser on your platform.\n\t' + | ||
'Please, set "%s" env variable.', self.name, self.ENV_CMD); | ||
if (!killingPromise) { | ||
killingPromise = this.emitAsync('kill'); | ||
} | ||
return cmd; | ||
}; | ||
this._execCommand = function(cmd, args) { | ||
// normalize the cmd, remove quotes (spawn does not like them) | ||
if (cmd.charAt(0) === cmd.charAt(cmd.length - 1) && '\'`"'.indexOf(cmd.charAt(0)) !== -1) { | ||
cmd = cmd.substring(1, cmd.length - 1); | ||
log.warn('The path should not be quoted.\n Normalized the path to %s', cmd); | ||
} | ||
log.debug(cmd + ' ' + args.join(' ')); | ||
self._process = spawn(cmd, args); | ||
var errorOutput = ''; | ||
self._process.on('close', function(code) { | ||
self._onProcessExit(code, errorOutput); | ||
}); | ||
self._process.on('error', function(err) { | ||
if (err.code === 'ENOENT') { | ||
retryLimit = 0; | ||
errorOutput = 'Can not find the binary ' + cmd + '\n\t' + | ||
'Please set env variable ' + self.ENV_CMD; | ||
killingPromise.then(function() { | ||
if (self.state === BEING_FORCE_KILLED) { | ||
self.state = FINISHED; | ||
} else { | ||
errorOutput += err.toString(); | ||
killingPromise = null; | ||
log.debug('Restarting %s', self.name); | ||
self.start(previousUrl); | ||
} | ||
}); | ||
// Node 0.8 does not emit the error | ||
if (process.versions.node.indexOf('0.8') === 0) { | ||
self._process.stderr.on('data', function(data) { | ||
var msg = data.toString(); | ||
self.state = RESTARTING; | ||
}; | ||
if (msg.indexOf('No such file or directory') !== -1) { | ||
retryLimit = 0; | ||
errorOutput = 'Can not find the binary ' + cmd + '\n\t' + | ||
'Please set env variable ' + self.ENV_CMD; | ||
} else { | ||
errorOutput += msg; | ||
} | ||
}); | ||
this.markCaptured = function() { | ||
if (this.state === BEING_CAPTURED) { | ||
this.state = CAPTURED; | ||
} | ||
}; | ||
this.isCaptured = function() { | ||
return this.state === CAPTURED; | ||
}; | ||
this._onProcessExit = function(code, errorOutput) { | ||
log.debug('Process %s exitted with code %d', self.name, code); | ||
this.toString = function() { | ||
return this.name; | ||
}; | ||
if (self.state === BEING_CAPTURED) { | ||
log.error('Cannot start %s\n\t%s', self.name, errorOutput); | ||
} | ||
this._done = function(error) { | ||
killingPromise = killingPromise || q(); | ||
if (self.state === CAPTURED) { | ||
log.error('%s crashed.\n\t%s', self.name, errorOutput); | ||
} | ||
this.error = this.error || error; | ||
this.emit('done'); | ||
retryLimit--; | ||
if (self.state === BEING_CAPTURED || self.state === BEING_TIMEOUTED) { | ||
if (retryLimit > 0) { | ||
return self._cleanUpTmp(function() { | ||
log.info('Trying to start %s again.', self.name); | ||
self.start(capturingUrl); | ||
}); | ||
} else { | ||
emitter.emit('browser_process_failure', self); | ||
} | ||
if (this.error && this.state !== BEING_FORCE_KILLED && this.state !== RESTARTING) { | ||
emitter.emit('browser_process_failure', this); | ||
} | ||
self.state = FINISHED; | ||
self._cleanUpTmp(function(err) { | ||
exitCallbacks.forEach(function(exitCallback) { | ||
exitCallback(err); | ||
}); | ||
exitCallbacks = []; | ||
}); | ||
this.state = FINISHED; | ||
}; | ||
this._cleanUpTmp = function(done) { | ||
log.debug('Cleaning temp dir %s', self._tempDir); | ||
rimraf(self._tempDir, done); | ||
}; | ||
this._getOptions = function(url) { | ||
return [url]; | ||
}; | ||
this.STATE_BEING_CAPTURED = BEING_CAPTURED; | ||
this.STATE_CAPTURED = CAPTURED; | ||
this.STATE_BEING_KILLED = BEING_KILLED; | ||
this.STATE_FINISHED = FINISHED; | ||
this.STATE_RESTARTING = RESTARTING; | ||
this.STATE_BEING_FORCE_KILLED = BEING_FORCE_KILLED; | ||
}; | ||
var baseBrowserDecoratorFactory = function(id, emitter, timeout) { | ||
return function(self) { | ||
BaseBrowser.call(self, id, emitter, timeout, 3); | ||
BaseLauncher.decoratorFactory = function(id, emitter) { | ||
return function(launcher) { | ||
BaseLauncher.call(launcher, id, emitter); | ||
}; | ||
}; | ||
baseBrowserDecoratorFactory.$inject = ['id', 'emitter', 'config.captureTimeout']; | ||
// PUBLISH | ||
exports.BaseBrowser = BaseBrowser; | ||
exports.decoratorFactory = baseBrowserDecoratorFactory; | ||
module.exports = BaseLauncher; |
@@ -108,2 +108,8 @@ /** | ||
}); | ||
}, function() { | ||
serveStaticFile(requestUrl, response, function(data) { | ||
common.setNoCacheHeaders(response); | ||
var cancelBuildCode = 'window.__karma__.error("TEST RUN CANCLLED");'; | ||
return data.replace('%SCRIPTS%', '').replace('%MAPPINGS%', cancelBuildCode); | ||
}); | ||
}); | ||
@@ -110,0 +116,0 @@ } |
@@ -20,3 +20,16 @@ var fs = require('graceful-fs'); | ||
var preprocessors = []; | ||
var nextPreprocessor = function(content) { | ||
var nextPreprocessor = function(error, content) { | ||
// normalize B-C | ||
if (arguments.length === 1 && typeof error === 'string') { | ||
content = error; | ||
error = null; | ||
} | ||
if (error) { | ||
// log.error('FAILED PREPROCES'); | ||
file.content = null; | ||
file.contentPath = null; | ||
return done(error); | ||
} | ||
if (!preprocessors.length) { | ||
@@ -23,0 +36,0 @@ file.contentPath = null; |
@@ -0,18 +1,56 @@ | ||
var util = require('util'); | ||
var log = require('./logger').create('reporter'); | ||
var MultiReporter = require('./reporters/multi'); | ||
var baseReporterDecoratorFactory = require('./reporters/base').decoratorFactory; | ||
var SourceMapConsumer = require('source-map').SourceMapConsumer; | ||
var createErrorFormatter = function(basePath) { | ||
var URL_REGEXP = new RegExp('http:\\/\\/[^\\/]*' + | ||
'\\/(base|absolute)([^\\?\\s\\:]*)(\\?\\w*)?', 'g'); | ||
var createErrorFormatter = function(basePath, emitter, SourceMapConsumer) { | ||
var lastServedFiles = []; | ||
emitter.on('file_list_modified', function(filesPromise) { | ||
filesPromise.then(function(files) { | ||
lastServedFiles = files.served; | ||
}); | ||
}); | ||
var findFile = function(path) { | ||
for (var i = 0; i < lastServedFiles.length; i++) { | ||
if (lastServedFiles[i].path === path) { | ||
return lastServedFiles[i]; | ||
} | ||
} | ||
return null; | ||
}; | ||
var URL_REGEXP = new RegExp('http:\\/\\/[^\\/]*\\/' + | ||
'(base|absolute)' + // prefix | ||
'([^\\?\\s\\:]*)' + // path | ||
'(\\?\\w*)?' + // sha | ||
'(\\:(\\d+))?' + // line | ||
'(\\:(\\d+))?' + // column | ||
'', 'g'); | ||
return function(msg, indentation) { | ||
// remove domain and timestamp from source files | ||
// and resolve base path / absolute path urls into absolute path | ||
msg = (msg || '').replace(URL_REGEXP, function(full, prefix, path) { | ||
msg = (msg || '').replace(URL_REGEXP, function(_, prefix, path, __, ___, line, ____, column) { | ||
if (prefix === 'base') { | ||
return basePath + path; | ||
} else if (prefix === 'absolute') { | ||
return path; | ||
path = basePath + path; | ||
} | ||
var file = findFile(path); | ||
if (file && file.sourceMap) { | ||
line = parseInt(line || '0', 10); | ||
column = parseInt(column || '0', 10); | ||
var smc = new SourceMapConsumer(file.sourceMap); | ||
var original = smc.originalPositionFor({line: line, column: column}); | ||
return util.format('%s:%d:%d <- %s:%d:%d', path, line, column, original.source, | ||
original.line, original.column); | ||
} | ||
return path + (line ? ':' + line : '') + (column ? ':' + column : ''); | ||
}); | ||
@@ -29,7 +67,5 @@ | ||
createErrorFormatter.$inject = ['config.basePath']; | ||
var createReporters = function(names, config, emitter, injector) { | ||
var errorFormatter = createErrorFormatter(config.basePath, config.urlRoot); | ||
var errorFormatter = createErrorFormatter(config.basePath, emitter, SourceMapConsumer); | ||
var reporters = []; | ||
@@ -46,3 +82,3 @@ | ||
baseReporterDecorator: ['factory', baseReporterDecoratorFactory], | ||
formatError: ['factory', createErrorFormatter] | ||
formatError: ['value', errorFormatter] | ||
}; | ||
@@ -49,0 +85,0 @@ |
@@ -37,2 +37,4 @@ var io = require('socket.io'); | ||
injector.invoke(watcher.watch); | ||
}, function() { | ||
injector.invoke(watcher.watch); | ||
}); | ||
@@ -39,0 +41,0 @@ } |
@@ -122,3 +122,4 @@ { | ||
"graceful-fs": "~1.2.1", | ||
"connect": "~2.8.4" | ||
"connect": "~2.8.4", | ||
"source-map": "~0.1.31" | ||
}, | ||
@@ -168,4 +169,4 @@ "devDependencies": { | ||
}, | ||
"version": "0.11.10", | ||
"version": "0.11.11-dev", | ||
"license": "MIT" | ||
} |
@@ -1,2 +0,2 @@ | ||
;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
module.exports = { | ||
@@ -181,2 +181,6 @@ VERSION: '%KARMA_VERSION%', | ||
var UNIMPLEMENTED_START = function() { | ||
this.error('You need to include some adapter that implements __karma__.start method!'); | ||
}; | ||
// all files loaded, let's start the execution | ||
@@ -190,3 +194,3 @@ this.loaded = function() { | ||
// remove reference to child iframe | ||
this.start = null; | ||
this.start = UNIMPLEMENTED_START; | ||
}; | ||
@@ -212,5 +216,3 @@ | ||
// TODO(vojta): support multiple callbacks (queue) | ||
this.start = function() { | ||
this.error('You need to include some adapter that implements __karma__.start method!'); | ||
}; | ||
this.start = UNIMPLEMENTED_START; | ||
@@ -405,3 +407,2 @@ socket.on('execute', function(cfg) { | ||
},{}]},{},[3]) | ||
; | ||
},{}]},{},[3]) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
913613
66
4239
18
+ Addedsource-map@~0.1.31
+ Addedamdefine@1.0.1(transitive)
+ Addedsource-map@0.1.43(transitive)