From 37e045101cc53d39e5c1d14169951fe52cbef119 Mon Sep 17 00:00:00 2001 From: Topher Fangio Date: Wed, 12 Aug 2015 13:38:49 -0700 Subject: [PATCH] fix(mdChips): Allow custom $interpolate start/end symbols. Refactor to pull template processing out of $interimElement and into $mdUtil. Fix chips to enable custom start/end symbols. fixes #2958 --- src/components/chips/chips.spec.js | 435 ++++++++++-------- src/components/chips/js/chipDirective.js | 12 +- .../chips/js/chipTranscludeDirective.js | 7 +- src/components/chips/js/chipsDirective.js | 10 + .../services/interimElement/interimElement.js | 9 +- src/core/util/util.js | 34 +- src/core/util/util.spec.js | 226 +++++---- 7 files changed, 424 insertions(+), 309 deletions(-) diff --git a/src/components/chips/chips.spec.js b/src/components/chips/chips.spec.js index 4c905e73a9c..1bd955c840d 100644 --- a/src/components/chips/chips.spec.js +++ b/src/components/chips/chips.spec.js @@ -1,140 +1,145 @@ describe('', function() { var scope; + var BASIC_CHIP_TEMPLATE = - ''; + ''; var CHIP_APPEND_TEMPLATE = - ''; + ''; - beforeEach(module('material.components.chips', 'material.components.autocomplete')); - beforeEach(inject(function ($rootScope) { - scope = $rootScope.$new(); - scope.items = ['Apple', 'Banana', 'Orange']; - })); + describe('with no overrides', function() { - describe('basic functionality', function () { + beforeEach(module('material.components.chips', 'material.components.autocomplete')); + beforeEach(inject(function($rootScope) { + scope = $rootScope.$new(); + scope.items = ['Apple', 'Banana', 'Orange']; + })); - it('should render a default input element', function() { - var element = buildChips(BASIC_CHIP_TEMPLATE); - var ctrl = element.controller('mdChips'); + describe('basic functionality', function() { - var input = element.find('input'); - expect(input.length).toBe(1); - }); + it('should render a default input element', function() { + var element = buildChips(BASIC_CHIP_TEMPLATE); + var ctrl = element.controller('mdChips'); - it('should render a list of chips', function() { - var element = buildChips(BASIC_CHIP_TEMPLATE); + var input = element.find('input'); + expect(input.length).toBe(1); + }); - var chips = getChipElements(element); - expect(chips.length).toBe(3); - expect(chips[0].innerHTML).toContain('Apple'); - expect(chips[1].innerHTML).toContain('Banana'); - expect(chips[2].innerHTML).toContain('Orange'); - }); + it('should render a list of chips', function() { + var element = buildChips(BASIC_CHIP_TEMPLATE); - it('should render a user-provided chip template', function() { - var template = + var chips = getChipElements(element); + expect(chips.length).toBe(3); + expect(chips[0].innerHTML).toContain('Apple'); + expect(chips[1].innerHTML).toContain('Banana'); + expect(chips[2].innerHTML).toContain('Orange'); + }); + + it('should render a user-provided chip template', function() { + var template = '' + '
{$chip}
' + '
'; - var element = buildChips(template); - var chip = element.find('md-chip'); - expect(chip[0].querySelector('div.mychiptemplate')).not.toBeNull(); - }); + var element = buildChips(template); + var chip = element.find('md-chip'); + expect(chip[0].querySelector('div.mychiptemplate')).not.toBeNull(); + }); - it('should add a chip', function() { - var element = buildChips(BASIC_CHIP_TEMPLATE); - var ctrl = element.controller('mdChips'); + it('should add a chip', function() { + var element = buildChips(BASIC_CHIP_TEMPLATE); + var ctrl = element.controller('mdChips'); - element.scope().$apply(function() { - ctrl.chipBuffer = 'Grape'; - simulateInputEnterKey(ctrl); - }); + element.scope().$apply(function() { + ctrl.chipBuffer = 'Grape'; + simulateInputEnterKey(ctrl); + }); - expect(scope.items.length).toBe(4); + expect(scope.items.length).toBe(4); - var chips = getChipElements(element); - expect(chips.length).toBe(4); - expect(chips[0].innerHTML).toContain('Apple'); - expect(chips[1].innerHTML).toContain('Banana'); - expect(chips[2].innerHTML).toContain('Orange'); + var chips = getChipElements(element); + expect(chips.length).toBe(4); + expect(chips[0].innerHTML).toContain('Apple'); + expect(chips[1].innerHTML).toContain('Banana'); + expect(chips[2].innerHTML).toContain('Orange'); - expect(chips[3].innerHTML).toContain('Grape'); - }); + expect(chips[3].innerHTML).toContain('Grape'); + }); - it('should not add a blank chip', function() { - var element = buildChips(BASIC_CHIP_TEMPLATE); - var ctrl = element.controller('mdChips'); + it('should not add a blank chip', function() { + var element = buildChips(BASIC_CHIP_TEMPLATE); + var ctrl = element.controller('mdChips'); - element.scope().$apply(function() { - ctrl.chipBuffer = ''; - simulateInputEnterKey(ctrl); + element.scope().$apply(function() { + ctrl.chipBuffer = ''; + simulateInputEnterKey(ctrl); + }); + + expect(scope.items.length).toBe(3); }); - expect(scope.items.length).toBe(3); - }); + it('should remove a chip', function() { + var element = buildChips(BASIC_CHIP_TEMPLATE); + var ctrl = element.controller('mdChips'); - it('should remove a chip', function() { - var element = buildChips(BASIC_CHIP_TEMPLATE); - var ctrl = element.controller('mdChips'); + element.scope().$apply(function() { + // Remove "Banana" + ctrl.removeChip(1); + }); - element.scope().$apply(function() { - // Remove "Banana" - ctrl.removeChip(1); + var chips = getChipElements(element); + expect(chips.length).toBe(2); + expect(chips[0].innerHTML).toContain('Apple'); + expect(chips[1].innerHTML).toContain('Orange'); }); - var chips = getChipElements(element); - expect(chips.length).toBe(2); - expect(chips[0].innerHTML).toContain('Apple'); - expect(chips[1].innerHTML).toContain('Orange'); - }); + it('should call the append method when adding a chip', function() { + var element = buildChips(CHIP_APPEND_TEMPLATE); + var ctrl = element.controller('mdChips'); - it('should call the append method when adding a chip', function() { - var element = buildChips(CHIP_APPEND_TEMPLATE); - var ctrl = element.controller('mdChips'); + var doubleText = function(text) { + return "" + text + text; + }; + scope.appendChip = jasmine.createSpy('appendChip').and.callFake(doubleText); - var doubleText = function (text) { return "" + text + text; }; - scope.appendChip = jasmine.createSpy('appendChip').and.callFake(doubleText); + element.scope().$apply(function() { + ctrl.chipBuffer = 'Grape'; + simulateInputEnterKey(ctrl); + }); - element.scope().$apply(function() { - ctrl.chipBuffer = 'Grape'; - simulateInputEnterKey(ctrl); + expect(scope.appendChip).toHaveBeenCalled(); + expect(scope.appendChip.calls.mostRecent().args[0]).toBe('Grape'); + expect(scope.items.length).toBe(4); + expect(scope.items[3]).toBe('GrapeGrape'); }); - expect(scope.appendChip).toHaveBeenCalled(); - expect(scope.appendChip.calls.mostRecent().args[0]).toBe('Grape'); - expect(scope.items.length).toBe(4); - expect(scope.items[3]).toBe('GrapeGrape'); - }); - - it('should handle appending an object chip', function() { - var element = buildChips(CHIP_APPEND_TEMPLATE); - var ctrl = element.controller('mdChips'); + it('should handle appending an object chip', function() { + var element = buildChips(CHIP_APPEND_TEMPLATE); + var ctrl = element.controller('mdChips'); - var chipObj = function(chip) { - return { - name : chip, - uppername: chip.toUpperCase() + var chipObj = function(chip) { + return { + name: chip, + uppername: chip.toUpperCase() + }; }; - }; - scope.appendChip = jasmine.createSpy('appendChip').and.callFake(chipObj); + scope.appendChip = jasmine.createSpy('appendChip').and.callFake(chipObj); - element.scope().$apply(function() { - ctrl.chipBuffer = 'Grape'; - simulateInputEnterKey(ctrl); - }); + element.scope().$apply(function() { + ctrl.chipBuffer = 'Grape'; + simulateInputEnterKey(ctrl); + }); - expect(scope.appendChip).toHaveBeenCalled(); - expect(scope.appendChip.calls.mostRecent().args[0]).toBe('Grape'); - expect(scope.items.length).toBe(4); - expect(scope.items[3].name).toBe('Grape'); - expect(scope.items[3].uppername).toBe('GRAPE'); + expect(scope.appendChip).toHaveBeenCalled(); + expect(scope.appendChip.calls.mostRecent().args[0]).toBe('Grape'); + expect(scope.items.length).toBe(4); + expect(scope.items[3].name).toBe('Grape'); + expect(scope.items[3].uppername).toBe('GRAPE'); + }); }); - }); - describe('custom inputs', function() { - describe('md-autocomplete', function() { - var AUTOCOMPLETE_CHIPS_TEMPLATE = '\ + describe('custom inputs', function() { + describe('md-autocomplete', function() { + var AUTOCOMPLETE_CHIPS_TEMPLATE = '\ \ ', function() { \ '; - it('should use the selected item as a buffer', inject(function($timeout) { - setupScopeForAutocomplete(); - var element = buildChips(AUTOCOMPLETE_CHIPS_TEMPLATE); - var ctrl = element.controller('mdChips'); - $timeout.flush(); // mdAutcomplete needs a flush for its init. - var autocompleteCtrl = element.find('md-autocomplete').controller('mdAutocomplete'); + it('should use the selected item as a buffer', inject(function($timeout) { + setupScopeForAutocomplete(); + var element = buildChips(AUTOCOMPLETE_CHIPS_TEMPLATE); + var ctrl = element.controller('mdChips'); + $timeout.flush(); // mdAutcomplete needs a flush for its init. + var autocompleteCtrl = element.find('md-autocomplete').controller('mdAutocomplete'); - element.scope().$apply(function() { - autocompleteCtrl.scope.searchText = 'K'; - }); + element.scope().$apply(function() { + autocompleteCtrl.scope.searchText = 'K'; + }); - element.scope().$apply(function() { - autocompleteCtrl.select(0); - }); - $timeout.flush(); - - expect(scope.items.length).toBe(4); - expect(scope.items[3]).toBe('Kiwi'); - expect(element.find('input').val()).toBe(''); - })); - }); + element.scope().$apply(function() { + autocompleteCtrl.select(0); + }); + $timeout.flush(); - describe('user input templates', function() { - var NG_MODEL_TEMPLATE = '\ + expect(scope.items.length).toBe(4); + expect(scope.items[3]).toBe('Kiwi'); + expect(element.find('input').val()).toBe(''); + })); + }); + + describe('user input templates', function() { + var NG_MODEL_TEMPLATE = '\ \ \ '; - var INPUT_TEMPLATE = '\ + var INPUT_TEMPLATE = '\ \ \ '; - describe('using ngModel', function() { - it('should add the ngModelCtrl.$viewValue when is pressed', + describe('using ngModel', function() { + it('should add the ngModelCtrl.$viewValue when is pressed', inject(function($timeout) { - var element = buildChips(NG_MODEL_TEMPLATE); - var ctrl = element.controller('mdChips'); - $timeout.flush(); + var element = buildChips(NG_MODEL_TEMPLATE); + var ctrl = element.controller('mdChips'); + $timeout.flush(); - var ngModelCtrl = ctrl.userInputNgModelCtrl; + var ngModelCtrl = ctrl.userInputNgModelCtrl; - element.scope().$apply(function() { - ngModelCtrl.$viewValue = 'Grape'; - simulateInputEnterKey(ctrl); - }); + element.scope().$apply(function() { + ngModelCtrl.$viewValue = 'Grape'; + simulateInputEnterKey(ctrl); + }); - expect(scope.items.length).toBe(4); - expect(scope.items[3]).toBe('Grape'); - })); - }); + expect(scope.items.length).toBe(4); + expect(scope.items[3]).toBe('Grape'); + })); + }); - describe('without ngModel', function() { - it('should support an input without an ngModel', inject(function ($timeout) { - var element = buildChips(INPUT_TEMPLATE); - var ctrl = element.controller('mdChips'); - $timeout.flush(); + describe('without ngModel', function() { + it('should support an input without an ngModel', inject(function($timeout) { + var element = buildChips(INPUT_TEMPLATE); + var ctrl = element.controller('mdChips'); + $timeout.flush(); - element.scope().$apply(function() { - ctrl.userInputElement[0].value = 'Kiwi'; - simulateInputEnterKey(ctrl); - }); + element.scope().$apply(function() { + ctrl.userInputElement[0].value = 'Kiwi'; + simulateInputEnterKey(ctrl); + }); - expect(scope.items.length).toBe(4); - expect(scope.items[3]).toBe('Kiwi'); - })); + expect(scope.items.length).toBe(4); + expect(scope.items[3]).toBe('Kiwi'); + })); + }); }); }); - }); - describe('static chips', function() { - var STATIC_CHIPS_TEMPLATE = '\ + describe('static chips', function() { + var STATIC_CHIPS_TEMPLATE = '\ \ Hockey\ Lacrosse\ Baseball\ {{chipItem}}\ '; - it('should transclude static chips', inject(function($timeout) { - scope.chipItem = 'Football'; - var element = buildChips(STATIC_CHIPS_TEMPLATE); - var ctrl = element.controller('mdChips'); - $timeout.flush(); - - var chips = getChipElements(element); - expect(chips.length).toBe(4); - expect(chips[0].innerHTML).toContain('Hockey'); - expect(chips[1].innerHTML).toContain('Lacrosse'); - expect(chips[2].innerHTML).toContain('Baseball'); - expect(chips[3].innerHTML).toContain('Football'); - })); + it('should transclude static chips', inject(function($timeout) { + scope.chipItem = 'Football'; + var element = buildChips(STATIC_CHIPS_TEMPLATE); + var ctrl = element.controller('mdChips'); + $timeout.flush(); + + var chips = getChipElements(element); + expect(chips.length).toBe(4); + expect(chips[0].innerHTML).toContain('Hockey'); + expect(chips[1].innerHTML).toContain('Lacrosse'); + expect(chips[2].innerHTML).toContain('Baseball'); + expect(chips[3].innerHTML).toContain('Football'); + })); + }); + + describe('', function() { + it('should remove a chip', function() { + var element = buildChips(BASIC_CHIP_TEMPLATE); + var ctrl = element.controller('mdChips'); + var chips = getChipElements(element); + + expect(chips.length).toBe(3); + + // Remove 'Banana' + var db = angular.element(chips[1]).find('button'); + db[0].click(); + + scope.$digest(); + chips = getChipElements(element); + expect(chips.length).toBe(2); + + // Remove 'Orange' + db = angular.element(chips[1]).find('button'); + db[0].click(); + + scope.$digest(); + chips = getChipElements(element); + expect(chips.length).toBe(1); + + }); + }); }); - describe('', function() { - it('should remove a chip', function() { - var element = buildChips(BASIC_CHIP_TEMPLATE); - var ctrl = element.controller('mdChips'); - var chips = getChipElements(element); + describe('with $interpolate.start/endSymbol override', function() { + beforeEach(module(function($interpolateProvider) { + $interpolateProvider.startSymbol('[[').endSymbol(']]'); + })); - expect(chips.length).toBe(3); + beforeEach(module('material.components.chips', 'material.components.autocomplete')); - // Remove 'Banana' - var db = angular.element(chips[1]).find('button'); - db[0].click(); + beforeEach(inject(function($rootScope) { + scope = $rootScope.$new(); + scope.items = ['Apple', 'Banana', 'Orange']; + })); - scope.$digest(); - chips = getChipElements(element); - expect(chips.length).toBe(2); + it('should render a user-provided chip template with custom start/end symbols', function() { + var template = + '' + + '
[[$chip]]
' + + '
'; + var element = buildChips(template); + var chips = element.find('md-chip'); - // Remove 'Orange' - db = angular.element(chips[1]).find('button'); - db[0].click(); + expect(chips.eq(0).text().trim()).toEqual('Apple'); + expect(chips.eq(1).text().trim()).toEqual('Banana'); + expect(chips.eq(2).text().trim()).toEqual('Orange'); + }); - scope.$digest(); - chips = getChipElements(element); - expect(chips.length).toBe(1); + it('should not interpolate old-style tags in a user-provided chip template', function() { + var template = + '' + + '
{{$chip}}
' + + '
'; + var element = buildChips(template); + var chips = element.find('md-chip'); + expect(chips.eq(0).text().trim()).toEqual('{{$chip}}'); + expect(chips.eq(1).text().trim()).toEqual('{{$chip}}'); + expect(chips.eq(2).text().trim()).toEqual('{{$chip}}'); }); }); @@ -268,14 +313,14 @@ describe('', function() { // Internal helper methods // ******************************* - function buildChips (str) { - var container; - inject(function ($compile) { - container = $compile(str)(scope); - container.scope().$apply(); - }); - return container; - } + function buildChips(str) { + var container; + inject(function($compile) { + container = $compile(str)(scope); + container.scope().$apply(); + }); + return container; + } function setupScopeForAutocomplete() { scope.selectedItem = ''; @@ -289,15 +334,15 @@ describe('', function() { } function simulateInputEnterKey(ctrl) { - var event = {}; - event.preventDefault = jasmine.createSpy('preventDefault'); - inject(function($mdConstant) { - event.keyCode = $mdConstant.KEY_CODE.ENTER; - }); - ctrl.inputKeydown(event); + var event = {}; + event.preventDefault = jasmine.createSpy('preventDefault'); + inject(function($mdConstant) { + event.keyCode = $mdConstant.KEY_CODE.ENTER; + }); + ctrl.inputKeydown(event); } function getChipElements(root) { - return angular.element(root[0].querySelectorAll('md-chip')); + return angular.element(root[0].querySelectorAll('md-chip')); } }); diff --git a/src/components/chips/js/chipDirective.js b/src/components/chips/js/chipDirective.js index 4f28244f28f..fcc3ea0a0b3 100644 --- a/src/components/chips/js/chipDirective.js +++ b/src/components/chips/js/chipDirective.js @@ -33,7 +33,9 @@ var DELETE_HINT_TEMPLATE = '\ * @param $mdInkRipple * @ngInject */ -function MdChip($mdTheming) { +function MdChip($mdTheming, $mdUtil) { + convertTemplates(); + return { restrict: 'E', require: '^?mdChips', @@ -41,7 +43,9 @@ function MdChip($mdTheming) { }; function compile(element, attr) { - element.append(DELETE_HINT_TEMPLATE); + // Append the delete template + element.append($mdUtil.processTemplate(DELETE_HINT_TEMPLATE)); + return function postLink(scope, element, attr, ctrl) { element.addClass('md-chip'); $mdTheming(element); @@ -52,4 +56,8 @@ function MdChip($mdTheming) { }); }; } + + function convertTemplates() { + DELETE_HINT_TEMPLATE = $mdUtil.processTemplate(DELETE_HINT_TEMPLATE); + } } diff --git a/src/components/chips/js/chipTranscludeDirective.js b/src/components/chips/js/chipTranscludeDirective.js index f2cce54b0b8..be19b74ed26 100644 --- a/src/components/chips/js/chipTranscludeDirective.js +++ b/src/components/chips/js/chipTranscludeDirective.js @@ -2,7 +2,7 @@ angular .module('material.components.chips') .directive('mdChipTransclude', MdChipTransclude); -function MdChipTransclude ($compile, $mdUtil) { +function MdChipTransclude ($compile) { return { restrict: 'EA', terminal: true, @@ -15,7 +15,10 @@ function MdChipTransclude ($compile, $mdUtil) { newScope.$$replacedScope = scope; newScope.$chip = scope.$chip; newScope.$mdChipsCtrl = ctrl; - element.html(ctrl.$scope.$eval(attr.mdChipTransclude)); + + var newHtml = ctrl.$scope.$eval(attr.mdChipTransclude); + + element.html(newHtml); $compile(element.contents())(newScope); } } diff --git a/src/components/chips/js/chipsDirective.js b/src/components/chips/js/chipsDirective.js index b6fd6f31550..d1acf95082d 100644 --- a/src/components/chips/js/chipsDirective.js +++ b/src/components/chips/js/chipsDirective.js @@ -142,6 +142,9 @@ * MDChips Directive Definition */ function MdChips ($mdTheming, $mdUtil, $compile, $log, $timeout) { + // Run our templates through $mdUtil.processTemplate() to allow custom start/end symbosl + convertTemplates(); + return { template: function(element, attrs) { // Clone the element into an attribute. By prepending the attribute @@ -266,4 +269,11 @@ } }; } + + function convertTemplates() { + MD_CHIPS_TEMPLATE = $mdUtil.processTemplate(MD_CHIPS_TEMPLATE); + CHIP_INPUT_TEMPLATE = $mdUtil.processTemplate(CHIP_INPUT_TEMPLATE); + CHIP_DEFAULT_TEMPLATE = $mdUtil.processTemplate(CHIP_DEFAULT_TEMPLATE); + CHIP_REMOVE_TEMPLATE = $mdUtil.processTemplate(CHIP_REMOVE_TEMPLATE); + } } diff --git a/src/core/services/interimElement/interimElement.js b/src/core/services/interimElement/interimElement.js index 119c6cabed9..d77d7c6677b 100644 --- a/src/core/services/interimElement/interimElement.js +++ b/src/core/services/interimElement/interimElement.js @@ -220,12 +220,7 @@ function InterimElementProvider() { /* @ngInject */ function InterimElementFactory($document, $q, $rootScope, $timeout, $rootElement, $animate, - $interpolate, $mdCompiler, $mdTheming, $log ) { - var startSymbol = $interpolate.startSymbol(), - endSymbol = $interpolate.endSymbol(), - usesStandardSymbols = ((startSymbol === '{{') && (endSymbol === '}}')), - processTemplate = usesStandardSymbols ? angular.identity : replaceInterpolationSymbols; - + $mdUtil, $mdCompiler, $mdTheming, $log ) { return function createInterimElementService() { var SHOW_CANCELLED = false; var SHOW_CLOSED = true; @@ -427,7 +422,7 @@ function InterimElementProvider() { function configureScopeAndTransitions(options) { options = options || { }; if ( options.template ) { - options.template = processTemplate(options.template); + options.template = $mdUtil.processTemplate(options.template); } return angular.extend({ diff --git a/src/core/util/util.js b/src/core/util/util.js index c05c9c931c5..d7ccc6392cb 100644 --- a/src/core/util/util.js +++ b/src/core/util/util.js @@ -1,16 +1,21 @@ /* - * This var has to be outside the angular factory, otherwise when - * there are multiple material apps on the same page, each app - * will create its own instance of this array and the app's IDs - * will not be unique. - */ + * This var has to be outside the angular factory, otherwise when + * there are multiple material apps on the same page, each app + * will create its own instance of this array and the app's IDs + * will not be unique. + */ var nextUniqueId = 0; angular .module('material.core') .factory('$mdUtil', UtilFactory); -function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate) { +function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $interpolate) { + // Setup some core variables for the processTemplate method + var startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(), + usesStandardSymbols = ((startSymbol === '{{') && (endSymbol === '}}')); + var $mdUtil = { dom: {}, now: window.performance ? @@ -505,8 +510,23 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate) { if (digest) $rootScope.$digest(); } - } + }, + /** + * Processes a template and replaces the start/end symbols if the application has + * overriden them. + * + * @param template The template to process whose start/end tags may be replaced. + * @returns {*} + */ + processTemplate: function(template) { + if (usesStandardSymbols) { + return template; + } else { + if (!template || !angular.isString(template)) return template; + return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); + } + } }; // Instantiate other namespace utility methods diff --git a/src/core/util/util.spec.js b/src/core/util/util.spec.js index c8906f2facb..c86ffb9c7f4 100644 --- a/src/core/util/util.spec.js +++ b/src/core/util/util.spec.js @@ -1,131 +1,165 @@ describe('util', function() { - beforeEach(module('material.core')); - var $rootScope, $timeout, $$mdAnimate; - beforeEach( inject(function(_$animate_,_$rootScope_,_$timeout_) { + describe('with no overrides', function() { + + beforeEach(module('material.core')); + + var $rootScope, $timeout, $$mdAnimate; + beforeEach(inject(function(_$animate_, _$rootScope_, _$timeout_) { $animate = _$animate_; $rootScope = _$rootScope_; $timeout = _$timeout_; - })); + })); - describe('now',function(){ + describe('now', function() { it('returns proper time values', inject(function($mdUtil, $timeout) { var t1 = $mdUtil.now(); - expect( t1 ).toBeGreaterThan(0); + expect(t1).toBeGreaterThan(0); })); }); - describe('disconnect',function(){ - var disconnectScope, reconnectScope; - beforeEach(inject(function($mdUtil) { - disconnectScope = $mdUtil.disconnectScope; - reconnectScope = $mdUtil.reconnectScope; - })); - - it('disconnectScope events', inject(function($rootScope) { - var scope1 = $rootScope.$new(); + describe('disconnect', function() { + var disconnectScope, reconnectScope; + beforeEach(inject(function($mdUtil) { + disconnectScope = $mdUtil.disconnectScope; + reconnectScope = $mdUtil.reconnectScope; + })); - var spy = jasmine.createSpy('eventSpy'); - scope1.$on('event', spy); + it('disconnectScope events', inject(function($rootScope) { + var scope1 = $rootScope.$new(); - disconnectScope(scope1); + var spy = jasmine.createSpy('eventSpy'); + scope1.$on('event', spy); - $rootScope.$broadcast('event'); - expect(spy).not.toHaveBeenCalled(); + disconnectScope(scope1); - reconnectScope(scope1); + $rootScope.$broadcast('event'); + expect(spy).not.toHaveBeenCalled(); - $rootScope.$broadcast('event'); - expect(spy).toHaveBeenCalled(); - })); + reconnectScope(scope1); - }); - - describe('throttle', function() { - var delay = 500; - var nowMockValue; - var originalFn; - var throttledFn; - - beforeEach(inject(function($mdUtil) { - $mdUtil.now = function () { return nowMockValue; }; - originalFn = jasmine.createSpy('originalFn'); - throttledFn = $mdUtil.throttle(originalFn, delay); - nowMockValue = 1; // Not 0, to prevent `!recent` inside `throttle()` to - // evaluate to true even after `recent` has been set - })); + $rootScope.$broadcast('event'); + expect(spy).toHaveBeenCalled(); + })); - it('should immediately invoke the function on first call', function() { - expect(originalFn).not.toHaveBeenCalled(); - throttledFn(); - expect(originalFn).toHaveBeenCalled(); }); - it('should not invoke the function again before (delay + 1) milliseconds', function() { - throttledFn(); - expect(originalFn.calls.count()).toBe(1); - - throttledFn(); - expect(originalFn.calls.count()).toBe(1); + describe('throttle', function() { + var delay = 500; + var nowMockValue; + var originalFn; + var throttledFn; + + beforeEach(inject(function($mdUtil) { + $mdUtil.now = function() { + return nowMockValue; + }; + originalFn = jasmine.createSpy('originalFn'); + throttledFn = $mdUtil.throttle(originalFn, delay); + nowMockValue = 1; // Not 0, to prevent `!recent` inside `throttle()` to + // evaluate to true even after `recent` has been set + })); - nowMockValue += delay; - throttledFn(); - expect(originalFn.calls.count()).toBe(1); + it('should immediately invoke the function on first call', function() { + expect(originalFn).not.toHaveBeenCalled(); + throttledFn(); + expect(originalFn).toHaveBeenCalled(); + }); + + it('should not invoke the function again before (delay + 1) milliseconds', function() { + throttledFn(); + expect(originalFn.calls.count()).toBe(1); + + throttledFn(); + expect(originalFn.calls.count()).toBe(1); + + nowMockValue += delay; + throttledFn(); + expect(originalFn.calls.count()).toBe(1); + + nowMockValue += 1; + throttledFn(); + expect(originalFn.calls.count()).toBe(2); + }); + + it('should pass the context to the original function', inject(function($mdUtil) { + var obj = { + called: false, + fn: function() { + this.called = true; + } + }; + var throttled = $mdUtil.throttle(obj.fn, delay); + + expect(obj.called).toBeFalsy(); + throttled.call(obj); + expect(obj.called).toBeTruthy(); + })); - nowMockValue += 1; - throttledFn(); - expect(originalFn.calls.count()).toBe(2); + it('should pass the arguments to the original function', function() { + throttledFn(1, 2, 3, 'test'); + expect(originalFn).toHaveBeenCalledWith(1, 2, 3, 'test'); + }); }); - it('should pass the context to the original function', inject(function($mdUtil) { - var obj = { - called: false, - fn: function() { this.called = true; } - }; - var throttled = $mdUtil.throttle(obj.fn, delay); + describe('nextTick', function() { + it('should combine multiple calls into a single digest', inject(function($mdUtil, $rootScope, $timeout) { + var digestWatchFn = jasmine.createSpy('watchFn'); + var callback = jasmine.createSpy('callback'); + var timeout; + $rootScope.$watch(digestWatchFn); + expect(digestWatchFn).not.toHaveBeenCalled(); + expect(callback).not.toHaveBeenCalled(); + //-- Add a bunch of calls to prove that they are batched + for (var i = 0; i < 10; i++) { + timeout = $mdUtil.nextTick(callback); + expect(timeout.$$timeoutId).toBeOfType('number'); + } + $timeout.flush(); + expect(digestWatchFn).toHaveBeenCalled(); + //-- $digest seems to be called one extra time here + expect(digestWatchFn.calls.count()).toBe(2); + //-- but callback is still called more + expect(callback.calls.count()).toBe(10); + })); + it('should return a timeout', inject(function($mdUtil) { + var timeout = $mdUtil.nextTick(angular.noop); + expect(timeout.$$timeoutId).toBeOfType('number'); + })); + it('should return the same timeout for multiple calls', inject(function($mdUtil) { + var a = $mdUtil.nextTick(angular.noop), + b = $mdUtil.nextTick(angular.noop); + expect(a).toBe(b); + })); + }); - expect(obj.called).toBeFalsy(); - throttled.call(obj); - expect(obj.called).toBeTruthy(); - })); + describe('processTemplate', function() { + it('should return exact template when using the default start/end symbols', + inject(function($mdUtil) { + var output = $mdUtil.processTemplate('{{some-var}}'); - it('should pass the arguments to the original function', function() { - throttledFn(1, 2, 3, 'test'); - expect(originalFn).toHaveBeenCalledWith(1, 2, 3, 'test'); + expect(output).toEqual('{{some-var}}'); + }) + ); }); }); - describe('nextTick', function () { - it('should combine multiple calls into a single digest', inject(function ($mdUtil, $rootScope, $timeout) { - var digestWatchFn = jasmine.createSpy('watchFn'); - var callback = jasmine.createSpy('callback'); - var timeout; - $rootScope.$watch(digestWatchFn); - expect(digestWatchFn).not.toHaveBeenCalled(); - expect(callback).not.toHaveBeenCalled(); - //-- Add a bunch of calls to prove that they are batched - for (var i = 0; i < 10; i++) { - timeout = $mdUtil.nextTick(callback); - expect(timeout.$$timeoutId).toBeOfType('number'); - } - $timeout.flush(); - expect(digestWatchFn).toHaveBeenCalled(); - //-- $digest seems to be called one extra time here - expect(digestWatchFn.calls.count()).toBe(2); - //-- but callback is still called more - expect(callback.calls.count()).toBe(10); - })); - it('should return a timeout', inject(function ($mdUtil) { - var timeout = $mdUtil.nextTick(angular.noop); - expect(timeout.$$timeoutId).toBeOfType('number'); - })); - it('should return the same timeout for multiple calls', inject(function ($mdUtil) { - var a = $mdUtil.nextTick(angular.noop), - b = $mdUtil.nextTick(angular.noop); - expect(a).toBe(b); + describe('with $interpolate.start/endSymbol override', function() { + beforeEach(module(function($interpolateProvider) { + $interpolateProvider.startSymbol('[[').endSymbol(']]'); + + module('material.core'); })); + + describe('processTemplate', function() { + it('should replace the start/end symbols', inject(function($mdUtil) { + var output = $mdUtil.processTemplate('{{some-var}}'); + + expect(output).toEqual('[[some-var]]'); + })); + }); }); function flush() {