diff --git a/build/browser/app.js b/build/browser/app.js index b0e36ba4..70d07b0e 100644 --- a/build/browser/app.js +++ b/build/browser/app.js @@ -20,6 +20,7 @@ */ var angular = require('angular'); +var _ = require('lodash'); var remote = window.require('remote'); var shell = remote.require('shell'); var dialog = remote.require('./src/dialog'); @@ -52,7 +53,27 @@ app.controller('AppController', function($q, DriveScannerService, SelectionState console.debug('Restarting'); this.selection.clear(); this.writer.setProgress(0); - this.scanner.start(2000); + this.scanner.start(2000).on('scan', function(drives) { + + // Notice we only autoselect the drive if there is an image, + // which means that the first step was completed successfully, + // otherwise the drive is selected while the drive step is disabled + // which looks very weird. + if (drives.length === 1 && self.selection.hasImage()) { + var drive = _.first(drives); + + // Do not autoselect the same drive over and over again + // and fill the logs unnecessary. + // `angular.equals` is used instead of `_.isEqual` to + // cope with `$$hashKey`. + if (!angular.equals(self.selection.getDrive(), drive)) { + console.debug('Autoselecting drive: ' + drive.device); + self.selectDrive(drive); + } + + } + + }); }; this.restart(); @@ -111,6 +132,7 @@ app.controller('AppController', function($q, DriveScannerService, SelectionState var angular = require('angular'); var _ = require('lodash'); +var EventEmitter = require('events').EventEmitter; var remote = window.require('remote'); if (window.mocha) { @@ -125,7 +147,7 @@ if (window.mocha) { var driveScanner = angular.module('ResinEtcher.drive-scanner', []); -driveScanner.service('DriveScannerRefreshService', function($interval) { +driveScanner.service('DriveScannerRefreshService', function($interval, $timeout) { 'use strict'; var interval = null; @@ -144,8 +166,15 @@ driveScanner.service('DriveScannerRefreshService', function($interval) { * }, 2000); */ this.every = function(fn, ms) { - fn(); - interval = $interval(fn, ms); + + // Call fn after in the next process tick + // to be able to capture the first run + // in unit tests. + $timeout(function() { + fn(); + interval = $interval(fn, ms); + }); + }; /** @@ -234,15 +263,32 @@ driveScanner.service('DriveScannerService', function($q, DriveScannerRefreshServ * @function * @public * + * @description + * This function returns an event emitter instance + * that emits a `scan` event everything it scans + * the drives successfully. + * * @param {Number} ms - interval milliseconds + * @returns {EventEmitter} event emitter instance * * @example - * DriveScannerService.start(2000); + * var emitter = DriveScannerService.start(2000); + * + * emitter.on('scan', function(drives) { + * console.log(drives); + * }); */ this.start = function(ms) { + var emitter = new EventEmitter(); + DriveScannerRefreshService.every(function() { - return self.scan().then(self.setDrives); + return self.scan().then(function(drives) { + emitter.emit('scan', drives); + self.setDrives(drives); + }); }, ms); + + return emitter; }; /** diff --git a/lib/browser/app.js b/lib/browser/app.js index 264ab437..556949d4 100644 --- a/lib/browser/app.js +++ b/lib/browser/app.js @@ -19,6 +19,7 @@ */ var angular = require('angular'); +var _ = require('lodash'); var remote = window.require('remote'); var shell = remote.require('shell'); var dialog = remote.require('./src/dialog'); @@ -51,7 +52,27 @@ app.controller('AppController', function($q, DriveScannerService, SelectionState console.debug('Restarting'); this.selection.clear(); this.writer.setProgress(0); - this.scanner.start(2000); + this.scanner.start(2000).on('scan', function(drives) { + + // Notice we only autoselect the drive if there is an image, + // which means that the first step was completed successfully, + // otherwise the drive is selected while the drive step is disabled + // which looks very weird. + if (drives.length === 1 && self.selection.hasImage()) { + var drive = _.first(drives); + + // Do not autoselect the same drive over and over again + // and fill the logs unnecessary. + // `angular.equals` is used instead of `_.isEqual` to + // cope with `$$hashKey`. + if (!angular.equals(self.selection.getDrive(), drive)) { + console.debug('Autoselecting drive: ' + drive.device); + self.selectDrive(drive); + } + + } + + }); }; this.restart(); diff --git a/lib/browser/modules/drive-scanner.js b/lib/browser/modules/drive-scanner.js index 65fd9351..bd3b6763 100644 --- a/lib/browser/modules/drive-scanner.js +++ b/lib/browser/modules/drive-scanner.js @@ -20,6 +20,7 @@ var angular = require('angular'); var _ = require('lodash'); +var EventEmitter = require('events').EventEmitter; var remote = window.require('remote'); if (window.mocha) { @@ -34,7 +35,7 @@ if (window.mocha) { var driveScanner = angular.module('ResinEtcher.drive-scanner', []); -driveScanner.service('DriveScannerRefreshService', function($interval) { +driveScanner.service('DriveScannerRefreshService', function($interval, $timeout) { 'use strict'; var interval = null; @@ -53,8 +54,15 @@ driveScanner.service('DriveScannerRefreshService', function($interval) { * }, 2000); */ this.every = function(fn, ms) { - fn(); - interval = $interval(fn, ms); + + // Call fn after in the next process tick + // to be able to capture the first run + // in unit tests. + $timeout(function() { + fn(); + interval = $interval(fn, ms); + }); + }; /** @@ -143,15 +151,32 @@ driveScanner.service('DriveScannerService', function($q, DriveScannerRefreshServ * @function * @public * + * @description + * This function returns an event emitter instance + * that emits a `scan` event everything it scans + * the drives successfully. + * * @param {Number} ms - interval milliseconds + * @returns {EventEmitter} event emitter instance * * @example - * DriveScannerService.start(2000); + * var emitter = DriveScannerService.start(2000); + * + * emitter.on('scan', function(drives) { + * console.log(drives); + * }); */ this.start = function(ms) { + var emitter = new EventEmitter(); + DriveScannerRefreshService.every(function() { - return self.scan().then(self.setDrives); + return self.scan().then(function(drives) { + emitter.emit('scan', drives); + self.setDrives(drives); + }); }, ms); + + return emitter; }; /** diff --git a/tests/browser/modules/drive-scanner.spec.js b/tests/browser/modules/drive-scanner.spec.js index 8cabbec1..8f4c1dc4 100644 --- a/tests/browser/modules/drive-scanner.spec.js +++ b/tests/browser/modules/drive-scanner.spec.js @@ -12,9 +12,11 @@ describe('Browser: DriveScanner', function() { var DriveScannerRefreshService; var $interval; + var $timeout; - beforeEach(angular.mock.inject(function(_$interval_, _DriveScannerRefreshService_) { + beforeEach(angular.mock.inject(function(_$interval_, _$timeout_, _DriveScannerRefreshService_) { $interval = _$interval_; + $timeout = _$timeout_; DriveScannerRefreshService = _DriveScannerRefreshService_; })); @@ -23,6 +25,7 @@ describe('Browser: DriveScanner', function() { it('should call the function right away', function() { var spy = m.sinon.spy(); DriveScannerRefreshService.every(spy, 1000); + $timeout.flush(); DriveScannerRefreshService.stop(); m.chai.expect(spy).to.have.been.calledOnce; }); @@ -30,6 +33,7 @@ describe('Browser: DriveScanner', function() { it('should call the function in an interval', function() { var spy = m.sinon.spy(); DriveScannerRefreshService.every(spy, 100); + $timeout.flush(); // 400ms = 100ms / 4 + 1 (the initial call) $interval.flush(400); @@ -45,11 +49,13 @@ describe('Browser: DriveScanner', function() { describe('DriveScannerService', function() { var $interval; + var $timeout; var $q; var DriveScannerService; - beforeEach(angular.mock.inject(function(_$interval_, _$q_, _DriveScannerService_) { + beforeEach(angular.mock.inject(function(_$interval_, _$timeout_, _$q_, _DriveScannerService_) { $interval = _$interval_; + $timeout = _$timeout_; $q = _$q_; DriveScannerService = _DriveScannerService_; })); @@ -169,11 +175,27 @@ describe('Browser: DriveScanner', function() { it('should set the drives to the scanned ones', function() { DriveScannerService.start(200); + $timeout.flush(); $interval.flush(400); m.chai.expect(DriveScannerService.drives).to.deep.equal(this.drives); DriveScannerService.stop(); }); + describe('.start()', function() { + + it('should emit a `scan` event with the drives', function() { + var emitter = DriveScannerService.start(2000); + var scanSpy = m.sinon.spy(); + emitter.on('scan', scanSpy); + $timeout.flush(); + $interval.flush(1000); + m.chai.expect(scanSpy).to.have.been.calledOnce; + m.chai.expect(scanSpy).to.have.been.calledWith(this.drives); + DriveScannerService.stop(); + }); + + }); + }); });