From 3a798bf543c112be0a6c4218c7db61d039ebbf05 Mon Sep 17 00:00:00 2001 From: Justin Stockton Date: Mon, 6 Apr 2015 15:55:58 -0600 Subject: [PATCH 1/6] Upgrade to Angular 1.3.15 to see if it fixes a memory leak --- package.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 9d5fd3f..d35ebe7 100644 --- a/package.json +++ b/package.json @@ -14,15 +14,15 @@ "url": "https://github.com/cfpb/hmda-pilot/issues" }, "dependencies": { - "angular": "1.3.13", - "angular-animate": "1.3.13", - "angular-aria": "1.3.13", - "angular-cookies": "1.3.13", + "angular": "1.3.15", + "angular-animate": "1.3.15", + "angular-aria": "1.3.15", + "angular-cookies": "1.3.15", "angular-filereader": "matteosuppo/angular-filereader#v1.0.2", - "angular-resource": "1.3.13", - "angular-route": "1.3.13", - "angular-sanitize": "1.3.13", - "angular-touch": "1.3.13", + "angular-resource": "1.3.15", + "angular-route": "1.3.15", + "angular-sanitize": "1.3.15", + "angular-touch": "1.3.15", "hmda-rule-engine": "cfpb/hmda-rule-engine#milestone9", "ng-dialog": "^0.3.12", "normalize-css": "^2.3.1" @@ -38,7 +38,7 @@ "cf-layout": "cfpb/cf-layout#0.2.2" }, "devDependencies": { - "angular-mocks": "1.3.13", + "angular-mocks": "1.3.15", "browserify-istanbul": "^0.1.2", "browserify-shim": "^3.8.3", "grunt": "^0.4.1", From 3c5234f7b62532c4aff82e7f0b092dda70658fc7 Mon Sep 17 00:00:00 2001 From: Justin Stockton Date: Thu, 9 Apr 2015 12:09:01 -0600 Subject: [PATCH 2/6] Display a progress bar overlay showing the computed progress through the relevant type of tests being run --- app/partials/progressBar.html | 6 +++ app/scripts/controllers/index.js | 1 + app/scripts/controllers/progressBar.js | 19 +++++++ app/scripts/controllers/selectFile.js | 19 +++++-- .../controllers/summaryQualityMacro.js | 12 ++++- .../controllers/summarySyntacticalValidity.js | 10 +++- app/scripts/modules/config.js | 10 +++- app/styles/hmda-pilot.less | 1 + app/styles/progressBar.less | 33 ++++++++++++ config/config.js | 10 +++- test/spec/controllers/progressBar.js | 33 ++++++++++++ test/spec/controllers/selectFile.js | 36 ++++++++++--- test/spec/controllers/summaryQualityMacro.js | 51 +++++++++---------- .../controllers/summarySyntacticalValidity.js | 27 +++++++--- 14 files changed, 220 insertions(+), 48 deletions(-) create mode 100644 app/partials/progressBar.html create mode 100644 app/scripts/controllers/progressBar.js create mode 100644 app/styles/progressBar.less create mode 100644 test/spec/controllers/progressBar.js diff --git a/app/partials/progressBar.html b/app/partials/progressBar.html new file mode 100644 index 0000000..1100efa --- /dev/null +++ b/app/partials/progressBar.html @@ -0,0 +1,6 @@ +
+
+ +
+

{{processStep}}

+
diff --git a/app/scripts/controllers/index.js b/app/scripts/controllers/index.js index d3fee33..d5f80af 100644 --- a/app/scripts/controllers/index.js +++ b/app/scripts/controllers/index.js @@ -13,3 +13,4 @@ app.controller('ErrorDetailCtrl', require('./errorDetail')); app.controller('SpecialErrorDetailCtrl', require('./specialErrorDetail')); app.controller('IRSReportCtrl', require('./irsReport')); app.controller('PaginationCtrl', require('./pagination')); +app.controller('ProgressBarCtrl', require('./progressBar')); diff --git a/app/scripts/controllers/progressBar.js b/app/scripts/controllers/progressBar.js new file mode 100644 index 0000000..302089b --- /dev/null +++ b/app/scripts/controllers/progressBar.js @@ -0,0 +1,19 @@ +'use strict'; + +/** + * @ngdoc function + * @name hmdaPilotApp.controller:ProgressBarCtrl + * @description + * # ProgressBarCtrl + * Controller of the hmdaPilotApp + */ +module.exports = /*@ngInject*/ function ($scope, HMDAEngine) { + + $scope.percentageComplete = 0; + + HMDAEngine.getProgress().events.on('progressStep', function(percent) { + $scope.$apply(function(){ + $scope.percentageComplete = percent; + }); + }); +}; diff --git a/app/scripts/controllers/selectFile.js b/app/scripts/controllers/selectFile.js index 25dd92d..44e01cd 100644 --- a/app/scripts/controllers/selectFile.js +++ b/app/scripts/controllers/selectFile.js @@ -7,13 +7,15 @@ * # Select File * Controller for selecting a HMDA file and Reporting Year for verification. */ -module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, FileReader, FileMetadata, HMDAEngine, Wizard, Session) { - var fiscalYears = HMDAEngine.getValidYears(); +module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, FileReader, FileMetadata, HMDAEngine, Wizard, Session, ngDialog, Configuration) { + var progressDialog, + fiscalYears = HMDAEngine.getValidYears(); // Set/Reset the state of different objects on load Session.reset(); HMDAEngine.clearHmdaJson(); HMDAEngine.clearErrors(); + HMDAEngine.clearProgress(); $scope.metadata = FileMetadata.clear(); $scope.wizardSteps = Wizard.initSteps(); @@ -48,11 +50,17 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, FileRe // Toggle processing flag on so that we can notify the user $scope.isProcessing = true; - $timeout(function() { $scope.process(hmdaData); }, 100); // Pause before starting the conversion so that the DOM can update + // Give a name to the current step in the process (shown in the progressDialog) + $scope.processStep = 'Processing HMDA file...'; + + progressDialog = ngDialog.open(angular.extend(Configuration.progressDialog, {scope: $scope})); + $timeout(function() { $scope.process(hmdaData); }, 100); // Pause before starting the conversion so that the DOM can update }; $scope.process = function(hmdaData) { + + // Enable LocalDB support? HMDAEngine.setUseLocalDB(hmdaData.local); /* istanbul ignore if debug */ @@ -77,6 +85,9 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, FileRe console.time('total time for syntactical and validity edits'); } + // Give a name to the current step in the process (shown in the progressDialog) + $scope.processStep = 'Validating Syntactical and Validity edits...'; + $q.all([HMDAEngine.runSyntactical(hmdaData.year), HMDAEngine.runValidity(hmdaData.year)]) .then(function() { @@ -96,10 +107,12 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, FileRe // Toggle processing flag off $scope.isProcessing = false; + progressDialog.close(); }) .catch(function(err) { // Toggle processing flag off $scope.isProcessing = false; + progressDialog.close(); $scope.errors.global = err.message; return; diff --git a/app/scripts/controllers/summaryQualityMacro.js b/app/scripts/controllers/summaryQualityMacro.js index 9bd4422..a66efcd 100644 --- a/app/scripts/controllers/summaryQualityMacro.js +++ b/app/scripts/controllers/summaryQualityMacro.js @@ -7,7 +7,7 @@ * # SummaryQualityMacroCtrl * Controller for the Syntactical and Validity Summary view */ -module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEngine, Wizard, Session) { /*jshint ignore:line*/ +module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEngine, Wizard, Session, ngDialog, Configuration) { /*jshint ignore:line*/ Array.prototype.diff = function(a) { return this.filter(function(i) { return a.indexOf(i) < 0; }); @@ -42,7 +42,8 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn $scope.isProcessing = false; // Get the list of errors from the HMDAEngine - var editErrors = HMDAEngine.getErrors(); + var progressDialog, + editErrors = HMDAEngine.getErrors(); $scope.data = { qualityErrors: editErrors.quality, @@ -64,6 +65,11 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn // Toggle processing flag on so that we can notify the user $scope.isProcessing = true; + // Give a name to the current step in the process (shown in the progressDialog) + $scope.processStep = 'Processing MSA/MD Data...'; + + progressDialog = ngDialog.open(angular.extend(Configuration.progressDialog, {scope: $scope})); + // Pause before starting the validation so that the DOM can update $timeout(function() { $scope.process(); }, 100); } @@ -93,9 +99,11 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn // Toggle processing flag off $scope.isProcessing = false; + progressDialog.close(); }).catch(function(err) { // Toggle processing flag off $scope.isProcessing = false; + progressDialog.close(); $scope.errors.global = err.message; return; diff --git a/app/scripts/controllers/summarySyntacticalValidity.js b/app/scripts/controllers/summarySyntacticalValidity.js index b099834..95b291a 100644 --- a/app/scripts/controllers/summarySyntacticalValidity.js +++ b/app/scripts/controllers/summarySyntacticalValidity.js @@ -14,7 +14,8 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn $scope.isProcessing = false; // Get the list of errors from the HMDAEngine - var editErrors = HMDAEngine.getErrors(); + var progressDialog, + editErrors = HMDAEngine.getErrors(); $scope.syntacticalErrors = editErrors.syntactical || {}; $scope.validityErrors = editErrors.validity || {}; @@ -48,6 +49,11 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn // Toggle processing flag on so that we can notify the user $scope.isProcessing = true; + // Give a name to the current step in the process (shown in the progressDialog) + $scope.processStep = 'Processing Quality and Macro edits...'; + + progressDialog = ngDialog.open(angular.extend(Configuration.progressDialog, {scope: $scope})); + // Pause before starting the validation so that the DOM can update $timeout(function() { $scope.process(); }, 100); } @@ -78,10 +84,12 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn // Toggle processing flag off $scope.isProcessing = false; + progressDialog.close(); }) .catch(function(err) { // Toggle processing flag off $scope.isProcessing = false; + progressDialog.close(); $scope.errors.global = err.message; return; diff --git a/app/scripts/modules/config.js b/app/scripts/modules/config.js index 082158a..4834768 100644 --- a/app/scripts/modules/config.js +++ b/app/scripts/modules/config.js @@ -3,5 +3,13 @@ angular.module('services.config', []) .constant('Configuration', { apiUrl: 'http://localhost:8000', - confirmSessionReset: false + confirmSessionReset: false, + progressDialog: { + name: 'progress', + controller: 'ProgressBarCtrl', + template: 'partials/progressBar.html', + showClose: false, + closeByDocument: false, + closeByEscape: false, + } }); diff --git a/app/styles/hmda-pilot.less b/app/styles/hmda-pilot.less index 022b467..f7bdeaf 100644 --- a/app/styles/hmda-pilot.less +++ b/app/styles/hmda-pilot.less @@ -29,6 +29,7 @@ // HMDA Pilot components @import (less) "utils.less"; @import (less) "wizard.less"; +@import (less) "progressBar.less"; // Layouts .wrapper, .wrap { diff --git a/app/styles/progressBar.less b/app/styles/progressBar.less new file mode 100644 index 0000000..07bb9a4 --- /dev/null +++ b/app/styles/progressBar.less @@ -0,0 +1,33 @@ +.progress-bar { + + .meter { + height: 20px; + margin-bottom: 1em; + position: relative; + background: @darkgray; + -moz-border-radius: 25px; + -webkit-border-radius: 25px; + border-radius: 25px; + padding: 3px; + box-shadow: inset 0 -1px 1px rgba(255,255,255,0.3); + + & > span { + display: block; + height: 100%; + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; + background-color: @green; + box-shadow: + inset 0 2px 9px rgba(255,255,255,0.3), + inset 0 -2px 6px rgba(0,0,0,0.4); + position: relative; + overflow: hidden; + } + } + + .description { + margin-bottom: 0; + } +} diff --git a/config/config.js b/config/config.js index 3acfbc2..20030bc 100644 --- a/config/config.js +++ b/config/config.js @@ -3,5 +3,13 @@ angular.module('services.config', []) .constant('Configuration', { apiUrl: '@@apiUrl', - confirmSessionReset: @@confirmSessionReset + confirmSessionReset: @@confirmSessionReset, + progressDialog: { + name: 'progress', + controller: 'ProgressBarCtrl', + template: 'partials/progressBar.html', + showClose: false, + closeByDocument: false, + closeByEscape: false, + } }); diff --git a/test/spec/controllers/progressBar.js b/test/spec/controllers/progressBar.js new file mode 100644 index 0000000..d2ab057 --- /dev/null +++ b/test/spec/controllers/progressBar.js @@ -0,0 +1,33 @@ +'use strict'; + +require('angular'); +require('angular-mocks'); + +describe('Controller: ProgressBarCtrl', function () { + + var scope, + mockEngine = { + getProgress: function() { + return { events: { on: function() {} } }; + } + }; + + beforeEach(angular.mock.module('hmdaPilotApp')); + + beforeEach(inject(function ($rootScope, $controller) { + scope = $rootScope.$new(); + + // spyOn(mockEngine.getProgress().events, 'on').and.returnValue(100); + + $controller('ProgressBarCtrl', { + $scope: scope, + HMDAEngine: mockEngine + }); + })); + + describe('initial scope', function() { + it('should set the percentageComplete to 0', function() { + expect(scope.percentageComplete).toBe(0); + }); + }); +}); diff --git a/test/spec/controllers/selectFile.js b/test/spec/controllers/selectFile.js index a1b3200..d0c69e9 100644 --- a/test/spec/controllers/selectFile.js +++ b/test/spec/controllers/selectFile.js @@ -8,6 +8,7 @@ describe('Controller: SelectFileCtrl', function () { var controller, scope, location, + timeout, Q, Wizard, FileMetadata, @@ -17,6 +18,7 @@ describe('Controller: SelectFileCtrl', function () { getValidYears: function() { return ['2013', '2014']; }, clearHmdaJson: function () { return {}; }, clearErrors: function () { return {}; }, + clearProgress: function () { return {}; }, fileToJson: function(file, year, next) { return next(null); }, runSyntactical: function() { return; }, runValidity: function() { return; }, @@ -27,11 +29,24 @@ describe('Controller: SelectFileCtrl', function () { beforeEach(angular.mock.module('hmdaPilotApp')); - beforeEach(inject(function ($rootScope, $location, $controller, $q, _Wizard_, _FileMetadata_, _FileReader_) { + beforeEach(inject(function($templateCache) { + var directiveTemplate = null; + var templateId = 'partials/progressBar.html'; + var req = new XMLHttpRequest(); + req.onload = function() { + directiveTemplate = this.responseText; + }; + req.open('get', '/base/app/'+templateId, false); + req.send(); + $templateCache.put(templateId, directiveTemplate); + })); + + beforeEach(inject(function ($rootScope, $location, $controller, $q, $timeout, _Wizard_, _FileMetadata_, _FileReader_, _Configuration_, _ngDialog_) { scope = $rootScope.$new(); controller = $controller; location = $location; Q = $q; + timeout = $timeout; Wizard = _Wizard_; FileMetadata = _FileMetadata_; FileReader = _FileReader_; @@ -41,10 +56,13 @@ describe('Controller: SelectFileCtrl', function () { $scope: scope, $location: location, $q: Q, + $timeout: timeout, Wizard: _Wizard_, FileMetadata: _FileMetadata_, FileReader: _FileReader_, - HMDAEngine: mockEngine + HMDAEngine: mockEngine, + ngDialog: _ngDialog_, + Configuration: _Configuration_ }); })); @@ -82,7 +100,7 @@ describe('Controller: SelectFileCtrl', function () { }); }); - describe('process()', function() { + describe('submit()', function() { var hmdaData = { file: 'test.dat', @@ -100,7 +118,8 @@ describe('Controller: SelectFileCtrl', function () { $q: Q, HMDAEngine: mockEngine }); - scope.process(hmdaData); + scope.submit(hmdaData); + timeout.flush(); scope.$digest(); expect(scope.errors.global).toBe('error'); @@ -118,7 +137,8 @@ describe('Controller: SelectFileCtrl', function () { $q: Q, HMDAEngine: mockEngine }); - scope.process(hmdaData); + scope.submit(hmdaData); + timeout.flush(); scope.$digest(); expect(scope.errors.global).toBe('error'); @@ -136,7 +156,8 @@ describe('Controller: SelectFileCtrl', function () { $q: Q, HMDAEngine: mockEngine }); - scope.process(hmdaData); + scope.submit(hmdaData); + timeout.flush(); scope.$digest(); expect(scope.errors.global).toBe('error'); @@ -153,7 +174,8 @@ describe('Controller: SelectFileCtrl', function () { $q: Q, HMDAEngine: mockEngine }); - scope.process(hmdaData); + scope.submit(hmdaData); + timeout.flush(); scope.$digest(); }); diff --git a/test/spec/controllers/summaryQualityMacro.js b/test/spec/controllers/summaryQualityMacro.js index b2109b1..b8fecf5 100644 --- a/test/spec/controllers/summaryQualityMacro.js +++ b/test/spec/controllers/summaryQualityMacro.js @@ -26,7 +26,19 @@ describe('Controller: SummaryQualityMacroCtrl', function () { beforeEach(angular.mock.module('hmdaPilotApp')); - beforeEach(inject(function ($rootScope, $location, $controller, $q, $timeout, _Wizard_, _Session_) { + beforeEach(inject(function($templateCache) { + var directiveTemplate = null; + var templateId = 'partials/progressBar.html'; + var req = new XMLHttpRequest(); + req.onload = function() { + directiveTemplate = this.responseText; + }; + req.open('get', '/base/app/'+templateId, false); + req.send(); + $templateCache.put(templateId, directiveTemplate); + })); + + beforeEach(inject(function ($rootScope, $location, $controller, $q, $timeout, _Wizard_, _Session_, _ngDialog_, _Configuration_) { scope = $rootScope.$new(); location = $location; controller = $controller; @@ -34,14 +46,18 @@ describe('Controller: SummaryQualityMacroCtrl', function () { Q = $q; Wizard = _Wizard_; Session = _Session_; + Wizard.initSteps(); + $controller('SummaryQualityMacroCtrl', { $scope: scope, $location: location, $timeout: timeout, HMDAEngine: mockEngine, Wizard: _Wizard_, - Session: _Session_ + Session: _Session_, + ngDialog: _ngDialog_, + Configuration: _Configuration_ }); })); @@ -133,39 +149,18 @@ describe('Controller: SummaryQualityMacroCtrl', function () { }); }); - describe('when special edit checks have not been run', function() { - beforeEach(function() { - mockErrors.special = {}; - controller('SummaryQualityMacroCtrl', { - $scope: scope, - $location: location, - $timeout: timeout, - HMDAEngine: mockEngine, - Session: Session - }); - }); - - it('should run the process() function', function() { - spyOn(scope, 'process'); - scope.next(); - scope.$digest(); - timeout.flush(); - expect(scope.process).toHaveBeenCalled(); - }); - }); - }); - - describe('process()', function() { describe('when runSpecial has a runtime error', function() { it('should display a global error', function() { mockEngine.runSpecial = function() { return Q.reject(new Error('error')); }; + mockErrors.special = {}; controller('SummaryQualityMacroCtrl', { $scope: scope, $location: location, $q: Q, HMDAEngine: mockEngine }); - scope.process(); + scope.next(); + timeout.flush(); scope.$digest(); expect(scope.errors.global).toBe('error'); @@ -175,13 +170,15 @@ describe('Controller: SummaryQualityMacroCtrl', function () { describe('when runSpecial has no runtime errors', function() { beforeEach(function() { mockEngine.runSpecial = function() { return; }; + mockErrors.special = {}; controller('SummaryQualityMacroCtrl', { $scope: scope, $location: location, $q: Q, HMDAEngine: mockEngine }); - scope.process(); + scope.next(); + timeout.flush(); scope.$digest(); }); diff --git a/test/spec/controllers/summarySyntacticalValidity.js b/test/spec/controllers/summarySyntacticalValidity.js index 9add049..686c857 100644 --- a/test/spec/controllers/summarySyntacticalValidity.js +++ b/test/spec/controllers/summarySyntacticalValidity.js @@ -29,6 +29,18 @@ describe('Controller: SummarySyntacticalValidityCtrl', function () { beforeEach(angular.mock.module('hmdaPilotApp')); + beforeEach(inject(function($templateCache) { + var directiveTemplate = null; + var templateId = 'partials/progressBar.html'; + var req = new XMLHttpRequest(); + req.onload = function() { + directiveTemplate = this.responseText; + }; + req.open('get', '/base/app/'+templateId, false); + req.send(); + $templateCache.put(templateId, directiveTemplate); + })); + beforeEach(inject(function ($rootScope, $location, $controller, $q, $timeout, _Wizard_, _Configuration_) { scope = $rootScope.$new(); location = $location; @@ -42,7 +54,9 @@ describe('Controller: SummarySyntacticalValidityCtrl', function () { } }; mockNgDialog = { - openConfirm: function() { } + open: function() {}, + openConfirm: function() {}, + close: function() {} }; spyOn(mockNgDialog, 'openConfirm').and.returnValue(mockNgDialogPromise); @@ -169,9 +183,7 @@ describe('Controller: SummarySyntacticalValidityCtrl', function () { expect(scope.process).toHaveBeenCalled(); }); }); - }); - describe('process()', function() { describe('when runQuality has a runtime error', function() { it('should display a global error', function() { mockEngine.runQuality = function() { return Q.reject(new Error('error')); }; @@ -182,8 +194,9 @@ describe('Controller: SummarySyntacticalValidityCtrl', function () { $q: Q, HMDAEngine: mockEngine }); - scope.process(); + scope.next(); scope.$digest(); + timeout.flush(); expect(scope.errors.global).toBe('error'); }); @@ -199,7 +212,8 @@ describe('Controller: SummarySyntacticalValidityCtrl', function () { $q: Q, HMDAEngine: mockEngine }); - scope.process(); + scope.next(); + timeout.flush(); scope.$digest(); expect(scope.errors.global).toBe('error'); @@ -216,7 +230,8 @@ describe('Controller: SummarySyntacticalValidityCtrl', function () { $q: Q, HMDAEngine: mockEngine }); - scope.process(); + scope.next(); + timeout.flush(); scope.$digest(); }); From 7dbf42c0b74e3c21998653cfafa070ac2d757835 Mon Sep 17 00:00:00 2001 From: Justin Stockton Date: Thu, 9 Apr 2015 13:12:20 -0600 Subject: [PATCH 3/6] Remove the cfButton since it was used only as a way to show the old processing button --- app/scripts/directives/cfButton.js | 124 --------------- app/scripts/directives/index.js | 1 - app/views/selectFile.html | 2 +- app/views/summaryQualityMacro.html | 2 +- app/views/summarySyntacticalValidity.html | 2 +- test/spec/directives/cfButton.js | 180 ---------------------- 6 files changed, 3 insertions(+), 308 deletions(-) delete mode 100644 app/scripts/directives/cfButton.js delete mode 100644 test/spec/directives/cfButton.js diff --git a/app/scripts/directives/cfButton.js b/app/scripts/directives/cfButton.js deleted file mode 100644 index b23bd9c..0000000 --- a/app/scripts/directives/cfButton.js +++ /dev/null @@ -1,124 +0,0 @@ -'use strict'; - -/** - * @ngdoc directive - * @name hmdaPilotApp.directive:cfButton - * @description - * # Capital Framework Button directive - * Directive for displaying a button that use the Capital Framework - */ -module.exports = /*@ngInject*/ function () { - - var origBtnText; - - function disable($btn) { - $btn.prop('disabled', true); - $btn.addClass('btn__disabled'); - return $btn; - } - - function enable($btn) { - $btn.prop('disabled', false); - $btn.removeClass('btn__disabled'); - return $btn; - } - - function showProcessing($btn) { - disable($btn); - - $btn.addClass('btn-processing'); - - var text = $btn[0].getElementsByClassName('text')[0], - icon = $btn[0].getElementsByClassName('cf-icon')[0], - iconName = icon.className.match(/(^|\s)cf-icon-\S+/g)[0] || ''; - - angular.element(icon) - .removeClass(iconName) - .addClass('cf-icon-update cf-spin'); - - angular.element(text) - .text('Processing...'); - - return $btn; - } - - function hideProcessing($btn, iconName, btnText) { - enable($btn); - - $btn.removeClass('btn-processing'); - - var text = $btn[0].getElementsByClassName('text')[0], - icon = $btn[0].getElementsByClassName('cf-icon')[0]; - - angular.element(icon) - .removeClass('cf-icon-update') - .removeClass('cf-spin') - .addClass('cf-icon-' + iconName); - - angular.element(text) - .text(btnText); - - return $btn; - } - - function displayIcon($btn, name, position) { - position = position || 'right'; // default to placing the icon on the right - - var icon = ''; - - if (position === 'right') { - $btn.append(icon); - } else { - $btn.prepend(icon); - } - return $btn; - } - - function link(scope, element, attrs) { - origBtnText = element.text(); - - if (!attrs.ngClick) { - element.attr('type', 'submit'); - } - - if (attrs.iconClass) { - displayIcon(element, attrs.iconClass, attrs.iconPosition); - } - - if (scope.processing) { - showProcessing(element); - } - - if (scope.isDisabled) { - disable(element); - } - } - - function controller($scope, $element, $attrs) { - if (!$scope.isDisabled) { // If the disabled state isn't being set 'manually' - // Watch to see if the value of processing changes - $scope.$watch('processing', function(newVal) { - if (newVal) { - showProcessing($element); - } else { - hideProcessing($element, $attrs.iconClass, origBtnText); - } - }); - } - } - - return { - restrict: 'E', - replace: true, - transclude: true, - template: '', - scope: { - processing: '=', - isDisabled: '=', - iconClass: '=', - iconPosition: '=' - }, - link: link, - controller: /*@ngInject*/ controller - }; -}; diff --git a/app/scripts/directives/index.js b/app/scripts/directives/index.js index 4bb8559..ae1123c 100644 --- a/app/scripts/directives/index.js +++ b/app/scripts/directives/index.js @@ -9,7 +9,6 @@ app.directive('errorSummary', require('./errorSummary')); app.directive('errorDetail', require('./errorDetail')); app.directive('fileMetadata', require('./fileMetadata')); app.directive('wizardNav', require('./wizardNav')); -app.directive('cfButton', require('./cfButton')); app.directive('disclaimer', require('./disclaimer')); app.directive('paginationSize', require('./paginationSize')); app.directive('paginationNav', require('./paginationNav')); diff --git a/app/views/selectFile.html b/app/views/selectFile.html index 10b54b5..e36c61e 100644 --- a/app/views/selectFile.html +++ b/app/views/selectFile.html @@ -28,6 +28,6 @@

Select File and Validate

- Start validation +
diff --git a/app/views/summaryQualityMacro.html b/app/views/summaryQualityMacro.html index ed56400..09dda31 100644 --- a/app/views/summaryQualityMacro.html +++ b/app/views/summaryQualityMacro.html @@ -13,6 +13,6 @@

Macro Edit Report

- Continue +
diff --git a/app/views/summarySyntacticalValidity.html b/app/views/summarySyntacticalValidity.html index 1c655eb..dcf3b55 100644 --- a/app/views/summarySyntacticalValidity.html +++ b/app/views/summarySyntacticalValidity.html @@ -13,6 +13,6 @@

Validity Edit Report

- Continue +
diff --git a/test/spec/directives/cfButton.js b/test/spec/directives/cfButton.js deleted file mode 100644 index 5abfb6d..0000000 --- a/test/spec/directives/cfButton.js +++ /dev/null @@ -1,180 +0,0 @@ -/*global jQuery:true*/ - -'use strict'; - -require('angular'); -require('angular-mocks'); - -describe('Directive: cfButton', function () { - - var element, - scope; - - beforeEach(angular.mock.module('hmdaPilotApp')); - - beforeEach(inject(function ($rootScope) { - scope = $rootScope.$new(); - })); - - describe('by default', function() { - beforeEach(inject(function ($compile) { - element = angular.element('A Button'); - element = $compile(element)(scope); - scope.$digest(); - })); - - it('should have type="submit", if not specified', function() { - expect(element.attr('type')).toBeDefined(); - expect(element.attr('type')).toBe('submit'); - }); - - it('should have the "btn" class', function() { - expect(element.hasClass('btn')).toBeTruthy(); - }); - - it('should not have the disabled property', function() { - expect(element.prop('disabled')).toBeFalsy(); - }); - - it('should not have the disabled class', function() { - expect(element.hasClass('btn__disabled')).toBeFalsy(); - }); - - it('should display the button text', function() { - expect(jQuery('.text', element).text()).toBe('A Button'); - }); - - it('should not display "Processing..." text', function() { - expect(jQuery('.text', element).text()).not.toBe('Processing...'); - }); - }); - - describe('icon-class', function() { - beforeEach(inject(function ($compile) { - element = angular.element('A Button'); - element = $compile(element)(scope); - scope.$digest(); - })); - - it('should display the named icon on the right by default', function() { - var el = jQuery('.cf-icon-test', element).parent(); - expect(el.hasClass('btn_icon__right')).toBeTruthy(); - }); - }); - - describe('icon-position', function() { - beforeEach(inject(function ($compile) { - element = angular.element('A Button'); - element = $compile(element)(scope); - scope.$digest(); - })); - - it('should display the icon on the left when specified', function() { - var el = jQuery('.cf-icon-test', element).parent(); - expect(el.hasClass('btn_icon__left')).toBeTruthy(); - }); - }); - - describe('if processing', function() { - beforeEach(inject(function ($compile) { - scope.isProcessing = true; - element = angular.element('A Button'); - element = $compile(element)(scope); - scope.$digest(); - })); - - it('should disable the button', function() { - expect(element.prop('disabled')).toBeTruthy(); - expect(element.hasClass('btn__disabled')).toBeTruthy(); - }); - - it('should change the button text to "Processing..."', function() { - var el = jQuery('span.text', element); - expect(el.text()).toBe('Processing...'); - expect(element.hasClass('btn-processing')).toBeTruthy(); - }); - - it('should display a spinning update icon', function() { - var el = jQuery('span.cf-icon', element); - expect(el.hasClass('cf-icon-update')).toBeTruthy(); - expect(el.hasClass('cf-spin')).toBeTruthy(); - }); - }); - - describe('if processing turns off', function() { - beforeEach(inject(function ($compile) { - scope.isProcessing = true; - element = angular.element('A Button'); - element = $compile(element)(scope); - scope.$digest(); - // Make sure that the processing is being triggered - expect(element.prop('disabled')).toBeTruthy(); - expect(element.hasClass('btn__disabled')).toBeTruthy(); - // Turn off processing - scope.isProcessing = false; - scope.$digest(); - })); - - it('should enable the button', function() { - expect(element.prop('disabled')).toBeFalsy(); - expect(element.hasClass('btn__disabled')).toBeFalsy(); - }); - - it('should reset the button text to "A Button"', function() { - var el = jQuery('span.text', element); - expect(el.text()).toBe('A Button'); - expect(element.hasClass('btn-processing')).toBeFalsy(); - }); - - it('should reset to the initial icon', function() { - var el = jQuery('span.cf-icon', element); - expect(el.hasClass('cf-icon-test')).toBeTruthy(); - expect(el.hasClass('cf-icon-update')).toBeFalsy(); - expect(el.hasClass('cf-spin')).toBeFalsy(); - }); - }); - - describe('when isDisabled evals to true', function() { - beforeEach(inject(function ($compile) { - scope.hasNext = function() { return true; }; - element = angular.element('A Button'); - element = $compile(element)(scope); - scope.$digest(); - })); - - it('should disable the button', function() { - expect(element.prop('disabled')).toBeTruthy(); - expect(element.hasClass('btn__disabled')).toBeTruthy(); - }); - }); - - describe('when the button has type="submit"', function() { - beforeEach(inject(function ($compile) { - element = angular.element('A Button'); - element = $compile(element)(scope); - scope.$digest(); - })); - - it('should pass through type="submit"', function() { - expect(element.attr('type')).toBeDefined(); - expect(element.attr('type')).toBe('submit'); - }); - }); - - describe('when the button has ngClick', function() { - beforeEach(inject(function ($compile) { - element = angular.element('A Button'); - element = $compile(element)(scope); - scope.$digest(); - })); - - it('should pass through the ngClick attr', function() { - expect(element.attr('ng-click')).toBeDefined(); - expect(element.attr('ng-click')).toBe('action()'); - }); - - it('should not include type="submit"', function() { - expect(element.attr('type')).toBeUndefined(); - }); - }); -}); From b1426118c5034ba13de6ba8e06cab03d24d6099e Mon Sep 17 00:00:00 2001 From: Justin Stockton Date: Thu, 9 Apr 2015 13:12:41 -0600 Subject: [PATCH 4/6] Remove the isProcessing flag in the controllers now that the cfButton is gone --- app/scripts/controllers/selectFile.js | 13 ++++--------- app/scripts/controllers/summaryQualityMacro.js | 10 ++-------- .../controllers/summarySyntacticalValidity.js | 10 ++-------- test/spec/controllers/selectFile.js | 4 ---- 4 files changed, 8 insertions(+), 29 deletions(-) diff --git a/app/scripts/controllers/selectFile.js b/app/scripts/controllers/selectFile.js index 44e01cd..0895b3e 100644 --- a/app/scripts/controllers/selectFile.js +++ b/app/scripts/controllers/selectFile.js @@ -21,7 +21,6 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, FileRe // Populate the $scope $scope.reportingYears = fiscalYears; - $scope.isProcessing = false; // Initialize the errors for the form fields $scope.errors = {}; @@ -47,8 +46,6 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, FileRe $scope.submit = function(hmdaData) { // Clear out any existing errors $scope.errors.global = null; - // Toggle processing flag on so that we can notify the user - $scope.isProcessing = true; // Give a name to the current step in the process (shown in the progressDialog) $scope.processStep = 'Processing HMDA file...'; @@ -71,8 +68,8 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, FileRe // Convert the file to JSON HMDAEngine.fileToJson(hmdaData.file, hmdaData.year, function(fileErr) { if (fileErr) { - // Toggle processing flag off - $scope.isProcessing = false; + // Close the progress dialog + progressDialog.close(); $scope.errors.global = fileErr; $scope.$apply(); @@ -105,13 +102,11 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, FileRe // And go the summary page $location.path('/summarySyntacticalValidity'); - // Toggle processing flag off - $scope.isProcessing = false; + // Close the progress dialog progressDialog.close(); }) .catch(function(err) { - // Toggle processing flag off - $scope.isProcessing = false; + // Close the progress dialog progressDialog.close(); $scope.errors.global = err.message; diff --git a/app/scripts/controllers/summaryQualityMacro.js b/app/scripts/controllers/summaryQualityMacro.js index a66efcd..351ff11 100644 --- a/app/scripts/controllers/summaryQualityMacro.js +++ b/app/scripts/controllers/summaryQualityMacro.js @@ -39,7 +39,6 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn // Populate the $scope $scope.errors = {}; - $scope.isProcessing = false; // Get the list of errors from the HMDAEngine var progressDialog, @@ -62,9 +61,6 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn if (hasErrors(editErrors.special)) { $location.path('/summaryMSA-IRS'); } else { - // Toggle processing flag on so that we can notify the user - $scope.isProcessing = true; - // Give a name to the current step in the process (shown in the progressDialog) $scope.processStep = 'Processing MSA/MD Data...'; @@ -97,12 +93,10 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn // And go the next summary page $location.path('/summaryMSA-IRS'); - // Toggle processing flag off - $scope.isProcessing = false; + // Close the progress dialog progressDialog.close(); }).catch(function(err) { - // Toggle processing flag off - $scope.isProcessing = false; + // Close the progress dialog progressDialog.close(); $scope.errors.global = err.message; diff --git a/app/scripts/controllers/summarySyntacticalValidity.js b/app/scripts/controllers/summarySyntacticalValidity.js index 95b291a..f545562 100644 --- a/app/scripts/controllers/summarySyntacticalValidity.js +++ b/app/scripts/controllers/summarySyntacticalValidity.js @@ -11,7 +11,6 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn // Populate the $scope $scope.errors = {}; - $scope.isProcessing = false; // Get the list of errors from the HMDAEngine var progressDialog, @@ -46,9 +45,6 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn if (hasErrors(editErrors.quality) || hasErrors(editErrors.macro)) { $location.path('/summaryQualityMacro'); } else{ - // Toggle processing flag on so that we can notify the user - $scope.isProcessing = true; - // Give a name to the current step in the process (shown in the progressDialog) $scope.processStep = 'Processing Quality and Macro edits...'; @@ -82,13 +78,11 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn // And go the next summary page $location.path('/summaryQualityMacro'); - // Toggle processing flag off - $scope.isProcessing = false; + // Close the progress dialog progressDialog.close(); }) .catch(function(err) { - // Toggle processing flag off - $scope.isProcessing = false; + // Close the progress dialog progressDialog.close(); $scope.errors.global = err.message; diff --git a/test/spec/controllers/selectFile.js b/test/spec/controllers/selectFile.js index d0c69e9..c070324 100644 --- a/test/spec/controllers/selectFile.js +++ b/test/spec/controllers/selectFile.js @@ -79,10 +79,6 @@ describe('Controller: SelectFileCtrl', function () { expect(scope.errors).toBeDefined(); expect(scope.errors).toEqual({}); }); - - it('should set the isProcessing flag to false', function () { - expect(scope.isProcessing).toBeFalsy(); - }); }); describe('getFile()', function() { From 9fee9f78026aa11ed7ee3c521fccf3b51e2af455 Mon Sep 17 00:00:00 2001 From: Justin Stockton Date: Thu, 9 Apr 2015 22:06:17 -0600 Subject: [PATCH 5/6] Access the error values directly from the engine rather than setting a variable in the scope to hold a reference to the errors object. This was causing a problem in some circumstances where after $digest the values set in the $scope were reset to null. This wasn't a problem when we had the cfButton directive in place because it had an isolated scope and so once we passed the values into the directive they were no longer being updated in the controller after $digest. Still not 100% sure why this was happening, but this does fix the issue while only making the code a little more wordier in some places. --- app/scripts/controllers/summaryMSA-IRS.js | 4 +--- app/scripts/controllers/summaryQualityMacro.js | 9 ++++----- .../controllers/summarySyntacticalValidity.js | 16 +++++++--------- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/app/scripts/controllers/summaryMSA-IRS.js b/app/scripts/controllers/summaryMSA-IRS.js index 76c78ad..88b4e28 100644 --- a/app/scripts/controllers/summaryMSA-IRS.js +++ b/app/scripts/controllers/summaryMSA-IRS.js @@ -24,10 +24,8 @@ module.exports = /*@ngInject*/ function ($scope, $location, Wizard, HMDAEngine, } // Get the list of errors from the HMDAEngine - var editErrors = HMDAEngine.getErrors(); - $scope.data = { - specialErrors: editErrors.special + specialErrors: HMDAEngine.getErrors().special }; $scope.showIRSReport = function() { diff --git a/app/scripts/controllers/summaryQualityMacro.js b/app/scripts/controllers/summaryQualityMacro.js index 351ff11..7e3544a 100644 --- a/app/scripts/controllers/summaryQualityMacro.js +++ b/app/scripts/controllers/summaryQualityMacro.js @@ -41,12 +41,11 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn $scope.errors = {}; // Get the list of errors from the HMDAEngine - var progressDialog, - editErrors = HMDAEngine.getErrors(); + var progressDialog; $scope.data = { - qualityErrors: editErrors.quality, - macroErrors: editErrors.macro + qualityErrors: HMDAEngine.getErrors().quality, + macroErrors: HMDAEngine.getErrors().macro }; $scope.previous = function () { @@ -58,7 +57,7 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn }; $scope.next = function() { - if (hasErrors(editErrors.special)) { + if (hasErrors(HMDAEngine.getErrors().special)) { $location.path('/summaryMSA-IRS'); } else { // Give a name to the current step in the process (shown in the progressDialog) diff --git a/app/scripts/controllers/summarySyntacticalValidity.js b/app/scripts/controllers/summarySyntacticalValidity.js index f545562..b17e8cf 100644 --- a/app/scripts/controllers/summarySyntacticalValidity.js +++ b/app/scripts/controllers/summarySyntacticalValidity.js @@ -9,15 +9,13 @@ */ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEngine, Wizard, ngDialog, Configuration) { - // Populate the $scope - $scope.errors = {}; - // Get the list of errors from the HMDAEngine - var progressDialog, - editErrors = HMDAEngine.getErrors(); + var progressDialog; - $scope.syntacticalErrors = editErrors.syntactical || {}; - $scope.validityErrors = editErrors.validity || {}; + // Populate the $scope + $scope.errors = {}; + $scope.syntacticalErrors = HMDAEngine.getErrors().syntactical; + $scope.validityErrors = HMDAEngine.getErrors().validity; $scope.previous = function() { if (Configuration.confirmSessionReset) { @@ -34,7 +32,7 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn }; $scope.hasNext = function() { - return angular.equals({}, $scope.syntacticalErrors) && angular.equals({}, $scope.validityErrors); + return angular.equals({}, HMDAEngine.getErrors().syntactical) && angular.equals({}, HMDAEngine.getErrors().validity); }; function hasErrors(obj) { @@ -42,7 +40,7 @@ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEn } $scope.next = function() { - if (hasErrors(editErrors.quality) || hasErrors(editErrors.macro)) { + if (hasErrors(HMDAEngine.getErrors().quality) || hasErrors(HMDAEngine.getErrors().macro)) { $location.path('/summaryQualityMacro'); } else{ // Give a name to the current step in the process (shown in the progressDialog) From 5135aecc7a5c578c293299cb6f6870b4f28cbb51 Mon Sep 17 00:00:00 2001 From: Justin Stockton Date: Fri, 10 Apr 2015 13:10:55 -0600 Subject: [PATCH 6/6] Clear the progress data with each controller --- app/scripts/controllers/summaryQualityMacro.js | 3 +++ app/scripts/controllers/summarySyntacticalValidity.js | 3 +++ test/spec/controllers/summaryQualityMacro.js | 3 ++- test/spec/controllers/summarySyntacticalValidity.js | 3 ++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/scripts/controllers/summaryQualityMacro.js b/app/scripts/controllers/summaryQualityMacro.js index 7e3544a..c792cb7 100644 --- a/app/scripts/controllers/summaryQualityMacro.js +++ b/app/scripts/controllers/summaryQualityMacro.js @@ -9,6 +9,9 @@ */ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEngine, Wizard, Session, ngDialog, Configuration) { /*jshint ignore:line*/ + // Set/Reset the state of different objects on load + HMDAEngine.clearProgress(); + Array.prototype.diff = function(a) { return this.filter(function(i) { return a.indexOf(i) < 0; }); }; diff --git a/app/scripts/controllers/summarySyntacticalValidity.js b/app/scripts/controllers/summarySyntacticalValidity.js index b17e8cf..7d623de 100644 --- a/app/scripts/controllers/summarySyntacticalValidity.js +++ b/app/scripts/controllers/summarySyntacticalValidity.js @@ -9,6 +9,9 @@ */ module.exports = /*@ngInject*/ function ($scope, $location, $q, $timeout, HMDAEngine, Wizard, ngDialog, Configuration) { + // Set/Reset the state of different objects on load + HMDAEngine.clearProgress(); + // Get the list of errors from the HMDAEngine var progressDialog; diff --git a/test/spec/controllers/summaryQualityMacro.js b/test/spec/controllers/summaryQualityMacro.js index b8fecf5..293ed1e 100644 --- a/test/spec/controllers/summaryQualityMacro.js +++ b/test/spec/controllers/summaryQualityMacro.js @@ -21,7 +21,8 @@ describe('Controller: SummaryQualityMacroCtrl', function () { getErrors: function() { return mockErrors; }, getRuleYear: function() { return '2015'; }, runSpecial: function(year, next) { return next(null); }, - getDebug: function() { return false; } + getDebug: function() { return false; }, + clearProgress: function () { return {}; } }; beforeEach(angular.mock.module('hmdaPilotApp')); diff --git a/test/spec/controllers/summarySyntacticalValidity.js b/test/spec/controllers/summarySyntacticalValidity.js index 686c857..7016c53 100644 --- a/test/spec/controllers/summarySyntacticalValidity.js +++ b/test/spec/controllers/summarySyntacticalValidity.js @@ -24,7 +24,8 @@ describe('Controller: SummarySyntacticalValidityCtrl', function () { getRuleYear: function() { return '2015'; }, runQuality: function(year, next) { return next(null); }, runMacro: function(year, next) { return next(null); }, - getDebug: function() { return false; } + getDebug: function() { return false; }, + clearProgress: function () { return {}; } }; beforeEach(angular.mock.module('hmdaPilotApp'));