From ffbfe81465ccce21bbc3e3d1f7ef9c26450b6dee Mon Sep 17 00:00:00 2001 From: Kelly Domico Date: Fri, 5 Jun 2015 18:26:53 -0700 Subject: [PATCH] JSCS cleanup - horizon/static/framework conf and util We need to do cleanup before we can enable JSCS globally (https://review.openstack.org/#/c/185725/). This patch excludes the 'util/tech-debt' folder which is covered in patch: https://review.openstack.org/#/c/188316/. Change-Id: I0b3e6435d48a24c0b24e38c96b1ce679c3268300 Partially-Implements: blueprint jscs-cleanup --- horizon/static/framework/conf/conf.js | 10 +- horizon/static/framework/framework.module.js | 66 +-- .../framework/util/bind-scope/bind-scope.js | 11 +- .../util/bind-scope/bind-scope.spec.js | 23 +- .../static/framework/util/filters/filters.js | 245 ++++++----- .../framework/util/filters/filters.spec.js | 397 +++++++++--------- horizon/static/framework/util/i18n/i18n.js | 64 ++- .../static/framework/util/i18n/i18n.spec.js | 30 +- horizon/static/framework/util/util.module.js | 3 +- .../framework/util/validators/validators.js | 257 ++++++------ .../util/validators/validators.spec.js | 60 ++- .../framework/util/workflow/workflow.js | 1 - .../framework/util/workflow/workflow.spec.js | 21 +- .../framework/widgets/widgets.module.js | 2 +- 14 files changed, 582 insertions(+), 608 deletions(-) diff --git a/horizon/static/framework/conf/conf.js b/horizon/static/framework/conf/conf.js index 6fd50d1f9d..f3d31f3b57 100644 --- a/horizon/static/framework/conf/conf.js +++ b/horizon/static/framework/conf/conf.js @@ -1,10 +1,10 @@ -/*global angular*/ (function () { 'use strict'; - angular.module('horizon.framework.conf', []) + angular + .module('horizon.framework.conf', []) .constant('horizon.framework.conf.spinner_options', { - inline: { + inline: { lines: 10, length: 5, width: 2, @@ -14,7 +14,7 @@ trail: 50, zIndex: 100 }, - modal: { + modal: { lines: 10, length: 15, width: 4, @@ -33,4 +33,4 @@ trail: 50 } }); -}()); +})(); diff --git a/horizon/static/framework/framework.module.js b/horizon/static/framework/framework.module.js index 7f255e8155..2e8068dceb 100644 --- a/horizon/static/framework/framework.module.js +++ b/horizon/static/framework/framework.module.js @@ -1,39 +1,41 @@ (function () { 'use strict'; - angular.module('horizon.framework', [ - 'horizon.framework.conf', - 'horizon.framework.util', - 'horizon.framework.widgets' - ]) - .constant('horizon.framework.basePath', '/static/framework/') + angular + .module('horizon.framework', [ + 'horizon.framework.conf', + 'horizon.framework.util', + 'horizon.framework.widgets' + ]) + .constant('horizon.framework.basePath', '/static/framework/') + .config([ + '$interpolateProvider', + '$httpProvider', + function ($interpolateProvider, $httpProvider) { + // Replacing the default angular symbol + // allow us to mix angular with django templates + $interpolateProvider.startSymbol('{$'); + $interpolateProvider.endSymbol('$}'); - .config(['$interpolateProvider', '$httpProvider', - function ($interpolateProvider, $httpProvider) { + // Http global settings for ease of use + $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; + $httpProvider.defaults.xsrfCookieName = 'csrftoken'; + $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; + $httpProvider.defaults.headers.common['Content-Type'] = 'application/json;charset=utf-8'; - // Replacing the default angular symbol - // allow us to mix angular with django templates - $interpolateProvider.startSymbol('{$'); - $interpolateProvider.endSymbol('$}'); - - // Http global settings for ease of use - $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; - $httpProvider.defaults.xsrfCookieName = 'csrftoken'; - $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; - $httpProvider.defaults.headers.common['Content-Type'] = 'application/json;charset=utf-8'; - - // Global http error handler - // if user is not authorized, log user out - // this can happen when session expires - $httpProvider.interceptors.push(function ($q) { - return { - responseError: function (error) { - if (error.status === 401) { - window.location.replace('/auth/logout'); + // Global http error handler + // if user is not authorized, log user out + // this can happen when session expires + $httpProvider.interceptors.push(function ($q) { + return { + responseError: function (error) { + if (error.status === 401) { + window.location.replace('/auth/logout'); + } + return $q.reject(error); } - return $q.reject(error); - } - }; - }); - }]); + }; + }); + } + ]); })(); diff --git a/horizon/static/framework/util/bind-scope/bind-scope.js b/horizon/static/framework/util/bind-scope/bind-scope.js index 50ad0be55c..9f3e296ed4 100644 --- a/horizon/static/framework/util/bind-scope/bind-scope.js +++ b/horizon/static/framework/util/bind-scope/bind-scope.js @@ -1,4 +1,4 @@ -(function() { +(function () { 'use strict'; /** @@ -41,12 +41,12 @@ * * ``` */ - .directive('bindScope', function() { + .directive('bindScope', function () { return { restrict: 'A', - link: function(scope, element, attrs, ctrl, transclude) { + link: function (scope, element, attrs, ctrl, transclude) { if (transclude) { - transclude(scope, function(clone) { + transclude(scope, function (clone) { var detailElt = element.find('[bind-scope-target]'); if (detailElt.length) { detailElt.append(clone); @@ -56,5 +56,4 @@ } }; }); - -})(); \ No newline at end of file +})(); diff --git a/horizon/static/framework/util/bind-scope/bind-scope.spec.js b/horizon/static/framework/util/bind-scope/bind-scope.spec.js index b479d0da20..a929dc4197 100644 --- a/horizon/static/framework/util/bind-scope/bind-scope.spec.js +++ b/horizon/static/framework/util/bind-scope/bind-scope.spec.js @@ -1,22 +1,21 @@ -(function() { +(function () { 'use strict'; - describe('horizon.framework.util.bind-scope module', function() { - it('should have been defined', function() { + describe('horizon.framework.util.bind-scope module', function () { + it('should have been defined', function () { expect(angular.module('horizon.framework.util.bind-scope')).toBeDefined(); }); }); - describe('bind-scope directive', function() { - + describe('bind-scope directive', function () { var $scope, $element; beforeEach(module('horizon.framework')); beforeEach(module('horizon.framework.widgets')); beforeEach(module('horizon.framework.util.bind-scope')); - beforeEach(module('horizon.framework.util.bind-scope', function($compileProvider) { - $compileProvider.directive('testBindScope', function() { + beforeEach(module('horizon.framework.util.bind-scope', function ($compileProvider) { + $compileProvider.directive('testBindScope', function () { return { restrict: 'E', scope: { @@ -30,7 +29,7 @@ }); })); - beforeEach(inject(function($injector) { + beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); @@ -48,17 +47,15 @@ $scope.$digest(); })); - it('should have 3 list items', function() { + it('should have 3 list items', function () { expect($element.find('li').length).toBe(3); }); - it('should have 3 list items with values "cat", "dog" and "fish"', function() { + it('should have 3 list items with values "cat", "dog" and "fish"', function () { var listItems = $element.find('li'); expect(listItems[0].textContent.trim()).toBe('cat'); expect(listItems[1].textContent.trim()).toBe('dog'); expect(listItems[2].textContent.trim()).toBe('fish'); }); - }); - -})(); \ No newline at end of file +})(); diff --git a/horizon/static/framework/util/filters/filters.js b/horizon/static/framework/util/filters/filters.js index 75d6346045..c9b0e929ee 100644 --- a/horizon/static/framework/util/filters/filters.js +++ b/horizon/static/framework/util/filters/filters.js @@ -25,128 +25,128 @@ */ angular.module('horizon.framework.util.filters', ['horizon.framework.util.i18n']) - /** - * @ngdoc filter - * @name yesno - * @description - * Evaluates given input for standard truthiness and returns translation - * of 'Yes' and 'No' for true/false respectively. - */ - .filter('yesno', ['horizon.framework.util.i18n.gettext', function(gettext) { - return function(input) { - return (input ? gettext("Yes") : gettext("No")); - }; - }]) + /** + * @ngdoc filter + * @name yesno + * @description + * Evaluates given input for standard truthiness and returns translation + * of 'Yes' and 'No' for true/false respectively. + */ + .filter('yesno', ['horizon.framework.util.i18n.gettext', function (gettext) { + return function (input) { + return (input ? gettext("Yes") : gettext("No")); + }; + }]) - /** - * @ngdoc filter - * @name gb - * @description - * Expects numeric value and suffixes translated 'GB' with spacing. - * Returns empty string if input is not a number or is null. - */ - .filter('gb', function() { - return function(input) { - if (isNaN(input) || null === input) { - return ''; - } else { - return interpolate(gettext("%s GB"), [input.toString()]); - } - }; - }) + /** + * @ngdoc filter + * @name gb + * @description + * Expects numeric value and suffixes translated 'GB' with spacing. + * Returns empty string if input is not a number or is null. + */ + .filter('gb', function () { + return function (input) { + if (isNaN(input) || null === input) { + return ''; + } else { + return interpolate(gettext("%s GB"), [input.toString()]); + } + }; + }) - /** - * @ngdoc filter - * @name mb - * @description - * Expects numeric value and suffixes translated 'MB' with spacing. - * Returns empty string if input is not a number or is null. - */ - .filter('mb', function() { - return function(input) { - if (isNaN(input) || null === input) { - return ''; - } else { - return interpolate(gettext("%s MB"), [input.toString()]); - } - }; - }) + /** + * @ngdoc filter + * @name mb + * @description + * Expects numeric value and suffixes translated 'MB' with spacing. + * Returns empty string if input is not a number or is null. + */ + .filter('mb', function () { + return function (input) { + if (isNaN(input) || null === input) { + return ''; + } else { + return interpolate(gettext("%s MB"), [input.toString()]); + } + }; + }) - /** - * @ngdoc filter - * @name title - * @description - * Capitalizes leading characters of individual words. - */ - .filter('title', function() { - return function(input) { - if (typeof input !== 'string') { - return input; - } - return input.replace(/(?:^|\s)\S/g, function(a) { - return a.toUpperCase(); - }); - }; - }) + /** + * @ngdoc filter + * @name title + * @description + * Capitalizes leading characters of individual words. + */ + .filter('title', function () { + return function (input) { + if (typeof input !== 'string') { + return input; + } + return input.replace(/(?:^|\s)\S/g, function (a) { + return a.toUpperCase(); + }); + }; + }) - /** - * @ngdoc filter - * @name noUnderscore - * @description - * Replaces all underscores with spaces. - */ - .filter('noUnderscore', function() { - return function(input) { - if (typeof input !== 'string') { - return input; - } - return input.replace(/_/g, ' '); - }; - }) + /** + * @ngdoc filter + * @name noUnderscore + * @description + * Replaces all underscores with spaces. + */ + .filter('noUnderscore', function () { + return function (input) { + if (typeof input !== 'string') { + return input; + } + return input.replace(/_/g, ' '); + }; + }) - /** - * @ngdoc filter - * @name decode - * @description - * Returns values based on key and given mapping. If key doesn't exist - * in given mapping, return key. This is useful when translations for - * codes are present. - */ - .filter('decode', function() { - return function(input, mapping) { - var val = mapping[input]; - return angular.isDefined(val) ? val : input; - }; - }) + /** + * @ngdoc filter + * @name decode + * @description + * Returns values based on key and given mapping. If key doesn't exist + * in given mapping, return key. This is useful when translations for + * codes are present. + */ + .filter('decode', function () { + return function (input, mapping) { + var val = mapping[input]; + return angular.isDefined(val) ? val : input; + }; + }) - /** - * @ngdoc filter - * @name bytes - * @description - * Returns a human-readable approximation of the input of bytes, - * converted to a useful unit of measure. Uses 1024-based notation. - */ - .filter('bytes', function() { - return function(input) { - var kb = 1024; - var mb = kb*1024; - var gb = mb*1024; - var tb = gb*1024; - if (isNaN(input) || null === input || input < 0) { - return ''; - } else if (input >= tb) { - return interpolate(gettext("%s TB"), [Number(input/tb).toFixed(2)]); - } else if (input >= gb) { - return interpolate(gettext("%s GB"), [Number(input/gb).toFixed(2)]); - } else if (input >= mb) { - return interpolate(gettext("%s MB"), [Number(input/mb).toFixed(2)]); - } else if (input >= kb) { - return interpolate(gettext("%s KB"), [Number(input/kb).toFixed(2)]); - } else { - return interpolate(gettext("%s bytes"), [Math.floor(input)]); - } - }; - }) + /** + * @ngdoc filter + * @name bytes + * @description + * Returns a human-readable approximation of the input of bytes, + * converted to a useful unit of measure. Uses 1024-based notation. + */ + .filter('bytes', function () { + return function (input) { + var kb = 1024; + var mb = kb * 1024; + var gb = mb * 1024; + var tb = gb * 1024; + if (isNaN(input) || null === input || input < 0) { + return ''; + } else if (input >= tb) { + return interpolate(gettext("%s TB"), [Number(input / tb).toFixed(2)]); + } else if (input >= gb) { + return interpolate(gettext("%s GB"), [Number(input / gb).toFixed(2)]); + } else if (input >= mb) { + return interpolate(gettext("%s MB"), [Number(input / mb).toFixed(2)]); + } else if (input >= kb) { + return interpolate(gettext("%s KB"), [Number(input / kb).toFixed(2)]); + } else { + return interpolate(gettext("%s bytes"), [Math.floor(input)]); + } + }; + }) /** * @ngdoc filter @@ -155,11 +155,11 @@ * Displays translated count in table footer. * Takes only finite numbers. */ - .filter('itemCount', function() { - return function(input) { + .filter('itemCount', function () { + return function (input) { var isNumeric = (input !== null && isFinite(input)); - var number = isNumeric ? Math.round(input): 0; - var count = (number > 0) ? number: 0; + var number = isNumeric ? Math.round(input) : 0; + var count = (number > 0) ? number : 0; var format = ngettext('Displaying %s item', 'Displaying %s items', count); return interpolate(format, [count]); }; @@ -171,11 +171,10 @@ * @description * Returns translated text. */ - .filter('trans', ['horizon.framework.util.i18n.gettext', function(gettextFunc) { - return function(input) { + .filter('trans', ['horizon.framework.util.i18n.gettext', function (gettextFunc) { + return function (input) { // NOTE: uses 'gettextFunc' to avoid message collection. return gettextFunc(input); }; }]); - }()); diff --git a/horizon/static/framework/util/filters/filters.spec.js b/horizon/static/framework/util/filters/filters.spec.js index e4a89e7e00..7734bfdd0a 100644 --- a/horizon/static/framework/util/filters/filters.spec.js +++ b/horizon/static/framework/util/filters/filters.spec.js @@ -1,220 +1,211 @@ -describe('horizon.framework.util.filters', function () { +(function () { 'use strict'; - beforeEach(module('horizon.framework.util.i18n')); - beforeEach(module('horizon.framework.util.filters')); + describe('horizon.framework.util.filters', function () { + beforeEach(module('horizon.framework.util.i18n')); + beforeEach(module('horizon.framework.util.filters')); - describe('yesno', function () { - var yesnoFilter; - beforeEach(inject(function (_yesnoFilter_) { - yesnoFilter = _yesnoFilter_; - })); + describe('yesno', function () { + var yesnoFilter; + beforeEach(inject(function (_yesnoFilter_) { + yesnoFilter = _yesnoFilter_; + })); - it('returns Yes for true', function () { - expect(yesnoFilter(true)).toBe('Yes'); + it('returns Yes for true', function () { + expect(yesnoFilter(true)).toBe('Yes'); + }); + + it('returns No for false', function () { + expect(yesnoFilter(false)).toBe('No'); + }); + + it('returns No for null', function () { + expect(yesnoFilter(null)).toBe('No'); + }); + + it('returns No for undefined', function () { + expect(yesnoFilter(undefined)).toBe('No'); + }); + + it('returns Yes for other truthy values', function () { + expect(yesnoFilter(7)).toBe('Yes'); + expect(yesnoFilter('C')).toBe('Yes'); + expect(yesnoFilter('This will be truthy')).toBe('Yes'); + }); + + it('returns No for other falsy values', function () { + expect(yesnoFilter(0)).toBe('No'); + expect(yesnoFilter('')).toBe('No'); + }); }); - it('returns No for false', function () { - expect(yesnoFilter(false)).toBe('No'); + describe('gb', function () { + var gbFilter; + beforeEach(inject(function (_gbFilter_) { + gbFilter = _gbFilter_; + })); + + it('returns given numeric value properly', function () { + expect(gbFilter(12)).toBe('12 GB'); + expect(gbFilter(-12)).toBe('-12 GB'); + expect(gbFilter(12.12)).toBe('12.12 GB'); + }); + + it('returns empty string for non-numeric', function () { + expect(gbFilter('humbug')).toBe(''); + }); + + it('returns empty string for null', function () { + expect(gbFilter(null)).toBe(''); + }); }); - it('returns No for null', function () { - expect(yesnoFilter(null)).toBe('No'); + describe('mb', function () { + var mbFilter; + beforeEach(inject(function (_mbFilter_) { + mbFilter = _mbFilter_; + })); + + it('returns given numeric value properly', function () { + expect(mbFilter(12)).toBe('12 MB'); + expect(mbFilter(-12)).toBe('-12 MB'); + expect(mbFilter(12.12)).toBe('12.12 MB'); + }); + + it('returns empty string for non-numeric', function () { + expect(mbFilter('humbug')).toBe(''); + }); + + it('returns empty string for null', function () { + expect(mbFilter(null)).toBe(''); + }); }); - it('returns No for undefined', function () { - expect(yesnoFilter(undefined)).toBe('No'); + describe('title', function () { + var titleFilter; + beforeEach(inject(function (_titleFilter_) { + titleFilter = _titleFilter_; + })); + + it('capitalizes as expected', function () { + expect(titleFilter('title')).toBe('Title'); + expect(titleFilter('we have several words')).toBe('We Have Several Words'); + }); + + it('handles non-strings correctly', function () { + expect(titleFilter(42)).toBe(42); + expect(titleFilter(null)).toBe(null); + expect(titleFilter(undefined)).toBe(undefined); + }); + + it('does not mess up properly capitalized strings', function () { + expect(titleFilter('I Love OpenStack Horizon!')).toBe('I Love OpenStack Horizon!'); + }); + + it('handles strings beginning with numbers', function () { + expect(titleFilter('3abc')).toBe('3abc'); + }); }); - it('returns Yes for other truthy values', function () { - expect(yesnoFilter(7)).toBe('Yes'); - expect(yesnoFilter('C')).toBe('Yes'); - expect(yesnoFilter('This will be truthy')).toBe('Yes'); + describe('noUnderscore', function () { + var noUnderscoreFilter; + beforeEach(inject(function (_noUnderscoreFilter_) { + noUnderscoreFilter = _noUnderscoreFilter_; + })); + + it('replaces all underscores with spaces', function () { + expect(noUnderscoreFilter('_this_is___a_lot____of_underscores__')) + .toBe(' this is a lot of underscores '); + }); + + it('returns non-string input', function () { + expect(noUnderscoreFilter(null)).toBe(null); + expect(noUnderscoreFilter(false)).toBe(false); + expect(noUnderscoreFilter(true)).toBe(true); + expect(noUnderscoreFilter('')).toBe(''); + expect(noUnderscoreFilter(21)).toBe(21); + }); }); - it('returns No for other falsy values', function () { - expect(yesnoFilter(0)).toBe('No'); - expect(yesnoFilter('')).toBe('No'); + describe("decode", function () { + var decodeFilter; + beforeEach(inject(function (_decodeFilter_) { + decodeFilter = _decodeFilter_; + })); + + it("Returns value when key is present", function () { + expect(decodeFilter('PRESENT', {'PRESENT': 'Here'})).toBe('Here'); + }); + + it("Returns value when key is present and value is falsy", function () { + expect(decodeFilter('PRESENT', {'PRESENT': false})).toBe(false); + }); + + it("Returns input when key is not present", function () { + expect(decodeFilter('NOT_PRESENT', {'PRESENT': 'Here'})).toBe('NOT_PRESENT'); + }); }); + describe('bytes', function () { + var bytesFilter; + beforeEach(inject(function (_bytesFilter_) { + bytesFilter = _bytesFilter_; + })); + + it('returns TB values', function () { + expect(bytesFilter(1099511627776)).toBe('1.00 TB'); + }); + + it('returns GB values', function () { + expect(bytesFilter(1073741824)).toBe('1.00 GB'); + }); + + it('returns MB values', function () { + expect(bytesFilter(1048576)).toBe('1.00 MB'); + }); + + it('returns KB values', function () { + expect(bytesFilter(1024)).toBe('1.00 KB'); + }); + + it('returns byte values', function () { + expect(bytesFilter(0)).toBe('0 bytes'); + expect(bytesFilter(1)).toBe('1 bytes'); + expect(bytesFilter(1023)).toBe('1023 bytes'); + }); + + it('handles non-numbers correctly', function () { + expect(bytesFilter('Yo!')).toBe(''); + expect(bytesFilter(null)).toBe(''); + }); + }); + + describe('itemCount', function () { + it('should return translated text with item count', + inject(function (itemCountFilter) { + expect(itemCountFilter(null)).toBe('Displaying 0 items'); + expect(itemCountFilter(undefined)).toBe('Displaying 0 items'); + expect(itemCountFilter(true)).toBe('Displaying 1 item'); + expect(itemCountFilter(false)).toBe('Displaying 0 items'); + expect(itemCountFilter('a')).toBe('Displaying 0 items'); + expect(itemCountFilter('0')).toBe('Displaying 0 items'); + expect(itemCountFilter('1')).toBe('Displaying 1 item'); + expect(itemCountFilter('1e1')).toBe('Displaying 10 items'); + expect(itemCountFilter('1b1')).toBe('Displaying 0 items'); + expect(itemCountFilter(0)).toBe('Displaying 0 items'); + expect(itemCountFilter(1)).toBe('Displaying 1 item'); + expect(itemCountFilter(1.2)).toBe('Displaying 1 item'); + expect(itemCountFilter(1.6)).toBe('Displaying 2 items'); + expect(itemCountFilter(-1)).toBe('Displaying 0 items'); + expect(itemCountFilter(-1.2)).toBe('Displaying 0 items'); + }) + ); + }); + + describe('trans', function () { + it('should return translated text', inject(function (transFilter) { + expect(transFilter("Howdy")).toBe("Howdy"); + })); + }); }); - - describe('gb', function () { - var gbFilter; - beforeEach(inject(function (_gbFilter_) { - gbFilter = _gbFilter_; - })); - - it('returns given numeric value properly', function () { - expect(gbFilter(12)).toBe('12 GB'); - expect(gbFilter(-12)).toBe('-12 GB'); - expect(gbFilter(12.12)).toBe('12.12 GB'); - }); - - it('returns empty string for non-numeric', function () { - expect(gbFilter('humbug')).toBe(''); - }); - - it('returns empty string for null', function () { - expect(gbFilter(null)).toBe(''); - }); - - }); - - describe('mb', function () { - var mbFilter; - beforeEach(inject(function (_mbFilter_) { - mbFilter = _mbFilter_; - })); - - it('returns given numeric value properly', function () { - expect(mbFilter(12)).toBe('12 MB'); - expect(mbFilter(-12)).toBe('-12 MB'); - expect(mbFilter(12.12)).toBe('12.12 MB'); - }); - - it('returns empty string for non-numeric', function () { - expect(mbFilter('humbug')).toBe(''); - }); - - it('returns empty string for null', function () { - expect(mbFilter(null)).toBe(''); - }); - - }); - - describe('title', function () { - var titleFilter; - beforeEach(inject(function (_titleFilter_) { - titleFilter = _titleFilter_; - })); - - it('capitalizes as expected', function () { - expect(titleFilter('title')).toBe('Title'); - expect(titleFilter('we have several words')).toBe('We Have Several Words'); - }); - - it('handles non-strings correctly', function () { - expect(titleFilter(42)).toBe(42); - expect(titleFilter(null)).toBe(null); - expect(titleFilter(undefined)).toBe(undefined); - }); - - it('does not mess up properly capitalized strings', function () { - expect(titleFilter('I Love OpenStack Horizon!')).toBe('I Love OpenStack Horizon!'); - }); - - it('handles strings beginning with numbers', function () { - expect(titleFilter('3abc')).toBe('3abc'); - }); - - }); - - describe('noUnderscore', function () { - var noUnderscoreFilter; - beforeEach(inject(function (_noUnderscoreFilter_) { - noUnderscoreFilter = _noUnderscoreFilter_; - })); - - it('replaces all underscores with spaces', function () { - expect(noUnderscoreFilter('_this_is___a_lot____of_underscores__')) - .toBe(' this is a lot of underscores '); - }); - - it('returns non-string input', function () { - expect(noUnderscoreFilter(null)).toBe(null); - expect(noUnderscoreFilter(false)).toBe(false); - expect(noUnderscoreFilter(true)).toBe(true); - expect(noUnderscoreFilter('')).toBe(''); - expect(noUnderscoreFilter(21)).toBe(21); - }); - - }); - - describe("decode", function () { - var decodeFilter; - beforeEach(inject(function (_decodeFilter_) { - decodeFilter = _decodeFilter_; - })); - - it("Returns value when key is present", function () { - expect(decodeFilter('PRESENT', {'PRESENT': 'Here'})).toBe('Here'); - }); - - it("Returns value when key is present and value is falsy", function () { - expect(decodeFilter('PRESENT', {'PRESENT': false})).toBe(false); - }); - - it("Returns input when key is not present", function () { - expect(decodeFilter('NOT_PRESENT', {'PRESENT': 'Here'})).toBe('NOT_PRESENT'); - }); - - }); - - describe('bytes', function () { - var bytesFilter; - beforeEach(inject(function (_bytesFilter_) { - bytesFilter = _bytesFilter_; - })); - - it('returns TB values', function () { - expect(bytesFilter(1099511627776)).toBe('1.00 TB'); - }); - - it('returns GB values', function () { - expect(bytesFilter(1073741824)).toBe('1.00 GB'); - }); - - it('returns MB values', function () { - expect(bytesFilter(1048576)).toBe('1.00 MB'); - }); - - it('returns KB values', function () { - expect(bytesFilter(1024)).toBe('1.00 KB'); - }); - - it('returns byte values', function () { - expect(bytesFilter(0)).toBe('0 bytes'); - expect(bytesFilter(1)).toBe('1 bytes'); - expect(bytesFilter(1023)).toBe('1023 bytes'); - }); - - it('handles non-numbers correctly', function () { - expect(bytesFilter('Yo!')).toBe(''); - expect(bytesFilter(null)).toBe(''); - }); - - }); - - describe('itemCount', function () { - - it('should return translated text with item count', - inject(function (itemCountFilter) { - expect(itemCountFilter(null)).toBe('Displaying 0 items'); - expect(itemCountFilter(undefined)).toBe('Displaying 0 items'); - expect(itemCountFilter(true)).toBe('Displaying 1 item'); - expect(itemCountFilter(false)).toBe('Displaying 0 items'); - expect(itemCountFilter('a')).toBe('Displaying 0 items'); - expect(itemCountFilter('0')).toBe('Displaying 0 items'); - expect(itemCountFilter('1')).toBe('Displaying 1 item'); - expect(itemCountFilter('1e1')).toBe('Displaying 10 items'); - expect(itemCountFilter('1b1')).toBe('Displaying 0 items'); - expect(itemCountFilter(0)).toBe('Displaying 0 items'); - expect(itemCountFilter(1)).toBe('Displaying 1 item'); - expect(itemCountFilter(1.2)).toBe('Displaying 1 item'); - expect(itemCountFilter(1.6)).toBe('Displaying 2 items'); - expect(itemCountFilter(-1)).toBe('Displaying 0 items'); - expect(itemCountFilter(-1.2)).toBe('Displaying 0 items'); - }) - ); - - }); - - describe('trans', function() { - - it('should return translated text', inject(function(transFilter) { - expect(transFilter("Howdy")).toBe("Howdy"); - })); - - }); -}); +})(); diff --git a/horizon/static/framework/util/i18n/i18n.js b/horizon/static/framework/util/i18n/i18n.js index 1c8ebf2ac3..fdf59eede4 100644 --- a/horizon/static/framework/util/i18n/i18n.js +++ b/horizon/static/framework/util/i18n/i18n.js @@ -13,43 +13,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -(function() { +(function () { 'use strict'; - angular.module("horizon.framework.util.i18n", []) - - /* - * @name horizon.framework.util.i18n.gettext - * @description - * Provides a wrapper for translation, using the global 'gettext' - * function if it is present. Provides a method that - * simply returns the input if the expected global 'gettext' is - * not provided. - * - * Ideally once gettext is no longer needed on a global scope, - * the global ref can be deleted here. For now that is not possible. - * Also, if future alternate means were provided, we could put that - * logic here. - * - * This could also be done in the context of the filter, but - * the approach taken here was to separate business logic - * (translation) from filters, which are arguably more - * presentation-oriented. - */ - .factory("horizon.framework.util.i18n.gettext", ['$window', function($window) { - - // If no global function, revert to just returning given text. - var gettextFunc = $window.gettext || function(x) { return x; }; - - // Eventually, could delete the window gettext references here, - // or provide an appropriate method. - - return function() { - return gettextFunc.apply(this, arguments); - }; - - }]); + angular.module('horizon.framework.util.i18n', []) + /* + * @name horizon.framework.util.i18n.gettext + * @description + * Provides a wrapper for translation, using the global 'gettext' + * function if it is present. Provides a method that + * simply returns the input if the expected global 'gettext' is + * not provided. + * + * Ideally once gettext is no longer needed on a global scope, + * the global ref can be deleted here. For now that is not possible. + * Also, if future alternate means were provided, we could put that + * logic here. + * + * This could also be done in the context of the filter, but + * the approach taken here was to separate business logic + * (translation) from filters, which are arguably more + * presentation-oriented. + */ + .factory('horizon.framework.util.i18n.gettext', ['$window', function ($window) { + // If no global function, revert to just returning given text. + var gettextFunc = $window.gettext || function (x) { return x; }; + // Eventually, could delete the window gettext references here, + // or provide an appropriate method. + return function () { + return gettextFunc.apply(this, arguments); + }; + }]); })(); diff --git a/horizon/static/framework/util/i18n/i18n.spec.js b/horizon/static/framework/util/i18n/i18n.spec.js index cc3702d863..fd7a3820a4 100644 --- a/horizon/static/framework/util/i18n/i18n.spec.js +++ b/horizon/static/framework/util/i18n/i18n.spec.js @@ -13,52 +13,46 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -(function() { +(function () { 'use strict'; - describe('horizon.framework.util.i18n', function() { - + describe('horizon.framework.util.i18n', function () { beforeEach(module('horizon.framework.util.i18n')); - describe('gettext', function() { + describe('gettext', function () { var factory; - describe('Normal operation', function() { - - beforeEach(inject(function($injector) { + describe('Normal operation', function () { + beforeEach(inject(function ($injector) { factory = $injector.get("horizon.framework.util.i18n.gettext"); })); - it('defines the factory', function() { + it('defines the factory', function () { expect(factory).toBeDefined(); }); - it('function returns what is given', function() { + it('function returns what is given', function () { expect(factory("Hello")).toBe('Hello'); }); }); - describe("injected window.gettext", function() { - - beforeEach(module(function($provide) { - var $window = {gettext: function(x) { return x.replace(/good/, 'bad'); }}; + describe("injected window.gettext", function () { + beforeEach(module(function ($provide) { + var $window = { gettext: function (x) { return x.replace(/good/, 'bad'); } }; $provide.value('$window', $window); })); // Get the factory by name. - beforeEach(inject(function($injector) { + beforeEach(inject(function ($injector) { factory = $injector.get("horizon.framework.util.i18n.gettext"); })); - it('uses the window gettext when available', function() { + it('uses the window gettext when available', function () { // we can't spy on the window gettext due to (appropriate) // indirection. But we can make sure it was called. expect(factory("good cop")).toBe("bad cop"); }); }); }); - }); - })(); diff --git a/horizon/static/framework/util/util.module.js b/horizon/static/framework/util/util.module.js index 1d99465f41..e6508d02fa 100644 --- a/horizon/static/framework/util/util.module.js +++ b/horizon/static/framework/util/util.module.js @@ -1,5 +1,5 @@ (function () { - 'use strict'; + 'use strict'; angular.module('horizon.framework.util', [ 'horizon.framework.util.bind-scope', @@ -10,5 +10,4 @@ 'horizon.framework.util.validators' ]) .constant('horizon.framework.util.basePath', '/static/framework/util/'); - })(); diff --git a/horizon/static/framework/util/validators/validators.js b/horizon/static/framework/util/validators/validators.js index a8622ad94b..6581eaadf2 100644 --- a/horizon/static/framework/util/validators/validators.js +++ b/horizon/static/framework/util/validators/validators.js @@ -15,6 +15,7 @@ * |---------------------------------------------------------------------------------| * | {@link horizon.framework.util.validators.directive:validateNumberMax `validateNumberMax`} | * | {@link horizon.framework.util.validators.directive:validateNumberMin `validateNumberMin`} | + * | {@link horizon.framework.util.validators.directive:notBlank `notBlank`} | * | {@link horizon.framework.util.validators.directive:hzPasswordMatch `hzPasswordMatch`} | * */ @@ -47,38 +48,40 @@ * validate-number-max="{$ maxNumber $}"> * ``` */ - .directive('validateNumberMax', [function () { - return { - require: 'ngModel', - restrict: 'A', - link: function (scope, element, attrs, ctrl) { - if (!ctrl) { - return; + .directive('validateNumberMax', [function () { + return { + require: 'ngModel', + restrict: 'A', + link: function (scope, element, attrs, ctrl) { + if (!ctrl) { + return; + } + + var maxValidator = function (value) { + var max = scope.$eval(attrs.validateNumberMax); + if (angular.isDefined(max) && !ctrl.$isEmpty(value) && value > max) { + ctrl.$setValidity('validateNumberMax', false); + } else { + ctrl.$setValidity('validateNumberMax', true); } - var maxValidator = function (value) { - var max = scope.$eval(attrs.validateNumberMax); - if (angular.isDefined(max) && !ctrl.$isEmpty(value) && value > max) { - ctrl.$setValidity('validateNumberMax', false); - } else { - ctrl.$setValidity('validateNumberMax', true); - } + // Return the value rather than undefined if invalid + return value; + }; - // Return the value rather than undefined if invalid - return value; - }; + /** + * Re-validate if value is changed through the UI + * or model (programmatically) + */ + ctrl.$parsers.push(maxValidator); + ctrl.$formatters.push(maxValidator); - // Re-validate if value is changed through the UI - // or model (programmatically) - ctrl.$parsers.push(maxValidator); - ctrl.$formatters.push(maxValidator); - - attrs.$observe('validateNumberMax', function () { - maxValidator(ctrl.$modelValue); - }); - } - }; - }]) + attrs.$observe('validateNumberMax', function () { + maxValidator(ctrl.$modelValue); + }); + } + }; + }]) /** * @ngdoc directive @@ -107,107 +110,115 @@ * validate-number-min="{$ minNumber $}"> * ``` */ - .directive('validateNumberMin', [function () { - return { - require: 'ngModel', - restrict: 'A', - link: function (scope, element, attrs, ctrl) { - if (!ctrl) { - return; + .directive('validateNumberMin', [function () { + return { + require: 'ngModel', + restrict: 'A', + link: function (scope, element, attrs, ctrl) { + if (!ctrl) { + return; + } + + var minValidator = function (value) { + var min = scope.$eval(attrs.validateNumberMin); + if (angular.isDefined(min) && !ctrl.$isEmpty(value) && value < min) { + ctrl.$setValidity('validateNumberMin', false); + } else { + ctrl.$setValidity('validateNumberMin', true); } - var minValidator = function (value) { - var min = scope.$eval(attrs.validateNumberMin); - if (angular.isDefined(min) && !ctrl.$isEmpty(value) && value < min) { - ctrl.$setValidity('validateNumberMin', false); - } else { - ctrl.$setValidity('validateNumberMin', true); - } + // Return the value rather than undefined if invalid + return value; + }; - // Return the value rather than undefined if invalid - return value; - }; + /** + * Re-validate if value is changed through the UI + * or model (programmatically) + */ + ctrl.$parsers.push(minValidator); + ctrl.$formatters.push(minValidator); - // Re-validate if value is changed through the UI - // or model (programmatically) - ctrl.$parsers.push(minValidator); - ctrl.$formatters.push(minValidator); + attrs.$observe('validateNumberMin', function () { + minValidator(ctrl.$modelValue); + }); + } + }; + }]) - attrs.$observe('validateNumberMin', function () { - minValidator(ctrl.$modelValue); - }); - } - }; - }]) - - .directive('notBlank', function () { - return { - require: 'ngModel', - link: function (scope, elm, attrs, ctrl) { - ctrl.$parsers.unshift(function (viewValue) { - if (viewValue.length) { - // it is valid - ctrl.$setValidity('notBlank', true); - return viewValue; - } - // it is invalid, return undefined (no model update) - ctrl.$setValidity('notBlank', false); - return undefined; - }); - } - }; - }) - - /** - * @ngdoc directive - * @name hzPasswordMatch - * @element ng-model - * - * @description - * A directive to ensure that password matches. - * Changing the password or confirmation password triggers a validation check. - * However, only the confirmation password will show an error if match is false. - * The goal is to check that confirmation password matches the password, - * not whether the password matches the confirmation password. - * The behavior here is NOT bi-directional. - * - * @restrict A - * - * @scope - * hzPasswordMatch - form model to validate against - * - * @example: - *
- * - * - *
- * - * Note that id and name are required for the password input. - * This directive uses the form model and id for validation check. - */ - .directive('hzPasswordMatch', function(){ - return { - restrict: 'A', - require: 'ngModel', - scope: { pw: '=hzPasswordMatch' }, - link: function(scope, element, attr, ctrl){ - - // helper function to check that password matches - function passwordCheck(){ - scope.$apply(function(){ - var match = (ctrl.$modelValue === scope.pw.$modelValue); - ctrl.$setValidity('match', match); - }); + /** + * @ngdoc directive + * @name notBlank + * @element ng-model + * @description Ensure that the value is not blank + */ + .directive('notBlank', function () { + return { + require: 'ngModel', + link: function (scope, elm, attrs, ctrl) { + ctrl.$parsers.unshift(function (viewValue) { + if (viewValue.length) { + // it is valid + ctrl.$setValidity('notBlank', true); + return viewValue; } + // it is invalid, return undefined (no model update) + ctrl.$setValidity('notBlank', false); + return undefined; + }); + } + }; + }) - // this ensures that typing in either input - // will trigger the password match - var pwElement = $('#'+scope.pw.$name); - pwElement.on('keyup change', passwordCheck); - element.on('keyup change', passwordCheck); + /** + * @ngdoc directive + * @name hzPasswordMatch + * @element ng-model + * + * @description + * A directive to ensure that password matches. + * Changing the password or confirmation password triggers a validation check. + * However, only the confirmation password will show an error if match is false. + * The goal is to check that confirmation password matches the password, + * not whether the password matches the confirmation password. + * The behavior here is NOT bi-directional. + * + * @restrict A + * + * @scope + * hzPasswordMatch - form model to validate against + * + * @example: + *
+ * + * + *
+ * + * Note that id and name are required for the password input. + * This directive uses the form model and id for validation check. + */ + .directive('hzPasswordMatch', function () { + return { + restrict: 'A', + require: 'ngModel', + scope: { pw: '=hzPasswordMatch' }, + link: function (scope, element, attr, ctrl) { - } // end of link - }; // end of return - }); // end of directive + // helper function to check that password matches + function passwordCheck() { + scope.$apply(function () { + var match = (ctrl.$modelValue === scope.pw.$modelValue); + ctrl.$setValidity('match', match); + }); + } -}()); \ No newline at end of file + /** + * this ensures that typing in either input + * will trigger the password match + */ + var pwElement = $('#' + scope.pw.$name); + pwElement.on('keyup change', passwordCheck); + element.on('keyup change', passwordCheck); + } // end of link + }; // end of return + }); // end of directive +})(); diff --git a/horizon/static/framework/util/validators/validators.spec.js b/horizon/static/framework/util/validators/validators.spec.js index 64423fb4b2..f4b01a3b75 100644 --- a/horizon/static/framework/util/validators/validators.spec.js +++ b/horizon/static/framework/util/validators/validators.spec.js @@ -1,22 +1,20 @@ -(function() { +(function () { 'use strict'; - describe('horizon.framework.util.validators module', function() { - it('should have been defined', function() { + describe('horizon.framework.util.validators module', function () { + it('should have been defined', function () { expect(angular.module('horizon.framework.util.validators')).toBeDefined(); }); }); - describe('validators directive', function() { - + describe('validators directive', function () { beforeEach(module('horizon.framework.widgets')); beforeEach(module('horizon.framework.util.validators')); - describe('validateNumberMax directive', function() { - + describe('validateNumberMax directive', function () { var $scope, $form; - beforeEach(inject(function($injector) { + beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); @@ -33,32 +31,30 @@ $scope.$digest(); })); - it('should pass validation initially when count is 0 and max is 1', function() { + it('should pass validation initially when count is 0 and max is 1', function () { expect($form.count.$valid).toBe(true); expect($form.$valid).toBe(true); }); - it('should not pass validation if count increased to 2 and max is 1', function() { + it('should not pass validation if count increased to 2 and max is 1', function () { $form.count.$setViewValue(2); $scope.$digest(); expect($form.count.$valid).toBe(false); expect($form.$valid).toBe(false); }); - it('should pass validation if count is empty', function() { + it('should pass validation if count is empty', function () { $form.count.$setViewValue(''); $scope.$digest(); expect($form.count.$valid).toBe(true); expect($form.$valid).toBe(true); }); - }); - describe('validateNumberMin directive', function() { - + describe('validateNumberMin directive', function () { var $scope, $form; - beforeEach(inject(function($injector) { + beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); @@ -75,12 +71,12 @@ $scope.$digest(); })); - it('should not pass validation initially when count is 0 and min is 1', function() { + it('should not pass validation initially when count is 0 and min is 1', function () { expect($form.count.$valid).toBe(false); expect($form.$valid).toBe(false); }); - it('should pass validation if count increased to 2 and min is 1', function() { + it('should pass validation if count increased to 2 and min is 1', function () { $form.count.$setViewValue(2); $scope.$digest(); expect($scope.count).toBe(2); @@ -88,16 +84,15 @@ expect($form.$valid).toBe(true); }); - it('should pass validation if count is empty', function() { + it('should pass validation if count is empty', function () { $form.count.$setViewValue(''); $scope.$digest(); expect($form.count.$valid).toBe(true); expect($form.$valid).toBe(true); }); - }); - describe('hzPasswordMatch directive', function() { + describe('hzPasswordMatch directive', function () { var $compile, $rootScope; var element, password, cpassword; @@ -108,7 +103,7 @@ 'hz-password-match="form.password">' + ''; - beforeEach(inject(function($injector){ + beforeEach(inject(function ($injector) { $compile = $injector.get('$compile'); $rootScope = $injector.get('$rootScope').$new(); @@ -122,60 +117,57 @@ $rootScope.$digest(); })); - it('should be initially empty', function() { + it('should be initially empty', function () { expect(password.val()).toEqual(''); expect(password.val()).toEqual(cpassword.val()); expect(cpassword.hasClass('ng-valid')).toBe(true); }); - it('should not match if user changes only password', function(done) { + it('should not match if user changes only password', function (done) { $rootScope.user.password = 'password'; $rootScope.$digest(); cpassword.change(); - setTimeout(function(){ + setTimeout(function () { expect(cpassword.val()).not.toEqual(password.val()); expect(cpassword.hasClass('ng-invalid')).toBe(true); done(); }, 1000); }); - it('should not match if user changes only confirmation password', function(done) { + it('should not match if user changes only confirmation password', function (done) { $rootScope.user.cpassword = 'password'; $rootScope.$digest(); cpassword.change(); - setTimeout(function(){ + setTimeout(function () { expect(cpassword.val()).not.toEqual(password.val()); expect(cpassword.hasClass('ng-invalid')).toBe(true); done(); }, 1000); }); - it('should match if both passwords are the same', function(done) { + it('should match if both passwords are the same', function (done) { $rootScope.user.password = 'password'; $rootScope.user.cpassword = 'password'; $rootScope.$digest(); cpassword.change(); - setTimeout(function(){ + setTimeout(function () { expect(cpassword.val()).toEqual(password.val()); expect(cpassword.hasClass('ng-valid')).toBe(true); done(); }, 1000); }); - it('should not match if both passwords are different', function(done) { + it('should not match if both passwords are different', function (done) { $rootScope.user.password = 'password123'; $rootScope.user.cpassword = 'password345'; $rootScope.$digest(); cpassword.change(); - setTimeout(function(){ + setTimeout(function () { expect(cpassword.val()).not.toEqual(password.val()); expect(cpassword.hasClass('ng-invalid')).toBe(true); done(); }, 1000); }); - }); // end of hzPasswordMatch directive - }); - -})(); \ No newline at end of file +})(); diff --git a/horizon/static/framework/util/workflow/workflow.js b/horizon/static/framework/util/workflow/workflow.js index 00125884c1..3ffc2b5d57 100644 --- a/horizon/static/framework/util/workflow/workflow.js +++ b/horizon/static/framework/util/workflow/workflow.js @@ -97,5 +97,4 @@ }; } ]); - })(); diff --git a/horizon/static/framework/util/workflow/workflow.spec.js b/horizon/static/framework/util/workflow/workflow.spec.js index 0fc94818a5..81683d994b 100644 --- a/horizon/static/framework/util/workflow/workflow.spec.js +++ b/horizon/static/framework/util/workflow/workflow.spec.js @@ -8,18 +8,16 @@ }); describe('workflow factory', function () { - - var workflow, - spec, - decorators = [ - function (spec) { - angular.forEach(spec.steps, function (step) { - if (step.requireSomeServices) { - step.checkReadiness = function () {}; - } - }); + var workflow, spec; + var decorators = [ + function (spec) { + angular.forEach(spec.steps, function (step) { + if (step.requireSomeServices) { + step.checkReadiness = function () {}; } - ]; + }); + } + ]; beforeEach(module('horizon.framework.util.workflow')); @@ -55,5 +53,4 @@ expect(angular.isFunction(steps[2].checkReadiness)).toBe(true); }); }); - })(); diff --git a/horizon/static/framework/widgets/widgets.module.js b/horizon/static/framework/widgets/widgets.module.js index 48075c71cc..06494bda48 100644 --- a/horizon/static/framework/widgets/widgets.module.js +++ b/horizon/static/framework/widgets/widgets.module.js @@ -1,5 +1,5 @@ (function () { - 'use strict'; + 'use strict'; angular.module('horizon.framework.widgets', [ 'horizon.framework.widgets.help-panel',