UNPKG

karma

Version:

Spectacular Test Runner for JavaScript.

243 lines (186 loc) 6.37 kB
var helper = require('./helper'); var events = require('./events'); var logger = require('./logger'); var Result = require('./browser_result'); // The browser is ready to execute tests. var READY = 1; // The browser is executing the tests/ var EXECUTING = 2; // The browser is not executing, but temporarily disconnected (waiting for reconnecting). var READY_DISCONNECTED = 3; // The browser is executing the tests, but temporarily disconnect (waiting for reconnecting). var EXECUTING_DISCONNECTED = 4; // The browser got permanently disconnected (being removed from the collection and destroyed). var DISCONNECTED = 5; var Browser = function(id, fullName, /* capturedBrowsers */ collection, emitter, socket, timer, /* config.browserDisconnectTimeout */ disconnectDelay, /* config.browserNoActivityTimeout */ noActivityTimeout) { var name = helper.browserFullNameToShort(fullName); var log = logger.create(name); var activeSockets = [socket]; var activeSocketsIds = function() { return activeSockets.map(function(s) { return s.id; }).join(', '); }; var self = this; var pendingDisconnect; var disconnect = function(reason) { self.state = DISCONNECTED; self.disconnectsCount++; log.warn('Disconnected (%d times)' + (reason || ''), self.disconnectsCount); collection.remove(self); }; var noActivityTimeoutId; var refreshNoActivityTimeout = noActivityTimeout ? function() { clearNoActivityTimeout(); noActivityTimeoutId = timer.setTimeout(function() { self.lastResult.totalTimeEnd(); self.lastResult.disconnected = true; disconnect(', because no message in ' + noActivityTimeout + ' ms.'); emitter.emit('browser_complete', self); }, noActivityTimeout); } : function() {}; var clearNoActivityTimeout = noActivityTimeout ? function() { if (noActivityTimeoutId) { timer.clearTimeout(noActivityTimeoutId); noActivityTimeoutId = null; } } : function() {}; this.id = id; this.fullName = fullName; this.name = name; this.state = READY; this.lastResult = new Result(); this.disconnectsCount = 0; this.init = function() { collection.add(this); events.bindAll(this, socket); log.info('Connected on socket %s with id %s', socket.id, id); // TODO(vojta): move to collection emitter.emit('browsers_change', collection); emitter.emit('browser_register', this); }; this.isReady = function() { return this.state === READY; }; this.toString = function() { return this.name; }; this.onError = function(error) { if (this.isReady()) { return; } this.lastResult.error = true; emitter.emit('browser_error', this, error); refreshNoActivityTimeout(); }; this.onInfo = function(info) { if (this.isReady()) { return; } // TODO(vojta): remove if (helper.isDefined(info.dump)) { emitter.emit('browser_log', this, info.dump, 'dump'); } if (helper.isDefined(info.log)) { emitter.emit('browser_log', this, info.log, info.type); } refreshNoActivityTimeout(); }; this.onStart = function(info) { this.lastResult = new Result(); this.lastResult.total = info.total; if (info.total === null) { log.warn('Adapter did not report total number of specs.'); } emitter.emit('browser_start', this, info); refreshNoActivityTimeout(); }; this.onComplete = function(result) { if (this.isReady()) { return; } this.state = READY; this.lastResult.totalTimeEnd(); if (!this.lastResult.success) { this.lastResult.error = true; } emitter.emit('browsers_change', collection); emitter.emit('browser_complete', this, result); clearNoActivityTimeout(); }; this.onDisconnect = function(_, disconnectedSocket) { activeSockets.splice(activeSockets.indexOf(disconnectedSocket), 1); if (activeSockets.length) { log.debug('Disconnected %s, still have %s', disconnectedSocket.id, activeSocketsIds()); return; } if (this.state === READY) { disconnect(); } else if (this.state === EXECUTING) { log.debug('Disconnected during run, waiting %sms for reconnecting.', disconnectDelay); this.state = EXECUTING_DISCONNECTED; pendingDisconnect = timer.setTimeout(function() { self.lastResult.totalTimeEnd(); self.lastResult.disconnected = true; disconnect(); emitter.emit('browser_complete', self); }, disconnectDelay); clearNoActivityTimeout(); } }; this.reconnect = function(newSocket) { if (this.state === EXECUTING_DISCONNECTED) { this.state = EXECUTING; log.debug('Reconnected on %s.', newSocket.id); } else if (this.state === EXECUTING || this.state === READY) { log.debug('New connection %s (already have %s)', newSocket.id, activeSocketsIds()); } else if (this.state === DISCONNECTED) { this.state = READY; log.info('Connected on socket %s with id %s', newSocket.id, this.id); collection.add(this); // TODO(vojta): move to collection emitter.emit('browsers_change', collection); emitter.emit('browser_register', this); } activeSockets.push(newSocket); events.bindAll(this, newSocket); if (pendingDisconnect) { timer.clearTimeout(pendingDisconnect); } refreshNoActivityTimeout(); }; this.onResult = function(result) { if (result.length) { return result.forEach(this.onResult, this); } // ignore - probably results from last run (after server disconnecting) if (this.isReady()) { return; } this.lastResult.add(result); emitter.emit('spec_complete', this, result); refreshNoActivityTimeout(); }; this.serialize = function() { return { id: this.id, name: this.name, isReady: this.state === READY }; }; this.execute = function(config) { activeSockets.forEach(function(socket) { socket.emit('execute', config); }); this.state = EXECUTING; refreshNoActivityTimeout(); }; }; Browser.STATE_READY = READY; Browser.STATE_EXECUTING = EXECUTING; Browser.STATE_READY_DISCONNECTED = READY_DISCONNECTED; Browser.STATE_EXECUTING_DISCONNECTED = EXECUTING_DISCONNECTED; Browser.STATE_DISCONNECTED = DISCONNECTED; module.exports = Browser;