')
+ ($rootScope);
+ var div = element.find('div');
+ expect(div.hasClass('medium-log')).toBe(true);
+ expect(div.hasClass('log')).toBe(true);
+ expect(div.css('width')).toBe('10px');
+ expect(div.css('height')).toBe('20px');
+ expect(div.attr('replace')).toEqual('');
+ expect(div.attr('high-log')).toEqual('');
+ }));
+
+ it('should prevent multiple templates per element', inject(function($compile) {
+ try {
+ $compile('
')
+ fail();
+ } catch(e) {
+ expect(e.message).toMatch(/Multiple directives .* asking for template/);
+ }
+ }));
+
+ it('should play nice with repeater when inline', inject(function($compile, $rootScope) {
+ element = $compile(
+ '
')($rootScope);
+ $rootScope.$digest();
+ expect(element.text()).toEqual('Hello: 1; Hello: 2; ');
+ }));
+
+
+ it('should play nice with repeater when append', inject(function($compile, $rootScope) {
+ element = $compile(
+ '
')($rootScope);
+ $rootScope.$digest();
+ expect(element.text()).toEqual('Hello: 1; Hello: 2; ');
+ }));
+
+
+ it('should merge interpolated css class', inject(function($compile, $rootScope) {
+ element = $compile('
')($rootScope);
+
+ $rootScope.$apply(function() {
+ $rootScope.cls = 'two';
+ });
+
+ expect(element).toHaveClass('one');
+ expect(element).toHaveClass('two'); // interpolated
+ expect(element).toHaveClass('three');
+ expect(element).toHaveClass('log'); // merged from replace directive template
+ }));
+
+
+ it('should merge interpolated css class with ngRepeat',
+ inject(function($compile, $rootScope) {
+ element = $compile(
+ '
')($rootScope);
+
+ $rootScope.$apply(function() {
+ $rootScope.cls = 'two';
+ });
+
+ var child = element.find('div').eq(0);
+ expect(child).toHaveClass('one');
+ expect(child).toHaveClass('two'); // interpolated
+ expect(child).toHaveClass('three');
+ expect(child).toHaveClass('log'); // merged from replace directive template
+ }));
+ });
+
+
+ describe('async templates', function() {
+
+ beforeEach(module(
+ function($compileProvider) {
+ $compileProvider.directive('hello', valueFn({ restrict: 'CAM', templateUrl: 'hello.html' }));
+ $compileProvider.directive('cau', valueFn({ restrict: 'CAM', templateUrl:'cau.html' }));
+
+ $compileProvider.directive('cError', valueFn({
+ restrict: 'CAM',
+ templateUrl:'error.html',
+ compile: function() {
+ throw Error('cError');
+ }
+ }));
+ $compileProvider.directive('lError', valueFn({
+ restrict: 'CAM',
+ templateUrl: 'error.html',
+ compile: function() {
+ throw Error('lError');
+ }
+ }));
+
+
+ $compileProvider.directive('iHello', valueFn({
+ restrict: 'CAM',
+ replace: true,
+ templateUrl: 'hello.html'
+ }));
+ $compileProvider.directive('iCau', valueFn({
+ restrict: 'CAM',
+ replace: true,
+ templateUrl:'cau.html'
+ }));
+
+ $compileProvider.directive('iCError', valueFn({
+ restrict: 'CAM',
+ replace: true,
+ templateUrl:'error.html',
+ compile: function() {
+ throw Error('cError');
+ }
+ }));
+ $compileProvider.directive('iLError', valueFn({
+ restrict: 'CAM',
+ replace: true,
+ templateUrl: 'error.html',
+ compile: function() {
+ throw Error('lError');
+ }
+ }));
+
+ }
+ ));
+
+
+ it('should append template via $http and cache it in $templateCache', inject(
+ function($compile, $httpBackend, $templateCache, $rootScope, $browser) {
+ $httpBackend.expect('GET', 'hello.html').respond('
Hello! World!');
+ $templateCache.put('cau.html', '
Cau! ');
+ element = $compile('
ignore ignore
')($rootScope);
+ expect(sortedHtml(element)).
+ toEqual('
');
+
+ $rootScope.$digest();
+
+
+ expect(sortedHtml(element)).
+ toEqual('
Cau!
');
+
+ $httpBackend.flush();
+ expect(sortedHtml(element)).toEqual(
+ '
' +
+ 'Hello! World! ' +
+ 'Cau! ' +
+ '
');
+ }
+ ));
+
+
+ it('should inline template via $http and cache it in $templateCache', inject(
+ function($compile, $httpBackend, $templateCache, $rootScope) {
+ $httpBackend.expect('GET', 'hello.html').respond('
Hello! ');
+ $templateCache.put('cau.html', '
Cau! ');
+ element = $compile('
ignore ignore
')($rootScope);
+ expect(sortedHtml(element)).
+ toEqual('
');
+
+ $rootScope.$digest();
+
+
+ expect(sortedHtml(element)).
+ toEqual('
Cau!
');
+
+ $httpBackend.flush();
+ expect(sortedHtml(element)).
+ toEqual('
Hello! Cau!
');
+ }
+ ));
+
+
+ it('should compile, link and flush the template append', inject(
+ function($compile, $templateCache, $rootScope, $browser) {
+ $templateCache.put('hello.html', '
Hello, {{name}}! ');
+ $rootScope.name = 'Elvis';
+ element = $compile('
')($rootScope);
+
+ $rootScope.$digest();
+
+ expect(sortedHtml(element)).
+ toEqual('
Hello, Elvis!
');
+ }
+ ));
+
+
+ it('should compile, link and flush the template inline', inject(
+ function($compile, $templateCache, $rootScope) {
+ $templateCache.put('hello.html', '
Hello, {{name}}! ');
+ $rootScope.name = 'Elvis';
+ element = $compile('
')($rootScope);
+
+ $rootScope.$digest();
+
+ expect(sortedHtml(element)).
+ toEqual('
Hello, Elvis!
');
+ }
+ ));
+
+
+ it('should compile, flush and link the template append', inject(
+ function($compile, $templateCache, $rootScope) {
+ $templateCache.put('hello.html', '
Hello, {{name}}! ');
+ $rootScope.name = 'Elvis';
+ var template = $compile('
');
+
+ element = template($rootScope);
+ $rootScope.$digest();
+
+ expect(sortedHtml(element)).
+ toEqual('
Hello, Elvis!
');
+ }
+ ));
+
+
+ it('should compile, flush and link the template inline', inject(
+ function($compile, $templateCache, $rootScope) {
+ $templateCache.put('hello.html', '
Hello, {{name}}! ');
+ $rootScope.name = 'Elvis';
+ var template = $compile('
');
+
+ element = template($rootScope);
+ $rootScope.$digest();
+
+ expect(sortedHtml(element)).
+ toEqual('
Hello, Elvis!
');
+ }
+ ));
+
+
+ it('should resolve widgets after cloning in append mode', function() {
+ module(function($exceptionHandlerProvider) {
+ $exceptionHandlerProvider.mode('log');
+ });
+ inject(function($compile, $templateCache, $rootScope, $httpBackend, $browser,
+ $exceptionHandler) {
+ $httpBackend.expect('GET', 'hello.html').respond('
{{greeting}} ');
+ $httpBackend.expect('GET', 'error.html').respond('
');
+ $templateCache.put('cau.html', '
{{name}} ');
+ $rootScope.greeting = 'Hello';
+ $rootScope.name = 'Elvis';
+ var template = $compile(
+ '
' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '
');
+ var e1;
+ var e2;
+
+ e1 = template($rootScope.$new(), noop); // clone
+ expect(e1.text()).toEqual('');
+
+ $httpBackend.flush();
+
+ e2 = template($rootScope.$new(), noop); // clone
+ $rootScope.$digest();
+ expect(e1.text()).toEqual('Hello Elvis');
+ expect(e2.text()).toEqual('Hello Elvis');
+
+ expect($exceptionHandler.errors.length).toEqual(2);
+ expect($exceptionHandler.errors[0][0].message).toEqual('cError');
+ expect($exceptionHandler.errors[1][0].message).toEqual('lError');
+
+ dealoc(e1);
+ dealoc(e2);
+ });
+ });
+
+
+ it('should resolve widgets after cloning in inline mode', function() {
+ module(function($exceptionHandlerProvider) {
+ $exceptionHandlerProvider.mode('log');
+ });
+ inject(function($compile, $templateCache, $rootScope, $httpBackend, $browser,
+ $exceptionHandler) {
+ $httpBackend.expect('GET', 'hello.html').respond('
{{greeting}} ');
+ $httpBackend.expect('GET', 'error.html').respond('
');
+ $templateCache.put('cau.html', '
{{name}} ');
+ $rootScope.greeting = 'Hello';
+ $rootScope.name = 'Elvis';
+ var template = $compile(
+ '
' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '
');
+ var e1;
+ var e2;
+
+ e1 = template($rootScope.$new(), noop); // clone
+ expect(e1.text()).toEqual('');
+
+ $httpBackend.flush();
+
+ e2 = template($rootScope.$new(), noop); // clone
+ $rootScope.$digest();
+ expect(e1.text()).toEqual('Hello Elvis');
+ expect(e2.text()).toEqual('Hello Elvis');
+
+ expect($exceptionHandler.errors.length).toEqual(2);
+ expect($exceptionHandler.errors[0][0].message).toEqual('cError');
+ expect($exceptionHandler.errors[1][0].message).toEqual('lError');
+
+ dealoc(e1);
+ dealoc(e2);
+ });
+ });
+
+
+ it('should be implicitly terminal and not compile placeholder content in append', inject(
+ function($compile, $templateCache, $rootScope, log) {
+ // we can't compile the contents because that would result in a memory leak
+
+ $templateCache.put('hello.html', 'Hello!');
+ element = $compile('
')($rootScope);
+
+ expect(log).toEqual('');
+ }
+ ));
+
+
+ it('should be implicitly terminal and not compile placeholder content in inline', inject(
+ function($compile, $templateCache, $rootScope, log) {
+ // we can't compile the contents because that would result in a memory leak
+
+ $templateCache.put('hello.html', 'Hello!');
+ element = $compile('
')($rootScope);
+
+ expect(log).toEqual('');
+ }
+ ));
+
+
+ it('should throw an error and clear element content if the template fails to load', inject(
+ function($compile, $httpBackend, $rootScope) {
+ $httpBackend.expect('GET', 'hello.html').respond(404, 'Not Found!');
+ element = $compile('
content
')($rootScope);
+
+ expect(function() {
+ $httpBackend.flush();
+ }).toThrow('Failed to load template: hello.html');
+ expect(sortedHtml(element)).toBe('
');
+ }
+ ));
+
+
+ it('should prevent multiple templates per element', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('sync', valueFn({
+ restrict: 'C',
+ template: '
'
+ }));
+ $compileProvider.directive('async', valueFn({
+ restrict: 'C',
+ templateUrl: 'template.html'
+ }));
+ });
+ inject(function($compile){
+ expect(function() {
+ $compile('
');
+ }).toThrow('Multiple directives [sync, async] asking for template on: <'+
+ (msie <= 8 ? 'DIV' : 'div') + ' class="sync async">');
+ });
+ });
+
+
+ describe('delay compile / linking functions until after template is resolved', function(){
+ var template;
+ beforeEach(module(function($compileProvider) {
+ function directive (name, priority, options) {
+ $compileProvider.directive(name, function(log) {
+ return (extend({
+ priority: priority,
+ compile: function() {
+ log(name + '-C');
+ return function() { log(name + '-L'); }
+ }
+ }, options || {}));
+ });
+ }
+
+ directive('first', 10);
+ directive('second', 5, { templateUrl: 'second.html' });
+ directive('third', 3);
+ directive('last', 0);
+
+ directive('iFirst', 10, {replace: true});
+ directive('iSecond', 5, {replace: true, templateUrl: 'second.html' });
+ directive('iThird', 3, {replace: true});
+ directive('iLast', 0, {replace: true});
+ }));
+
+ it('should flush after link append', inject(
+ function($compile, $rootScope, $httpBackend, log) {
+ $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
+ template = $compile('
');
+ element = template($rootScope);
+ expect(log).toEqual('first-C');
+
+ log('FLUSH');
+ $httpBackend.flush();
+ $rootScope.$digest();
+ expect(log).toEqual(
+ 'first-C; FLUSH; second-C; last-C; third-C; ' +
+ 'third-L; first-L; second-L; last-L');
+
+ var span = element.find('span');
+ expect(span.attr('first')).toEqual('');
+ expect(span.attr('second')).toEqual('');
+ expect(span.find('div').attr('third')).toEqual('');
+ expect(span.attr('last')).toEqual('');
+
+ expect(span.text()).toEqual('3');
+ }));
+
+
+ it('should flush after link inline', inject(
+ function($compile, $rootScope, $httpBackend, log) {
+ $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
+ template = $compile('
');
+ element = template($rootScope);
+ expect(log).toEqual('iFirst-C');
+
+ log('FLUSH');
+ $httpBackend.flush();
+ $rootScope.$digest();
+ expect(log).toEqual(
+ 'iFirst-C; FLUSH; iSecond-C; iThird-C; iLast-C; ' +
+ 'iFirst-L; iSecond-L; iThird-L; iLast-L');
+
+ var div = element.find('div');
+ expect(div.attr('i-first')).toEqual('');
+ expect(div.attr('i-second')).toEqual('');
+ expect(div.attr('i-third')).toEqual('');
+ expect(div.attr('i-last')).toEqual('');
+
+ expect(div.text()).toEqual('3');
+ }));
+
+
+ it('should flush before link append', inject(
+ function($compile, $rootScope, $httpBackend, log) {
+ $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
+ template = $compile('
');
+ expect(log).toEqual('first-C');
+ log('FLUSH');
+ $httpBackend.flush();
+ expect(log).toEqual('first-C; FLUSH; second-C; last-C; third-C');
+
+ element = template($rootScope);
+ $rootScope.$digest();
+ expect(log).toEqual(
+ 'first-C; FLUSH; second-C; last-C; third-C; ' +
+ 'third-L; first-L; second-L; last-L');
+
+ var span = element.find('span');
+ expect(span.attr('first')).toEqual('');
+ expect(span.attr('second')).toEqual('');
+ expect(span.find('div').attr('third')).toEqual('');
+ expect(span.attr('last')).toEqual('');
+
+ expect(span.text()).toEqual('3');
+ }));
+
+
+ it('should flush before link inline', inject(
+ function($compile, $rootScope, $httpBackend, log) {
+ $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
+ template = $compile('
');
+ expect(log).toEqual('iFirst-C');
+ log('FLUSH');
+ $httpBackend.flush();
+ expect(log).toEqual('iFirst-C; FLUSH; iSecond-C; iThird-C; iLast-C');
+
+ element = template($rootScope);
+ $rootScope.$digest();
+ expect(log).toEqual(
+ 'iFirst-C; FLUSH; iSecond-C; iThird-C; iLast-C; ' +
+ 'iFirst-L; iSecond-L; iThird-L; iLast-L');
+
+ var div = element.find('div');
+ expect(div.attr('i-first')).toEqual('');
+ expect(div.attr('i-second')).toEqual('');
+ expect(div.attr('i-third')).toEqual('');
+ expect(div.attr('i-last')).toEqual('');
+
+ expect(div.text()).toEqual('3');
+ }));
+ });
+
+
+ it('should check that template has root element', inject(function($compile, $httpBackend) {
+ $httpBackend.expect('GET', 'hello.html').respond('before
mid after');
+ $compile('
');
+ expect(function(){
+ $httpBackend.flush();
+ }).toThrow('Template must have exactly one root element: before
mid after');
+ }));
+
+
+ it('should allow multiple elements in template', inject(function($compile, $httpBackend) {
+ $httpBackend.expect('GET', 'hello.html').respond('before
mid after');
+ element = jqLite('
');
+ $compile(element);
+ $httpBackend.flush();
+ expect(element.text()).toEqual('before mid after');
+ }));
+
+
+ it('should work when widget is in root element', inject(
+ function($compile, $httpBackend, $rootScope) {
+ $httpBackend.expect('GET', 'hello.html').respond('
3==<> ');
+ element = jqLite('
{{1+2}} ');
+ $compile(element)($rootScope);
+
+ $httpBackend.flush();
+ expect(element.text()).toEqual('3==3');
+ }
+ ));
+
+
+ it('should work when widget is a repeater', inject(
+ function($compile, $httpBackend, $rootScope) {
+ $httpBackend.expect('GET', 'hello.html').respond('
i=<>; ');
+ element = jqLite('
{{i}}
');
+ $compile(element)($rootScope);
+
+ $httpBackend.flush();
+ expect(element.text()).toEqual('i=1;i=2;');
+ }
+ ));
+ });
+
+
+ describe('scope', function() {
+ var iscope;
+
+ beforeEach(module(function($compileProvider) {
+ forEach(['', 'a', 'b'], function(name) {
+ $compileProvider.directive('scope' + uppercase(name), function(log) {
+ return {
+ scope: true,
+ restrict: 'CA',
+ compile: function() {
+ return function (scope, element) {
+ log(scope.$id);
+ expect(element.data('$scope')).toBe(scope);
+ };
+ }
+ };
+ });
+ $compileProvider.directive('iscope' + uppercase(name), function(log) {
+ return {
+ scope: {},
+ restrict: 'CA',
+ compile: function() {
+ return function (scope, element) {
+ iscope = scope;
+ log(scope.$id);
+ expect(element.data('$scope')).toBe(scope);
+ };
+ }
+ };
+ });
+ $compileProvider.directive('tiscope' + uppercase(name), function(log) {
+ return {
+ scope: {},
+ restrict: 'CA',
+ templateUrl: 'tiscope.html',
+ compile: function() {
+ return function (scope, element) {
+ iscope = scope;
+ log(scope.$id);
+ expect(element.data('$scope')).toBe(scope);
+ };
+ }
+ };
+ });
+ });
+ $compileProvider.directive('log', function(log) {
+ return {
+ restrict: 'CA',
+ link: function(scope) {
+ log('log-' + scope.$id + '-' + scope.$parent.$id);
+ }
+ };
+ });
+ }));
+
+
+ it('should allow creation of new scopes', inject(function($rootScope, $compile, log) {
+ element = $compile('
')($rootScope);
+ expect(log).toEqual('LOG; log-002-001; 002');
+ expect(element.find('span').hasClass('ng-scope')).toBe(true);
+ }));
+
+
+ it('should allow creation of new isolated scopes for directives', inject(
+ function($rootScope, $compile, log) {
+ element = $compile('
')($rootScope);
+ expect(log).toEqual('LOG; log-002-001; 002');
+ $rootScope.name = 'abc';
+ expect(iscope.$parent).toBe($rootScope);
+ expect(iscope.name).toBeUndefined();
+ }));
+
+
+ it('should allow creation of new isolated scopes for directives with templates', inject(
+ function($rootScope, $compile, log, $httpBackend) {
+ $httpBackend.expect('GET', 'tiscope.html').respond('
');
+ element = $compile('
')($rootScope);
+ $httpBackend.flush();
+ expect(log).toEqual('LOG; log-002-001; 002');
+ $rootScope.name = 'abc';
+ expect(iscope.$parent).toBe($rootScope);
+ expect(iscope.name).toBeUndefined();
+ }));
+
+
+ it('should correctly create the scope hierachy', inject(
+ function($rootScope, $compile, log) {
+ element = $compile(
+ '
' + //1
+ '' + //2
+ ' ' + //3
+ ' ' +
+ ' ' +
+ '' + //4
+ ' ' +
+ ' ' +
+ '
'
+ )($rootScope);
+ expect(log).toEqual('LOG; log-003-002; 003; LOG; log-002-001; 002; LOG; log-004-001; 004');
+ })
+ );
+
+
+ it('should allow more one new scope directives per element, but directives should share' +
+ 'the scope', inject(
+ function($rootScope, $compile, log) {
+ element = $compile('
')($rootScope);
+ expect(log).toEqual('002; 002');
+ })
+ );
+
+ it('should not allow more then one isolate scope creation per element', inject(
+ function($rootScope, $compile) {
+ expect(function(){
+ $compile('
');
+ }).toThrow('Multiple directives [iscopeA, scopeB] asking for isolated scope on: ' +
+ '<' + (msie < 9 ? 'DIV' : 'div') +
+ ' class="iscope-a; scope-b ng-isolate-scope ng-scope">');
+ })
+ );
+
+
+ it('should not allow more then one isolate scope creation per element', inject(
+ function($rootScope, $compile) {
+ expect(function(){
+ $compile('
');
+ }).toThrow('Multiple directives [iscopeA, iscopeB] asking for isolated scope on: ' +
+ '<' + (msie < 9 ? 'DIV' : 'div') +
+ ' class="iscope-a; iscope-b ng-isolate-scope ng-scope">');
+ })
+ );
+
+
+ it('should create new scope even at the root of the template', inject(
+ function($rootScope, $compile, log) {
+ element = $compile('
')($rootScope);
+ expect(log).toEqual('002');
+ })
+ );
+
+
+ it('should create isolate scope even at the root of the template', inject(
+ function($rootScope, $compile, log) {
+ element = $compile('
')($rootScope);
+ expect(log).toEqual('002');
+ })
+ );
+ });
+ });
+ });
+
+
+ describe('interpolation', function() {
+ var observeSpy, attrValueDuringLinking;
+
+ beforeEach(module(function($compileProvider) {
+ $compileProvider.directive('observer', function() {
+ return function(scope, elm, attr) {
+ observeSpy = jasmine.createSpy('$observe attr');
+
+ attr.$observe('someAttr', observeSpy);
+ attrValueDuringLinking = attr.someAttr;
+ };
+ });
+ }));
+
+
+ it('should compile and link both attribute and text bindings', inject(
+ function($rootScope, $compile) {
+ $rootScope.name = 'angular';
+ element = $compile('
text: {{name}}
')($rootScope);
+ $rootScope.$digest();
+ expect(element.text()).toEqual('text: angular');
+ expect(element.attr('name')).toEqual('attr: angular');
+ }));
+
+
+ it('should decorate the binding with ng-binding and interpolation function', inject(
+ function($compile, $rootScope) {
+ element = $compile('
{{1+2}}
')($rootScope);
+ expect(element.hasClass('ng-binding')).toBe(true);
+ expect(element.data('$binding')[0].exp).toEqual('{{1+2}}');
+ }));
+
+
+ it('should observe interpolated attrs', inject(function($rootScope, $compile) {
+ $compile('
')($rootScope);
+
+ // should be async
+ expect(observeSpy).not.toHaveBeenCalled();
+
+ $rootScope.$apply(function() {
+ $rootScope.value = 'bound-value';
+ });
+ expect(observeSpy).toHaveBeenCalledOnceWith('bound-value');
+ }));
+
+
+ it('should set interpolated attrs to undefined', inject(function($rootScope, $compile) {
+ attrValueDuringLinking = null;
+ $compile('
')($rootScope);
+ expect(attrValueDuringLinking).toBeUndefined();
+ }));
+
+
+ it('should not call observer of non-interpolated attr', inject(function($rootScope, $compile) {
+ $compile('
')($rootScope);
+ expect(attrValueDuringLinking).toBe('nonBound');
+
+ $rootScope.$digest();
+ expect(observeSpy).not.toHaveBeenCalled();
+ }));
+
+
+ it('should delegate exceptions to $exceptionHandler', function() {
+ observeSpy = jasmine.createSpy('$observe attr').andThrow('ERROR');
+
+ module(function($compileProvider, $exceptionHandlerProvider) {
+ $exceptionHandlerProvider.mode('log');
+ $compileProvider.directive('error', function() {
+ return function(scope, elm, attr) {
+ attr.$observe('someAttr', observeSpy);
+ attr.$observe('someAttr', observeSpy);
+ };
+ });
+ });
+
+ inject(function($compile, $rootScope, $exceptionHandler) {
+ $compile('
')($rootScope);
+ $rootScope.$digest();
+
+ expect(observeSpy).toHaveBeenCalled();
+ expect(observeSpy.callCount).toBe(2);
+ expect($exceptionHandler.errors).toEqual(['ERROR', 'ERROR']);
+ });
+ });
+
+
+ it('should translate {{}} in terminal nodes', inject(function($rootScope, $compile) {
+ element = $compile('
Greet {{name}}! ')($rootScope)
+ $rootScope.$digest();
+ expect(sortedHtml(element).replace(' selected="true"', '')).
+ toEqual('
' +
+ 'Greet ! ' +
+ ' ');
+ $rootScope.name = 'Misko';
+ $rootScope.$digest();
+ expect(sortedHtml(element).replace(' selected="true"', '')).
+ toEqual('
' +
+ 'Greet Misko! ' +
+ ' ');
+ }));
+ });
+
+
+ describe('link phase', function() {
+
+ beforeEach(module(function($compileProvider) {
+
+ forEach(['a', 'b', 'c'], function(name) {
+ $compileProvider.directive(name, function(log) {
+ return {
+ restrict: 'ECA',
+ compile: function() {
+ log('t' + uppercase(name))
+ return {
+ pre: function() {
+ log('pre' + uppercase(name));
+ },
+ post: function linkFn() {
+ log('post' + uppercase(name));
+ }
+ };
+ }
+ };
+ });
+ });
+ }));
+
+
+ it('should not store linkingFns for noop branches', inject(function ($rootScope, $compile) {
+ element = jqLite('
ignore
');
+ var linkingFn = $compile(element);
+ // Now prune the branches with no directives
+ element.find('span').remove();
+ expect(element.find('span').length).toBe(0);
+ // and we should still be able to compile without errors
+ linkingFn($rootScope);
+ }));
+
+
+ it('should compile from top to bottom but link from bottom up', inject(
+ function($compile, $rootScope, log) {
+ element = $compile('
')($rootScope);
+ expect(log).toEqual('tA; tB; tC; preA; preB; preC; postC; postA; postB');
+ }
+ ));
+
+
+ it('should support link function on directive object', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('abc', valueFn({
+ link: function(scope, element, attrs) {
+ element.text(attrs.abc);
+ }
+ }));
+ });
+ inject(function($compile, $rootScope) {
+ element = $compile('
FAIL
')($rootScope);
+ expect(element.text()).toEqual('WORKS');
+ });
+ });
+ });
+
+
+ describe('attrs', function() {
+
+ it('should allow setting of attributes', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive({
+ setter: valueFn(function(scope, element, attr) {
+ attr.$set('name', 'abc');
+ attr.$set('disabled', true);
+ expect(attr.name).toBe('abc');
+ expect(attr.disabled).toBe(true);
+ })
+ });
+ });
+ inject(function($rootScope, $compile) {
+ element = $compile('
')($rootScope);
+ expect(element.attr('name')).toEqual('abc');
+ expect(element.attr('disabled')).toEqual('disabled');
+ });
+ });
+
+
+ it('should read boolean attributes as boolean only on control elements', function() {
+ var value;
+ module(function($compileProvider) {
+ $compileProvider.directive({
+ input: valueFn({
+ restrict: 'ECA',
+ link:function(scope, element, attr) {
+ value = attr.required;
+ }
+ })
+ });
+ });
+ inject(function($rootScope, $compile) {
+ element = $compile('
')($rootScope);
+ expect(value).toEqual(true);
+ });
+ });
+
+ it('should read boolean attributes as text on non-controll elements', function() {
+ var value;
+ module(function($compileProvider) {
+ $compileProvider.directive({
+ div: valueFn({
+ restrict: 'ECA',
+ link:function(scope, element, attr) {
+ value = attr.required;
+ }
+ })
+ });
+ });
+ inject(function($rootScope, $compile) {
+ element = $compile('
')($rootScope);
+ expect(value).toEqual('some text');
+ });
+ });
+
+ it('should allow setting of attributes', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive({
+ setter: valueFn(function(scope, element, attr) {
+ attr.$set('name', 'abc');
+ attr.$set('disabled', true);
+ expect(attr.name).toBe('abc');
+ expect(attr.disabled).toBe(true);
+ })
+ });
+ });
+ inject(function($rootScope, $compile) {
+ element = $compile('
')($rootScope);
+ expect(element.attr('name')).toEqual('abc');
+ expect(element.attr('disabled')).toEqual('disabled');
+ });
+ });
+
+
+ it('should create new instance of attr for each template stamping', function() {
+ module(function($compileProvider, $provide) {
+ var state = { first: [], second: [] };
+ $provide.value('state', state);
+ $compileProvider.directive({
+ first: valueFn({
+ priority: 1,
+ compile: function(templateElement, templateAttr) {
+ return function(scope, element, attr) {
+ state.first.push({
+ template: {element: templateElement, attr:templateAttr},
+ link: {element: element, attr: attr}
+ });
+ }
+ }
+ }),
+ second: valueFn({
+ priority: 2,
+ compile: function(templateElement, templateAttr) {
+ return function(scope, element, attr) {
+ state.second.push({
+ template: {element: templateElement, attr:templateAttr},
+ link: {element: element, attr: attr}
+ });
+ }
+ }
+ })
+ });
+ });
+ inject(function($rootScope, $compile, state) {
+ var template = $compile('
');
+ dealoc(template($rootScope.$new(), noop));
+ dealoc(template($rootScope.$new(), noop));
+
+ // instance between directives should be shared
+ expect(state.first[0].template.element).toBe(state.second[0].template.element);
+ expect(state.first[0].template.attr).toBe(state.second[0].template.attr);
+
+ // the template and the link can not be the same instance
+ expect(state.first[0].template.element).not.toBe(state.first[0].link.element);
+ expect(state.first[0].template.attr).not.toBe(state.first[0].link.attr);
+
+ // each new template needs to be new instance
+ expect(state.first[0].link.element).not.toBe(state.first[1].link.element);
+ expect(state.first[0].link.attr).not.toBe(state.first[1].link.attr);
+ expect(state.second[0].link.element).not.toBe(state.second[1].link.element);
+ expect(state.second[0].link.attr).not.toBe(state.second[1].link.attr);
+ });
+ });
+
+
+ it('should properly $observe inside ng-repeat', function() {
+ var spies = [];
+
+ module(function($compileProvider) {
+ $compileProvider.directive('observer', function() {
+ return function(scope, elm, attr) {
+ spies.push(jasmine.createSpy('observer ' + spies.length));
+ attr.$observe('some', spies[spies.length - 1]);
+ };
+ });
+ });
+
+ inject(function($compile, $rootScope) {
+ element = $compile('
')($rootScope);
+
+ $rootScope.$apply(function() {
+ $rootScope.items = [{id: 1}, {id: 2}];
+ });
+
+ expect(spies[0]).toHaveBeenCalledOnceWith('id_1');
+ expect(spies[1]).toHaveBeenCalledOnceWith('id_2');
+ spies[0].reset();
+ spies[1].reset();
+
+ $rootScope.$apply(function() {
+ $rootScope.items[0].id = 5;
+ });
+
+ expect(spies[0]).toHaveBeenCalledOnceWith('id_5');
+ });
+ });
+
+
+ describe('$set', function() {
+ var attr;
+ beforeEach(function(){
+ module(function($compileProvider) {
+ $compileProvider.directive('input', valueFn({
+ restrict: 'ECA',
+ link: function(scope, element, attr) {
+ scope.attr = attr;
+ }
+ }));
+ });
+ inject(function($compile, $rootScope) {
+ element = $compile('
')($rootScope);
+ attr = $rootScope.attr;
+ expect(attr).toBeDefined();
+ });
+ });
+
+
+ it('should set attributes', function() {
+ attr.$set('ngMyAttr', 'value');
+ expect(element.attr('ng-my-attr')).toEqual('value');
+ expect(attr.ngMyAttr).toEqual('value');
+ });
+
+
+ it('should allow overriding of attribute name and remember the name', function() {
+ attr.$set('ngOther', '123', true, 'other');
+ expect(element.attr('other')).toEqual('123');
+ expect(attr.ngOther).toEqual('123');
+
+ attr.$set('ngOther', '246');
+ expect(element.attr('other')).toEqual('246');
+ expect(attr.ngOther).toEqual('246');
+ });
+
+
+ it('should remove attribute', function() {
+ attr.$set('ngMyAttr', 'value');
+ expect(element.attr('ng-my-attr')).toEqual('value');
+
+ attr.$set('ngMyAttr', undefined);
+ expect(element.attr('ng-my-attr')).toBe(undefined);
+
+ attr.$set('ngMyAttr', 'value');
+ attr.$set('ngMyAttr', null);
+ expect(element.attr('ng-my-attr')).toBe(undefined);
+ });
+
+
+ it('should not set DOM element attr if writeAttr false', function() {
+ attr.$set('test', 'value', false);
+
+ expect(element.attr('test')).toBeUndefined();
+ expect(attr.test).toBe('value');
+ });
+ });
+ });
+
+
+ describe('locals', function() {
+ it('should marshal to locals', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('widget', function(log) {
+ return {
+ scope: {
+ attr: 'attribute',
+ prop: 'evaluate',
+ bind: 'bind',
+ assign: 'accessor',
+ read: 'accessor',
+ exp: 'expression',
+ nonExist: 'accessor',
+ nonExistExpr: 'expression'
+ },
+ link: function(scope, element, attrs) {
+ scope.nonExist(); // noop
+ scope.nonExist(123); // noop
+ scope.nonExistExpr(); // noop
+ scope.nonExistExpr(123); // noop
+ log(scope.attr);
+ log(scope.prop);
+ log(scope.assign());
+ log(scope.read());
+ log(scope.assign('ng'));
+ scope.exp({myState:'OK'});
+ expect(function() { scope.read(undefined); }).
+ toThrow("Expression ''D'' not assignable.");
+ scope.$watch('bind', log);
+ }
+ };
+ });
+ });
+ inject(function(log, $compile, $rootScope) {
+ $rootScope.myProp = 'B';
+ $rootScope.bi = {nd: 'C'};
+ $rootScope.name = 'C';
+ element = $compile(
+ '
')
+ ($rootScope);
+ expect(log).toEqual('A; B; C; D; ng');
+ expect($rootScope.name).toEqual('ng');
+ expect($rootScope.state).toEqual('OK');
+ log.reset();
+ $rootScope.$apply();
+ expect(element.text()).toEqual('C');
+ expect(log).toEqual('C');
+ $rootScope.bi.nd = 'c';
+ $rootScope.$apply();
+ expect(log).toEqual('C; c');
+ });
+ });
+ });
+
+
+ describe('controller', function() {
+ it('should inject locals to controller', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('widget', function(log) {
+ return {
+ controller: function(attr, prop, assign, read, exp){
+ log(attr);
+ log(prop);
+ log(assign());
+ log(read());
+ log(assign('ng'));
+ exp();
+ expect(function() { read(undefined); }).
+ toThrow("Expression ''D'' not assignable.");
+ this.result = 'OK';
+ },
+ inject: {
+ attr: 'attribute',
+ prop: 'evaluate',
+ assign: 'accessor',
+ read: 'accessor',
+ exp: 'expression'
+ },
+ link: function(scope, element, attrs, controller) {
+ log(controller.result);
+ }
+ };
+ });
+ });
+ inject(function(log, $compile, $rootScope) {
+ $rootScope.myProp = 'B';
+ $rootScope.bi = {nd: 'C'};
+ $rootScope.name = 'C';
+ element = $compile(
+ '
')
+ ($rootScope);
+ expect(log).toEqual('A; B; C; D; ng; OK');
+ expect($rootScope.name).toEqual('ng');
+ });
+ });
+
+
+ it('should get required controller', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('main', function(log) {
+ return {
+ priority: 2,
+ controller: function() {
+ this.name = 'main';
+ },
+ link: function(scope, element, attrs, controller) {
+ log(controller.name);
+ }
+ };
+ });
+ $compileProvider.directive('dep', function(log) {
+ return {
+ priority: 1,
+ require: 'main',
+ link: function(scope, element, attrs, controller) {
+ log('dep:' + controller.name);
+ }
+ };
+ });
+ $compileProvider.directive('other', function(log) {
+ return {
+ link: function(scope, element, attrs, controller) {
+ log(!!controller); // should be false
+ }
+ };
+ });
+ });
+ inject(function(log, $compile, $rootScope) {
+ element = $compile('
')($rootScope);
+ expect(log).toEqual('main; dep:main; false');
+ });
+ });
+
+
+ it('should require controller on parent element',function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('main', function(log) {
+ return {
+ controller: function() {
+ this.name = 'main';
+ }
+ };
+ });
+ $compileProvider.directive('dep', function(log) {
+ return {
+ require: '^main',
+ link: function(scope, element, attrs, controller) {
+ log('dep:' + controller.name);
+ }
+ };
+ });
+ });
+ inject(function(log, $compile, $rootScope) {
+ element = $compile('
')($rootScope);
+ expect(log).toEqual('dep:main');
+ });
+ });
+
+
+ it('should have optional controller on current element', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('dep', function(log) {
+ return {
+ require: '?main',
+ link: function(scope, element, attrs, controller) {
+ log('dep:' + !!controller);
+ }
+ };
+ });
+ });
+ inject(function(log, $compile, $rootScope) {
+ element = $compile('
')($rootScope);
+ expect(log).toEqual('dep:false');
+ });
+ });
+
+
+ it('should support multiple controllers', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('c1', valueFn({
+ controller: function() { this.name = 'c1'; }
+ }));
+ $compileProvider.directive('c2', valueFn({
+ controller: function() { this.name = 'c2'; }
+ }));
+ $compileProvider.directive('dep', function(log) {
+ return {
+ require: ['^c1', '^c2'],
+ link: function(scope, element, attrs, controller) {
+ log('dep:' + controller[0].name + '-' + controller[1].name);
+ }
+ };
+ });
+ });
+ inject(function(log, $compile, $rootScope) {
+ element = $compile('
')($rootScope);
+ expect(log).toEqual('dep:c1-c2');
+ });
+
+ });
+ });
+
+
+ describe('transclude', function() {
+ it('should compile get templateFn', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('trans', function(log) {
+ return {
+ transclude: 'element',
+ priority: 2,
+ controller: function($transclude) { this.$transclude = $transclude; },
+ compile: function(element, attrs, template) {
+ log('compile: ' + angular.mock.dump(element));
+ return function(scope, element, attrs, ctrl) {
+ log('link');
+ var cursor = element;
+ template(scope.$new(), function(clone) {cursor.after(cursor = clone)});
+ ctrl.$transclude(function(clone) {cursor.after(clone)});
+ };
+ }
+ }
+ });
+ });
+ inject(function(log, $rootScope, $compile) {
+ element = $compile('
')
+ ($rootScope);
+ $rootScope.$apply();
+ expect(log).toEqual('compile: ; HIGH; link; LOG; LOG');
+ expect(element.text()).toEqual('001-002;001-003;');
+ });
+ });
+
+
+ it('should support transclude directive', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('trans', function() {
+ return {
+ transclude: 'content',
+ replace: true,
+ scope: true,
+ template: '
W:{{$parent.$id}}-{{$id}}; '
+ }
+ });
+ });
+ inject(function(log, $rootScope, $compile) {
+ element = $compile('
T:{{$parent.$id}}-{{$id}};
')
+ ($rootScope);
+ $rootScope.$apply();
+ expect(element.text()).toEqual('W:001-002;T:001-003;');
+ expect(jqLite(element.find('span')[0]).text()).toEqual('T:001-003');
+ expect(jqLite(element.find('span')[1]).text()).toEqual(';');
+ });
+ });
+
+
+ it('should transclude transcluded content', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('book', valueFn({
+ transclude: 'content',
+ template: '
'
+ }));
+ $compileProvider.directive('chapter', valueFn({
+ transclude: 'content',
+ templateUrl: 'chapter.html'
+ }));
+ $compileProvider.directive('section', valueFn({
+ transclude: 'content',
+ template: '
'
+ }));
+ return function($httpBackend) {
+ $httpBackend.
+ expect('GET', 'chapter.html').
+ respond('
');
+ }
+ });
+ inject(function(log, $rootScope, $compile, $httpBackend) {
+ element = $compile('
')($rootScope);
+ $rootScope.$apply();
+
+ expect(element.text()).toEqual('book-');
+
+ $httpBackend.flush();
+ $rootScope.$apply();
+ expect(element.text()).toEqual('book-chapter-section-![(paragraph)]!');
+ });
+ });
+
+
+ it('should only allow one transclude per element', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('first', valueFn({
+ scope: {},
+ restrict: 'CA',
+ transclude: 'content'
+ }));
+ $compileProvider.directive('second', valueFn({
+ restrict: 'CA',
+ transclude: 'content'
+ }));
+ });
+ inject(function($compile) {
+ expect(function() {
+ $compile('
');
+ }).toThrow('Multiple directives [first, second] asking for transclusion on: <' +
+ (msie <= 8 ? 'DIV' : 'div') + ' class="first second ng-isolate-scope ng-scope">');
+ });
+ });
+
+
+ it('should remove transclusion scope, when the DOM is destroyed', function() {
+ module(function($compileProvider) {
+ $compileProvider.directive('box', valueFn({
+ transclude: 'content',
+ scope: { name: 'evaluate', show: 'accessor' },
+ template: '
',
+ link: function(scope, element) {
+ scope.$watch(
+ function() { return scope.show(); },
+ function(show) {
+ if (!show) {
+ element.find('div').find('div').remove();
+ }
+ }
+ );
+ }
+ }));
+ });
+ inject(function($compile, $rootScope) {
+ $rootScope.username = 'Misko';
+ $rootScope.select = true;
+ element = $compile(
+ '
')
+ ($rootScope);
+ $rootScope.$apply();
+ expect(element.text()).toEqual('Hello: Misko!user: Misko');
+
+ var widgetScope = $rootScope.$$childHead;
+ var transcludeScope = widgetScope.$$nextSibling;
+ expect(widgetScope.name).toEqual('Misko');
+ expect(widgetScope.$parent).toEqual($rootScope);
+ expect(transcludeScope.$parent).toEqual($rootScope);
+
+ $rootScope.select = false;
+ $rootScope.$apply();
+ expect(element.text()).toEqual('Hello: Misko!');
+ expect(widgetScope.$$nextSibling).toEqual(null);
+ });
+ });
+
+
+ it('should support transcluded element on root content', function() {
+ var comment;
+ module(function($compileProvider) {
+ $compileProvider.directive('transclude', valueFn({
+ transclude: 'element',
+ compile: function(element, attr, linker) {
+ return function(scope, element, attr) {
+ comment = element;
+ };
+ }
+ }));
+ });
+ inject(function($compile, $rootScope) {
+ var element = jqLite('
').contents();
+ expect(element.length).toEqual(3);
+ expect(nodeName_(element[1])).toBe('DIV');
+ $compile(element)($rootScope);
+ expect(nodeName_(element[1])).toBe('#comment');
+ expect(nodeName_(comment)).toBe('#comment');
+ });
+ });
+ });
+});
diff --git a/test/ng/compilerSpec.js b/test/ng/compilerSpec.js
deleted file mode 100644
index 1aef24fe..00000000
--- a/test/ng/compilerSpec.js
+++ /dev/null
@@ -1,1869 +0,0 @@
-'use strict';
-
-describe('$compile', function() {
- var element;
-
- beforeEach(module(provideLog, function($provide, $compileProvider){
- element = null;
-
- $compileProvider.directive('log', function(log) {
- return {
- restrict: 'CAM',
- priority:0,
- compile: valueFn(function(scope, element, attrs) {
- log(attrs.log || 'LOG');
- })
- };
- });
-
- $compileProvider.directive('highLog', function(log) {
- return { restrict: 'CAM', priority:3, compile: valueFn(function(scope, element, attrs) {
- log(attrs.highLog || 'HIGH');
- })};
- });
-
- $compileProvider.directive('mediumLog', function(log) {
- return { restrict: 'CAM', priority:2, compile: valueFn(function(scope, element, attrs) {
- log(attrs.mediumLog || 'MEDIUM');
- })};
- });
-
- $compileProvider.directive('greet', function() {
- return { restrict: 'CAM', priority:10, compile: valueFn(function(scope, element, attrs) {
- element.text("Hello " + attrs.greet);
- })};
- });
-
- $compileProvider.directive('set', function() {
- return function(scope, element, attrs) {
- element.text(attrs.set);
- };
- });
-
- $compileProvider.directive('mediumStop', valueFn({
- priority: 2,
- terminal: true
- }));
-
- $compileProvider.directive('stop', valueFn({
- terminal: true
- }));
-
- $compileProvider.directive('negativeStop', valueFn({
- priority: -100, // even with negative priority we still should be able to stop descend
- terminal: true
- }));
- }));
-
-
- afterEach(function(){
- dealoc(element);
- });
-
-
- describe('configuration', function() {
- it('should register a directive', function() {
- module(function($compileProvider) {
- $compileProvider.directive('div', function(log) {
- return {
- restrict: 'ECA',
- link: function(scope, element) {
- log('OK');
- element.text('SUCCESS');
- }
- };
- })
- });
- inject(function($compile, $rootScope, log) {
- element = $compile('
')($rootScope);
- expect(element.text()).toEqual('SUCCESS');
- expect(log).toEqual('OK');
- })
- });
-
- it('should allow registration of multiple directives with same name', function() {
- module(function($compileProvider) {
- $compileProvider.directive('div', function(log) {
- return {
- restrict: 'ECA',
- link: log.fn('1')
- };
- });
- $compileProvider.directive('div', function(log) {
- return {
- restrict: 'ECA',
- link: log.fn('2')
- };
- });
- });
- inject(function($compile, $rootScope, log) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('1; 2');
- });
- });
- });
-
-
- describe('compile phase', function() {
-
- it('should wrap root text nodes in spans', inject(function($compile, $rootScope) {
- element = jqLite('
A<a>B</a>C
');
- var text = element.contents();
- expect(text[0].nodeName).toEqual('#text');
- text = $compile(text)($rootScope);
- expect(lowercase(text[0].nodeName)).toEqual('span');
- expect(element.find('span').text()).toEqual('A
B C');
- }));
-
- describe('multiple directives per element', function() {
- it('should allow multiple directives per element', inject(function($compile, $rootScope, log){
- element = $compile(
- '
')
- ($rootScope);
- expect(element.text()).toEqual('Hello angular');
- expect(log).toEqual('H; M; L');
- }));
-
-
- it('should recurse to children', inject(function($compile, $rootScope){
- element = $compile('
')($rootScope);
- expect(element.text()).toEqual('0hello2angular4');
- }));
-
-
- it('should allow directives in classes', inject(function($compile, $rootScope, log) {
- element = $compile('
')($rootScope);
- expect(element.html()).toEqual('Hello angular');
- expect(log).toEqual('123');
- }));
-
-
- it('should ignore not set CSS classes on SVG elements', inject(function($compile, $rootScope, log) {
- if (!window.SVGElement) return;
- // According to spec SVG element className property is readonly, but only FF
- // implements it this way which causes compile exceptions.
- element = $compile('
{{1}} ')($rootScope);
- $rootScope.$digest();
- expect(element.text()).toEqual('1');
- }));
-
-
- it('should allow directives in comments', inject(
- function($compile, $rootScope, log) {
- element = $compile('
01
')($rootScope);
- expect(log).toEqual('angular');
- }
- ));
-
-
- it('should receive scope, element, and attributes', function() {
- var injector;
- module(function($compileProvider) {
- $compileProvider.directive('log', function($injector, $rootScope) {
- injector = $injector;
- return {
- restrict: 'CA',
- compile: function(element, templateAttr) {
- expect(typeof templateAttr.$normalize).toBe('function');
- expect(typeof templateAttr.$set).toBe('function');
- expect(isElement(templateAttr.$$element)).toBeTruthy();
- expect(element.text()).toEqual('unlinked');
- expect(templateAttr.exp).toEqual('abc');
- expect(templateAttr.aa).toEqual('A');
- expect(templateAttr.bb).toEqual('B');
- expect(templateAttr.cc).toEqual('C');
- return function(scope, element, attr) {
- expect(element.text()).toEqual('unlinked');
- expect(attr).toBe(templateAttr);
- expect(scope).toEqual($rootScope);
- element.text('worked');
- }
- }
- };
- });
- });
- inject(function($rootScope, $compile, $injector) {
- element = $compile(
- '
unlinked
')($rootScope);
- expect(element.text()).toEqual('worked');
- expect(injector).toBe($injector); // verify that directive is injectable
- });
- });
- });
-
- describe('error handling', function() {
-
- it('should handle exceptions', function() {
- module(function($compileProvider, $exceptionHandlerProvider) {
- $exceptionHandlerProvider.mode('log');
- $compileProvider.directive('factoryError', function() { throw 'FactoryError'; });
- $compileProvider.directive('templateError',
- valueFn({ compile: function() { throw 'TemplateError'; } }));
- $compileProvider.directive('linkingError',
- valueFn(function() { throw 'LinkingError'; }));
- });
- inject(function($rootScope, $compile, $exceptionHandler) {
- element = $compile('
')($rootScope);
- expect($exceptionHandler.errors[0]).toEqual('FactoryError');
- expect($exceptionHandler.errors[1][0]).toEqual('TemplateError');
- expect(ie($exceptionHandler.errors[1][1])).
- toEqual('
');
- expect($exceptionHandler.errors[2][0]).toEqual('LinkingError');
- expect(ie($exceptionHandler.errors[2][1])).
- toEqual('
');
-
-
- // crazy stuff to make IE happy
- function ie(text) {
- var list = [],
- parts, elementName;
-
- parts = lowercase(text).
- replace('<', '').
- replace('>', '').
- split(' ');
- elementName = parts.shift();
- parts.sort();
- parts.unshift(elementName);
- forEach(parts, function(value, key){
- if (value.substring(0,3) == 'ng-') {
- } else {
- value = value.replace('=""', '');
- var match = value.match(/=(.*)/);
- if (match && match[1].charAt(0) != '"') {
- value = value.replace(/=(.*)/, '="$1"');
- }
- list.push(value);
- }
- });
- return '<' + list.join(' ') + '>';
- }
- });
- });
-
-
- it('should allow changing the template structure after the current node', function() {
- module(function($compileProvider){
- $compileProvider.directive('after', valueFn({
- compile: function(element) {
- element.after('
B ');
- }
- }));
- });
- inject(function($compile, $rootScope, log){
- element = jqLite("
");
- $compile(element)($rootScope);
- expect(element.text()).toBe('AB');
- expect(log).toEqual('LOG');
- });
- });
-
-
- it('should allow changing the template structure after the current node inside ngRepeat', function() {
- module(function($compileProvider){
- $compileProvider.directive('after', valueFn({
- compile: function(element) {
- element.after('
B ');
- }
- }));
- });
- inject(function($compile, $rootScope, log){
- element = jqLite('
');
- $compile(element)($rootScope);
- $rootScope.$digest();
- expect(element.text()).toBe('ABAB');
- expect(log).toEqual('LOG; LOG');
- });
- });
- });
-
- describe('compiler control', function() {
- describe('priority', function() {
- it('should honor priority', inject(function($compile, $rootScope, log){
- element = $compile(
- '
')
- ($rootScope);
- expect(log).toEqual('H; M; L');
- }));
- });
-
-
- describe('terminal', function() {
-
- it('should prevent further directives from running', inject(function($rootScope, $compile) {
- element = $compile('
')($rootScope);
- expect(element.text()).toEqual('OK');
- }
- ));
-
-
- it('should prevent further directives from running, but finish current priority level',
- inject(function($rootScope, $compile, log) {
- // class is processed after attrs, so putting log in class will put it after
- // the stop in the current level. This proves that the log runs after stop
- element = $compile(
- '
')($rootScope);
- expect(element.text()).toEqual('OK');
- expect(log.toArray().sort()).toEqual(['HIGH', 'MEDIUM']);
- })
- );
- });
-
-
- describe('restrict', function() {
-
- it('should allow restriction of attributes', function() {
- module(function($compileProvider, $provide) {
- forEach({div:'E', attr:'A', clazz:'C', all:'EAC'}, function(restrict, name) {
- $compileProvider.directive(name, function(log) {
- return {
- restrict: restrict,
- compile: valueFn(function(scope, element, attr) {
- log(name);
- })
- };
- });
- });
- });
- inject(function($rootScope, $compile, log) {
- dealoc($compile('
')($rootScope));
- expect(log).toEqual('');
- log.reset();
-
- dealoc($compile('
')($rootScope));
- expect(log).toEqual('div');
- log.reset();
-
- dealoc($compile('
')($rootScope));
- expect(log).toEqual('');
- log.reset();
-
- dealoc($compile('
')($rootScope));
- expect(log).toEqual('attr');
- log.reset();
-
- dealoc($compile('
')($rootScope));
- expect(log).toEqual('');
- log.reset();
-
- dealoc($compile('
')($rootScope));
- expect(log).toEqual('clazz');
- log.reset();
-
- dealoc($compile('
')($rootScope));
- expect(log).toEqual('all; all; all');
- });
- });
- });
-
-
- describe('template', function() {
-
-
- beforeEach(module(function($compileProvider) {
- $compileProvider.directive('replace', valueFn({
- restrict: 'CAM',
- replace: true,
- template: '
Hello: <>
',
- compile: function(element, attr) {
- attr.$set('compiled', 'COMPILED');
- expect(element).toBe(attr.$$element);
- }
- }));
- $compileProvider.directive('append', valueFn({
- restrict: 'CAM',
- template: '
Hello: <>
',
- compile: function(element, attr) {
- attr.$set('compiled', 'COMPILED');
- expect(element).toBe(attr.$$element);
- }
- }));
- }));
-
-
- it('should replace element with template', inject(function($compile, $rootScope) {
- element = $compile('
content
')($rootScope);
- expect(element.text()).toEqual('Hello: content');
- expect(element.find('div').attr('compiled')).toEqual('COMPILED');
- }));
-
-
- it('should append element with template', inject(function($compile, $rootScope) {
- element = $compile('
content
')($rootScope);
- expect(element.text()).toEqual('Hello: content');
- expect(element.find('div').attr('compiled')).toEqual('COMPILED');
- }));
-
-
- it('should compile replace template', inject(function($compile, $rootScope, log) {
- element = $compile('
{{ "angular" }}
')
- ($rootScope);
- $rootScope.$digest();
- expect(element.text()).toEqual('Hello: angular');
- // HIGH goes after MEDIUM since it executes as part of replaced template
- expect(log).toEqual('MEDIUM; HIGH; LOG');
- }));
-
-
- it('should compile append template', inject(function($compile, $rootScope, log) {
- element = $compile('
{{ "angular" }}
')
- ($rootScope);
- $rootScope.$digest();
- expect(element.text()).toEqual('Hello: angular');
- expect(log).toEqual('HIGH; LOG; MEDIUM');
- }));
-
-
- it('should merge attributes including style attr', inject(function($compile, $rootScope) {
- element = $compile(
- '
')
- ($rootScope);
- var div = element.find('div');
- expect(div.hasClass('medium-log')).toBe(true);
- expect(div.hasClass('log')).toBe(true);
- expect(div.css('width')).toBe('10px');
- expect(div.css('height')).toBe('20px');
- expect(div.attr('replace')).toEqual('');
- expect(div.attr('high-log')).toEqual('');
- }));
-
- it('should prevent multiple templates per element', inject(function($compile) {
- try {
- $compile('
')
- fail();
- } catch(e) {
- expect(e.message).toMatch(/Multiple directives .* asking for template/);
- }
- }));
-
- it('should play nice with repeater when inline', inject(function($compile, $rootScope) {
- element = $compile(
- '
')($rootScope);
- $rootScope.$digest();
- expect(element.text()).toEqual('Hello: 1; Hello: 2; ');
- }));
-
-
- it('should play nice with repeater when append', inject(function($compile, $rootScope) {
- element = $compile(
- '
')($rootScope);
- $rootScope.$digest();
- expect(element.text()).toEqual('Hello: 1; Hello: 2; ');
- }));
-
-
- it('should merge interpolated css class', inject(function($compile, $rootScope) {
- element = $compile('
')($rootScope);
-
- $rootScope.$apply(function() {
- $rootScope.cls = 'two';
- });
-
- expect(element).toHaveClass('one');
- expect(element).toHaveClass('two'); // interpolated
- expect(element).toHaveClass('three');
- expect(element).toHaveClass('log'); // merged from replace directive template
- }));
-
-
- it('should merge interpolated css class with ngRepeat',
- inject(function($compile, $rootScope) {
- element = $compile(
- '
')($rootScope);
-
- $rootScope.$apply(function() {
- $rootScope.cls = 'two';
- });
-
- var child = element.find('div').eq(0);
- expect(child).toHaveClass('one');
- expect(child).toHaveClass('two'); // interpolated
- expect(child).toHaveClass('three');
- expect(child).toHaveClass('log'); // merged from replace directive template
- }));
- });
-
-
- describe('async templates', function() {
-
- beforeEach(module(
- function($compileProvider) {
- $compileProvider.directive('hello', valueFn({ restrict: 'CAM', templateUrl: 'hello.html' }));
- $compileProvider.directive('cau', valueFn({ restrict: 'CAM', templateUrl:'cau.html' }));
-
- $compileProvider.directive('cError', valueFn({
- restrict: 'CAM',
- templateUrl:'error.html',
- compile: function() {
- throw Error('cError');
- }
- }));
- $compileProvider.directive('lError', valueFn({
- restrict: 'CAM',
- templateUrl: 'error.html',
- compile: function() {
- throw Error('lError');
- }
- }));
-
-
- $compileProvider.directive('iHello', valueFn({
- restrict: 'CAM',
- replace: true,
- templateUrl: 'hello.html'
- }));
- $compileProvider.directive('iCau', valueFn({
- restrict: 'CAM',
- replace: true,
- templateUrl:'cau.html'
- }));
-
- $compileProvider.directive('iCError', valueFn({
- restrict: 'CAM',
- replace: true,
- templateUrl:'error.html',
- compile: function() {
- throw Error('cError');
- }
- }));
- $compileProvider.directive('iLError', valueFn({
- restrict: 'CAM',
- replace: true,
- templateUrl: 'error.html',
- compile: function() {
- throw Error('lError');
- }
- }));
-
- }
- ));
-
-
- it('should append template via $http and cache it in $templateCache', inject(
- function($compile, $httpBackend, $templateCache, $rootScope, $browser) {
- $httpBackend.expect('GET', 'hello.html').respond('
Hello! World!');
- $templateCache.put('cau.html', '
Cau! ');
- element = $compile('
ignore ignore
')($rootScope);
- expect(sortedHtml(element)).
- toEqual('
');
-
- $rootScope.$digest();
-
-
- expect(sortedHtml(element)).
- toEqual('
Cau!
');
-
- $httpBackend.flush();
- expect(sortedHtml(element)).toEqual(
- '
' +
- 'Hello! World! ' +
- 'Cau! ' +
- '
');
- }
- ));
-
-
- it('should inline template via $http and cache it in $templateCache', inject(
- function($compile, $httpBackend, $templateCache, $rootScope) {
- $httpBackend.expect('GET', 'hello.html').respond('
Hello! ');
- $templateCache.put('cau.html', '
Cau! ');
- element = $compile('
ignore ignore
')($rootScope);
- expect(sortedHtml(element)).
- toEqual('
');
-
- $rootScope.$digest();
-
-
- expect(sortedHtml(element)).
- toEqual('
Cau!
');
-
- $httpBackend.flush();
- expect(sortedHtml(element)).
- toEqual('
Hello! Cau!
');
- }
- ));
-
-
- it('should compile, link and flush the template append', inject(
- function($compile, $templateCache, $rootScope, $browser) {
- $templateCache.put('hello.html', '
Hello, {{name}}! ');
- $rootScope.name = 'Elvis';
- element = $compile('
')($rootScope);
-
- $rootScope.$digest();
-
- expect(sortedHtml(element)).
- toEqual('
Hello, Elvis!
');
- }
- ));
-
-
- it('should compile, link and flush the template inline', inject(
- function($compile, $templateCache, $rootScope) {
- $templateCache.put('hello.html', '
Hello, {{name}}! ');
- $rootScope.name = 'Elvis';
- element = $compile('
')($rootScope);
-
- $rootScope.$digest();
-
- expect(sortedHtml(element)).
- toEqual('
Hello, Elvis!
');
- }
- ));
-
-
- it('should compile, flush and link the template append', inject(
- function($compile, $templateCache, $rootScope) {
- $templateCache.put('hello.html', '
Hello, {{name}}! ');
- $rootScope.name = 'Elvis';
- var template = $compile('
');
-
- element = template($rootScope);
- $rootScope.$digest();
-
- expect(sortedHtml(element)).
- toEqual('
Hello, Elvis!
');
- }
- ));
-
-
- it('should compile, flush and link the template inline', inject(
- function($compile, $templateCache, $rootScope) {
- $templateCache.put('hello.html', '
Hello, {{name}}! ');
- $rootScope.name = 'Elvis';
- var template = $compile('
');
-
- element = template($rootScope);
- $rootScope.$digest();
-
- expect(sortedHtml(element)).
- toEqual('
Hello, Elvis!
');
- }
- ));
-
-
- it('should resolve widgets after cloning in append mode', function() {
- module(function($exceptionHandlerProvider) {
- $exceptionHandlerProvider.mode('log');
- });
- inject(function($compile, $templateCache, $rootScope, $httpBackend, $browser,
- $exceptionHandler) {
- $httpBackend.expect('GET', 'hello.html').respond('
{{greeting}} ');
- $httpBackend.expect('GET', 'error.html').respond('
');
- $templateCache.put('cau.html', '
{{name}} ');
- $rootScope.greeting = 'Hello';
- $rootScope.name = 'Elvis';
- var template = $compile(
- '
' +
- ' ' +
- ' ' +
- ' ' +
- ' ' +
- '
');
- var e1;
- var e2;
-
- e1 = template($rootScope.$new(), noop); // clone
- expect(e1.text()).toEqual('');
-
- $httpBackend.flush();
-
- e2 = template($rootScope.$new(), noop); // clone
- $rootScope.$digest();
- expect(e1.text()).toEqual('Hello Elvis');
- expect(e2.text()).toEqual('Hello Elvis');
-
- expect($exceptionHandler.errors.length).toEqual(2);
- expect($exceptionHandler.errors[0][0].message).toEqual('cError');
- expect($exceptionHandler.errors[1][0].message).toEqual('lError');
-
- dealoc(e1);
- dealoc(e2);
- });
- });
-
-
- it('should resolve widgets after cloning in inline mode', function() {
- module(function($exceptionHandlerProvider) {
- $exceptionHandlerProvider.mode('log');
- });
- inject(function($compile, $templateCache, $rootScope, $httpBackend, $browser,
- $exceptionHandler) {
- $httpBackend.expect('GET', 'hello.html').respond('
{{greeting}} ');
- $httpBackend.expect('GET', 'error.html').respond('
');
- $templateCache.put('cau.html', '
{{name}} ');
- $rootScope.greeting = 'Hello';
- $rootScope.name = 'Elvis';
- var template = $compile(
- '
' +
- ' ' +
- ' ' +
- ' ' +
- ' ' +
- '
');
- var e1;
- var e2;
-
- e1 = template($rootScope.$new(), noop); // clone
- expect(e1.text()).toEqual('');
-
- $httpBackend.flush();
-
- e2 = template($rootScope.$new(), noop); // clone
- $rootScope.$digest();
- expect(e1.text()).toEqual('Hello Elvis');
- expect(e2.text()).toEqual('Hello Elvis');
-
- expect($exceptionHandler.errors.length).toEqual(2);
- expect($exceptionHandler.errors[0][0].message).toEqual('cError');
- expect($exceptionHandler.errors[1][0].message).toEqual('lError');
-
- dealoc(e1);
- dealoc(e2);
- });
- });
-
-
- it('should be implicitly terminal and not compile placeholder content in append', inject(
- function($compile, $templateCache, $rootScope, log) {
- // we can't compile the contents because that would result in a memory leak
-
- $templateCache.put('hello.html', 'Hello!');
- element = $compile('
')($rootScope);
-
- expect(log).toEqual('');
- }
- ));
-
-
- it('should be implicitly terminal and not compile placeholder content in inline', inject(
- function($compile, $templateCache, $rootScope, log) {
- // we can't compile the contents because that would result in a memory leak
-
- $templateCache.put('hello.html', 'Hello!');
- element = $compile('
')($rootScope);
-
- expect(log).toEqual('');
- }
- ));
-
-
- it('should throw an error and clear element content if the template fails to load', inject(
- function($compile, $httpBackend, $rootScope) {
- $httpBackend.expect('GET', 'hello.html').respond(404, 'Not Found!');
- element = $compile('
content
')($rootScope);
-
- expect(function() {
- $httpBackend.flush();
- }).toThrow('Failed to load template: hello.html');
- expect(sortedHtml(element)).toBe('
');
- }
- ));
-
-
- it('should prevent multiple templates per element', function() {
- module(function($compileProvider) {
- $compileProvider.directive('sync', valueFn({
- restrict: 'C',
- template: '
'
- }));
- $compileProvider.directive('async', valueFn({
- restrict: 'C',
- templateUrl: 'template.html'
- }));
- });
- inject(function($compile){
- expect(function() {
- $compile('
');
- }).toThrow('Multiple directives [sync, async] asking for template on: <'+
- (msie <= 8 ? 'DIV' : 'div') + ' class="sync async">');
- });
- });
-
-
- describe('delay compile / linking functions until after template is resolved', function(){
- var template;
- beforeEach(module(function($compileProvider) {
- function directive (name, priority, options) {
- $compileProvider.directive(name, function(log) {
- return (extend({
- priority: priority,
- compile: function() {
- log(name + '-C');
- return function() { log(name + '-L'); }
- }
- }, options || {}));
- });
- }
-
- directive('first', 10);
- directive('second', 5, { templateUrl: 'second.html' });
- directive('third', 3);
- directive('last', 0);
-
- directive('iFirst', 10, {replace: true});
- directive('iSecond', 5, {replace: true, templateUrl: 'second.html' });
- directive('iThird', 3, {replace: true});
- directive('iLast', 0, {replace: true});
- }));
-
- it('should flush after link append', inject(
- function($compile, $rootScope, $httpBackend, log) {
- $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
- template = $compile('
');
- element = template($rootScope);
- expect(log).toEqual('first-C');
-
- log('FLUSH');
- $httpBackend.flush();
- $rootScope.$digest();
- expect(log).toEqual(
- 'first-C; FLUSH; second-C; last-C; third-C; ' +
- 'third-L; first-L; second-L; last-L');
-
- var span = element.find('span');
- expect(span.attr('first')).toEqual('');
- expect(span.attr('second')).toEqual('');
- expect(span.find('div').attr('third')).toEqual('');
- expect(span.attr('last')).toEqual('');
-
- expect(span.text()).toEqual('3');
- }));
-
-
- it('should flush after link inline', inject(
- function($compile, $rootScope, $httpBackend, log) {
- $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
- template = $compile('
');
- element = template($rootScope);
- expect(log).toEqual('iFirst-C');
-
- log('FLUSH');
- $httpBackend.flush();
- $rootScope.$digest();
- expect(log).toEqual(
- 'iFirst-C; FLUSH; iSecond-C; iThird-C; iLast-C; ' +
- 'iFirst-L; iSecond-L; iThird-L; iLast-L');
-
- var div = element.find('div');
- expect(div.attr('i-first')).toEqual('');
- expect(div.attr('i-second')).toEqual('');
- expect(div.attr('i-third')).toEqual('');
- expect(div.attr('i-last')).toEqual('');
-
- expect(div.text()).toEqual('3');
- }));
-
-
- it('should flush before link append', inject(
- function($compile, $rootScope, $httpBackend, log) {
- $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
- template = $compile('
');
- expect(log).toEqual('first-C');
- log('FLUSH');
- $httpBackend.flush();
- expect(log).toEqual('first-C; FLUSH; second-C; last-C; third-C');
-
- element = template($rootScope);
- $rootScope.$digest();
- expect(log).toEqual(
- 'first-C; FLUSH; second-C; last-C; third-C; ' +
- 'third-L; first-L; second-L; last-L');
-
- var span = element.find('span');
- expect(span.attr('first')).toEqual('');
- expect(span.attr('second')).toEqual('');
- expect(span.find('div').attr('third')).toEqual('');
- expect(span.attr('last')).toEqual('');
-
- expect(span.text()).toEqual('3');
- }));
-
-
- it('should flush before link inline', inject(
- function($compile, $rootScope, $httpBackend, log) {
- $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
- template = $compile('
');
- expect(log).toEqual('iFirst-C');
- log('FLUSH');
- $httpBackend.flush();
- expect(log).toEqual('iFirst-C; FLUSH; iSecond-C; iThird-C; iLast-C');
-
- element = template($rootScope);
- $rootScope.$digest();
- expect(log).toEqual(
- 'iFirst-C; FLUSH; iSecond-C; iThird-C; iLast-C; ' +
- 'iFirst-L; iSecond-L; iThird-L; iLast-L');
-
- var div = element.find('div');
- expect(div.attr('i-first')).toEqual('');
- expect(div.attr('i-second')).toEqual('');
- expect(div.attr('i-third')).toEqual('');
- expect(div.attr('i-last')).toEqual('');
-
- expect(div.text()).toEqual('3');
- }));
- });
-
-
- it('should check that template has root element', inject(function($compile, $httpBackend) {
- $httpBackend.expect('GET', 'hello.html').respond('before
mid after');
- $compile('
');
- expect(function(){
- $httpBackend.flush();
- }).toThrow('Template must have exactly one root element: before
mid after');
- }));
-
-
- it('should allow multiple elements in template', inject(function($compile, $httpBackend) {
- $httpBackend.expect('GET', 'hello.html').respond('before
mid after');
- element = jqLite('
');
- $compile(element);
- $httpBackend.flush();
- expect(element.text()).toEqual('before mid after');
- }));
-
-
- it('should work when widget is in root element', inject(
- function($compile, $httpBackend, $rootScope) {
- $httpBackend.expect('GET', 'hello.html').respond('
3==<> ');
- element = jqLite('
{{1+2}} ');
- $compile(element)($rootScope);
-
- $httpBackend.flush();
- expect(element.text()).toEqual('3==3');
- }
- ));
-
-
- it('should work when widget is a repeater', inject(
- function($compile, $httpBackend, $rootScope) {
- $httpBackend.expect('GET', 'hello.html').respond('
i=<>; ');
- element = jqLite('
{{i}}
');
- $compile(element)($rootScope);
-
- $httpBackend.flush();
- expect(element.text()).toEqual('i=1;i=2;');
- }
- ));
- });
-
-
- describe('scope', function() {
- var iscope;
-
- beforeEach(module(function($compileProvider) {
- forEach(['', 'a', 'b'], function(name) {
- $compileProvider.directive('scope' + uppercase(name), function(log) {
- return {
- scope: true,
- restrict: 'CA',
- compile: function() {
- return function (scope, element) {
- log(scope.$id);
- expect(element.data('$scope')).toBe(scope);
- };
- }
- };
- });
- $compileProvider.directive('iscope' + uppercase(name), function(log) {
- return {
- scope: {},
- restrict: 'CA',
- compile: function() {
- return function (scope, element) {
- iscope = scope;
- log(scope.$id);
- expect(element.data('$scope')).toBe(scope);
- };
- }
- };
- });
- $compileProvider.directive('tiscope' + uppercase(name), function(log) {
- return {
- scope: {},
- restrict: 'CA',
- templateUrl: 'tiscope.html',
- compile: function() {
- return function (scope, element) {
- iscope = scope;
- log(scope.$id);
- expect(element.data('$scope')).toBe(scope);
- };
- }
- };
- });
- });
- $compileProvider.directive('log', function(log) {
- return {
- restrict: 'CA',
- link: function(scope) {
- log('log-' + scope.$id + '-' + scope.$parent.$id);
- }
- };
- });
- }));
-
-
- it('should allow creation of new scopes', inject(function($rootScope, $compile, log) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('LOG; log-002-001; 002');
- expect(element.find('span').hasClass('ng-scope')).toBe(true);
- }));
-
-
- it('should allow creation of new isolated scopes for directives', inject(
- function($rootScope, $compile, log) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('LOG; log-002-001; 002');
- $rootScope.name = 'abc';
- expect(iscope.$parent).toBe($rootScope);
- expect(iscope.name).toBeUndefined();
- }));
-
-
- it('should allow creation of new isolated scopes for directives with templates', inject(
- function($rootScope, $compile, log, $httpBackend) {
- $httpBackend.expect('GET', 'tiscope.html').respond('
');
- element = $compile('
')($rootScope);
- $httpBackend.flush();
- expect(log).toEqual('LOG; log-002-001; 002');
- $rootScope.name = 'abc';
- expect(iscope.$parent).toBe($rootScope);
- expect(iscope.name).toBeUndefined();
- }));
-
-
- it('should correctly create the scope hierachy', inject(
- function($rootScope, $compile, log) {
- element = $compile(
- '
' + //1
- '' + //2
- ' ' + //3
- ' ' +
- ' ' +
- '' + //4
- ' ' +
- ' ' +
- '
'
- )($rootScope);
- expect(log).toEqual('LOG; log-003-002; 003; LOG; log-002-001; 002; LOG; log-004-001; 004');
- })
- );
-
-
- it('should allow more one new scope directives per element, but directives should share' +
- 'the scope', inject(
- function($rootScope, $compile, log) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('002; 002');
- })
- );
-
- it('should not allow more then one isolate scope creation per element', inject(
- function($rootScope, $compile) {
- expect(function(){
- $compile('
');
- }).toThrow('Multiple directives [iscopeA, scopeB] asking for isolated scope on: ' +
- '<' + (msie < 9 ? 'DIV' : 'div') +
- ' class="iscope-a; scope-b ng-isolate-scope ng-scope">');
- })
- );
-
-
- it('should not allow more then one isolate scope creation per element', inject(
- function($rootScope, $compile) {
- expect(function(){
- $compile('
');
- }).toThrow('Multiple directives [iscopeA, iscopeB] asking for isolated scope on: ' +
- '<' + (msie < 9 ? 'DIV' : 'div') +
- ' class="iscope-a; iscope-b ng-isolate-scope ng-scope">');
- })
- );
-
-
- it('should create new scope even at the root of the template', inject(
- function($rootScope, $compile, log) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('002');
- })
- );
-
-
- it('should create isolate scope even at the root of the template', inject(
- function($rootScope, $compile, log) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('002');
- })
- );
- });
- });
- });
-
-
- describe('interpolation', function() {
- var observeSpy, attrValueDuringLinking;
-
- beforeEach(module(function($compileProvider) {
- $compileProvider.directive('observer', function() {
- return function(scope, elm, attr) {
- observeSpy = jasmine.createSpy('$observe attr');
-
- attr.$observe('someAttr', observeSpy);
- attrValueDuringLinking = attr.someAttr;
- };
- });
- }));
-
-
- it('should compile and link both attribute and text bindings', inject(
- function($rootScope, $compile) {
- $rootScope.name = 'angular';
- element = $compile('
text: {{name}}
')($rootScope);
- $rootScope.$digest();
- expect(element.text()).toEqual('text: angular');
- expect(element.attr('name')).toEqual('attr: angular');
- }));
-
-
- it('should decorate the binding with ng-binding and interpolation function', inject(
- function($compile, $rootScope) {
- element = $compile('
{{1+2}}
')($rootScope);
- expect(element.hasClass('ng-binding')).toBe(true);
- expect(element.data('$binding')[0].exp).toEqual('{{1+2}}');
- }));
-
-
- it('should observe interpolated attrs', inject(function($rootScope, $compile) {
- $compile('
')($rootScope);
-
- // should be async
- expect(observeSpy).not.toHaveBeenCalled();
-
- $rootScope.$apply(function() {
- $rootScope.value = 'bound-value';
- });
- expect(observeSpy).toHaveBeenCalledOnceWith('bound-value');
- }));
-
-
- it('should set interpolated attrs to undefined', inject(function($rootScope, $compile) {
- attrValueDuringLinking = null;
- $compile('
')($rootScope);
- expect(attrValueDuringLinking).toBeUndefined();
- }));
-
-
- it('should not call observer of non-interpolated attr', inject(function($rootScope, $compile) {
- $compile('
')($rootScope);
- expect(attrValueDuringLinking).toBe('nonBound');
-
- $rootScope.$digest();
- expect(observeSpy).not.toHaveBeenCalled();
- }));
-
-
- it('should delegate exceptions to $exceptionHandler', function() {
- observeSpy = jasmine.createSpy('$observe attr').andThrow('ERROR');
-
- module(function($compileProvider, $exceptionHandlerProvider) {
- $exceptionHandlerProvider.mode('log');
- $compileProvider.directive('error', function() {
- return function(scope, elm, attr) {
- attr.$observe('someAttr', observeSpy);
- attr.$observe('someAttr', observeSpy);
- };
- });
- });
-
- inject(function($compile, $rootScope, $exceptionHandler) {
- $compile('
')($rootScope);
- $rootScope.$digest();
-
- expect(observeSpy).toHaveBeenCalled();
- expect(observeSpy.callCount).toBe(2);
- expect($exceptionHandler.errors).toEqual(['ERROR', 'ERROR']);
- });
- });
-
-
- it('should translate {{}} in terminal nodes', inject(function($rootScope, $compile) {
- element = $compile('
Greet {{name}}! ')($rootScope)
- $rootScope.$digest();
- expect(sortedHtml(element).replace(' selected="true"', '')).
- toEqual('
' +
- 'Greet ! ' +
- ' ');
- $rootScope.name = 'Misko';
- $rootScope.$digest();
- expect(sortedHtml(element).replace(' selected="true"', '')).
- toEqual('
' +
- 'Greet Misko! ' +
- ' ');
- }));
- });
-
-
- describe('link phase', function() {
-
- beforeEach(module(function($compileProvider) {
-
- forEach(['a', 'b', 'c'], function(name) {
- $compileProvider.directive(name, function(log) {
- return {
- restrict: 'ECA',
- compile: function() {
- log('t' + uppercase(name))
- return {
- pre: function() {
- log('pre' + uppercase(name));
- },
- post: function linkFn() {
- log('post' + uppercase(name));
- }
- };
- }
- };
- });
- });
- }));
-
-
- it('should not store linkingFns for noop branches', inject(function ($rootScope, $compile) {
- element = jqLite('
ignore
');
- var linkingFn = $compile(element);
- // Now prune the branches with no directives
- element.find('span').remove();
- expect(element.find('span').length).toBe(0);
- // and we should still be able to compile without errors
- linkingFn($rootScope);
- }));
-
-
- it('should compile from top to bottom but link from bottom up', inject(
- function($compile, $rootScope, log) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('tA; tB; tC; preA; preB; preC; postC; postA; postB');
- }
- ));
-
-
- it('should support link function on directive object', function() {
- module(function($compileProvider) {
- $compileProvider.directive('abc', valueFn({
- link: function(scope, element, attrs) {
- element.text(attrs.abc);
- }
- }));
- });
- inject(function($compile, $rootScope) {
- element = $compile('
FAIL
')($rootScope);
- expect(element.text()).toEqual('WORKS');
- });
- });
- });
-
-
- describe('attrs', function() {
-
- it('should allow setting of attributes', function() {
- module(function($compileProvider) {
- $compileProvider.directive({
- setter: valueFn(function(scope, element, attr) {
- attr.$set('name', 'abc');
- attr.$set('disabled', true);
- expect(attr.name).toBe('abc');
- expect(attr.disabled).toBe(true);
- })
- });
- });
- inject(function($rootScope, $compile) {
- element = $compile('
')($rootScope);
- expect(element.attr('name')).toEqual('abc');
- expect(element.attr('disabled')).toEqual('disabled');
- });
- });
-
-
- it('should read boolean attributes as boolean only on control elements', function() {
- var value;
- module(function($compileProvider) {
- $compileProvider.directive({
- input: valueFn({
- restrict: 'ECA',
- link:function(scope, element, attr) {
- value = attr.required;
- }
- })
- });
- });
- inject(function($rootScope, $compile) {
- element = $compile('
')($rootScope);
- expect(value).toEqual(true);
- });
- });
-
- it('should read boolean attributes as text on non-controll elements', function() {
- var value;
- module(function($compileProvider) {
- $compileProvider.directive({
- div: valueFn({
- restrict: 'ECA',
- link:function(scope, element, attr) {
- value = attr.required;
- }
- })
- });
- });
- inject(function($rootScope, $compile) {
- element = $compile('
')($rootScope);
- expect(value).toEqual('some text');
- });
- });
-
- it('should allow setting of attributes', function() {
- module(function($compileProvider) {
- $compileProvider.directive({
- setter: valueFn(function(scope, element, attr) {
- attr.$set('name', 'abc');
- attr.$set('disabled', true);
- expect(attr.name).toBe('abc');
- expect(attr.disabled).toBe(true);
- })
- });
- });
- inject(function($rootScope, $compile) {
- element = $compile('
')($rootScope);
- expect(element.attr('name')).toEqual('abc');
- expect(element.attr('disabled')).toEqual('disabled');
- });
- });
-
-
- it('should create new instance of attr for each template stamping', function() {
- module(function($compileProvider, $provide) {
- var state = { first: [], second: [] };
- $provide.value('state', state);
- $compileProvider.directive({
- first: valueFn({
- priority: 1,
- compile: function(templateElement, templateAttr) {
- return function(scope, element, attr) {
- state.first.push({
- template: {element: templateElement, attr:templateAttr},
- link: {element: element, attr: attr}
- });
- }
- }
- }),
- second: valueFn({
- priority: 2,
- compile: function(templateElement, templateAttr) {
- return function(scope, element, attr) {
- state.second.push({
- template: {element: templateElement, attr:templateAttr},
- link: {element: element, attr: attr}
- });
- }
- }
- })
- });
- });
- inject(function($rootScope, $compile, state) {
- var template = $compile('
');
- dealoc(template($rootScope.$new(), noop));
- dealoc(template($rootScope.$new(), noop));
-
- // instance between directives should be shared
- expect(state.first[0].template.element).toBe(state.second[0].template.element);
- expect(state.first[0].template.attr).toBe(state.second[0].template.attr);
-
- // the template and the link can not be the same instance
- expect(state.first[0].template.element).not.toBe(state.first[0].link.element);
- expect(state.first[0].template.attr).not.toBe(state.first[0].link.attr);
-
- // each new template needs to be new instance
- expect(state.first[0].link.element).not.toBe(state.first[1].link.element);
- expect(state.first[0].link.attr).not.toBe(state.first[1].link.attr);
- expect(state.second[0].link.element).not.toBe(state.second[1].link.element);
- expect(state.second[0].link.attr).not.toBe(state.second[1].link.attr);
- });
- });
-
-
- it('should properly $observe inside ng-repeat', function() {
- var spies = [];
-
- module(function($compileProvider) {
- $compileProvider.directive('observer', function() {
- return function(scope, elm, attr) {
- spies.push(jasmine.createSpy('observer ' + spies.length));
- attr.$observe('some', spies[spies.length - 1]);
- };
- });
- });
-
- inject(function($compile, $rootScope) {
- element = $compile('
')($rootScope);
-
- $rootScope.$apply(function() {
- $rootScope.items = [{id: 1}, {id: 2}];
- });
-
- expect(spies[0]).toHaveBeenCalledOnceWith('id_1');
- expect(spies[1]).toHaveBeenCalledOnceWith('id_2');
- spies[0].reset();
- spies[1].reset();
-
- $rootScope.$apply(function() {
- $rootScope.items[0].id = 5;
- });
-
- expect(spies[0]).toHaveBeenCalledOnceWith('id_5');
- });
- });
-
-
- describe('$set', function() {
- var attr;
- beforeEach(function(){
- module(function($compileProvider) {
- $compileProvider.directive('input', valueFn({
- restrict: 'ECA',
- link: function(scope, element, attr) {
- scope.attr = attr;
- }
- }));
- });
- inject(function($compile, $rootScope) {
- element = $compile('
')($rootScope);
- attr = $rootScope.attr;
- expect(attr).toBeDefined();
- });
- });
-
-
- it('should set attributes', function() {
- attr.$set('ngMyAttr', 'value');
- expect(element.attr('ng-my-attr')).toEqual('value');
- expect(attr.ngMyAttr).toEqual('value');
- });
-
-
- it('should allow overriding of attribute name and remember the name', function() {
- attr.$set('ngOther', '123', true, 'other');
- expect(element.attr('other')).toEqual('123');
- expect(attr.ngOther).toEqual('123');
-
- attr.$set('ngOther', '246');
- expect(element.attr('other')).toEqual('246');
- expect(attr.ngOther).toEqual('246');
- });
-
-
- it('should remove attribute', function() {
- attr.$set('ngMyAttr', 'value');
- expect(element.attr('ng-my-attr')).toEqual('value');
-
- attr.$set('ngMyAttr', undefined);
- expect(element.attr('ng-my-attr')).toBe(undefined);
-
- attr.$set('ngMyAttr', 'value');
- attr.$set('ngMyAttr', null);
- expect(element.attr('ng-my-attr')).toBe(undefined);
- });
-
-
- it('should not set DOM element attr if writeAttr false', function() {
- attr.$set('test', 'value', false);
-
- expect(element.attr('test')).toBeUndefined();
- expect(attr.test).toBe('value');
- });
- });
- });
-
-
- describe('locals', function() {
- it('should marshal to locals', function() {
- module(function($compileProvider) {
- $compileProvider.directive('widget', function(log) {
- return {
- scope: {
- attr: 'attribute',
- prop: 'evaluate',
- bind: 'bind',
- assign: 'accessor',
- read: 'accessor',
- exp: 'expression',
- nonExist: 'accessor',
- nonExistExpr: 'expression'
- },
- link: function(scope, element, attrs) {
- scope.nonExist(); // noop
- scope.nonExist(123); // noop
- scope.nonExistExpr(); // noop
- scope.nonExistExpr(123); // noop
- log(scope.attr);
- log(scope.prop);
- log(scope.assign());
- log(scope.read());
- log(scope.assign('ng'));
- scope.exp({myState:'OK'});
- expect(function() { scope.read(undefined); }).
- toThrow("Expression ''D'' not assignable.");
- scope.$watch('bind', log);
- }
- };
- });
- });
- inject(function(log, $compile, $rootScope) {
- $rootScope.myProp = 'B';
- $rootScope.bi = {nd: 'C'};
- $rootScope.name = 'C';
- element = $compile(
- '
')
- ($rootScope);
- expect(log).toEqual('A; B; C; D; ng');
- expect($rootScope.name).toEqual('ng');
- expect($rootScope.state).toEqual('OK');
- log.reset();
- $rootScope.$apply();
- expect(element.text()).toEqual('C');
- expect(log).toEqual('C');
- $rootScope.bi.nd = 'c';
- $rootScope.$apply();
- expect(log).toEqual('C; c');
- });
- });
- });
-
-
- describe('controller', function() {
- it('should inject locals to controller', function() {
- module(function($compileProvider) {
- $compileProvider.directive('widget', function(log) {
- return {
- controller: function(attr, prop, assign, read, exp){
- log(attr);
- log(prop);
- log(assign());
- log(read());
- log(assign('ng'));
- exp();
- expect(function() { read(undefined); }).
- toThrow("Expression ''D'' not assignable.");
- this.result = 'OK';
- },
- inject: {
- attr: 'attribute',
- prop: 'evaluate',
- assign: 'accessor',
- read: 'accessor',
- exp: 'expression'
- },
- link: function(scope, element, attrs, controller) {
- log(controller.result);
- }
- };
- });
- });
- inject(function(log, $compile, $rootScope) {
- $rootScope.myProp = 'B';
- $rootScope.bi = {nd: 'C'};
- $rootScope.name = 'C';
- element = $compile(
- '
')
- ($rootScope);
- expect(log).toEqual('A; B; C; D; ng; OK');
- expect($rootScope.name).toEqual('ng');
- });
- });
-
-
- it('should get required controller', function() {
- module(function($compileProvider) {
- $compileProvider.directive('main', function(log) {
- return {
- priority: 2,
- controller: function() {
- this.name = 'main';
- },
- link: function(scope, element, attrs, controller) {
- log(controller.name);
- }
- };
- });
- $compileProvider.directive('dep', function(log) {
- return {
- priority: 1,
- require: 'main',
- link: function(scope, element, attrs, controller) {
- log('dep:' + controller.name);
- }
- };
- });
- $compileProvider.directive('other', function(log) {
- return {
- link: function(scope, element, attrs, controller) {
- log(!!controller); // should be false
- }
- };
- });
- });
- inject(function(log, $compile, $rootScope) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('main; dep:main; false');
- });
- });
-
-
- it('should require controller on parent element',function() {
- module(function($compileProvider) {
- $compileProvider.directive('main', function(log) {
- return {
- controller: function() {
- this.name = 'main';
- }
- };
- });
- $compileProvider.directive('dep', function(log) {
- return {
- require: '^main',
- link: function(scope, element, attrs, controller) {
- log('dep:' + controller.name);
- }
- };
- });
- });
- inject(function(log, $compile, $rootScope) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('dep:main');
- });
- });
-
-
- it('should have optional controller on current element', function() {
- module(function($compileProvider) {
- $compileProvider.directive('dep', function(log) {
- return {
- require: '?main',
- link: function(scope, element, attrs, controller) {
- log('dep:' + !!controller);
- }
- };
- });
- });
- inject(function(log, $compile, $rootScope) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('dep:false');
- });
- });
-
-
- it('should support multiple controllers', function() {
- module(function($compileProvider) {
- $compileProvider.directive('c1', valueFn({
- controller: function() { this.name = 'c1'; }
- }));
- $compileProvider.directive('c2', valueFn({
- controller: function() { this.name = 'c2'; }
- }));
- $compileProvider.directive('dep', function(log) {
- return {
- require: ['^c1', '^c2'],
- link: function(scope, element, attrs, controller) {
- log('dep:' + controller[0].name + '-' + controller[1].name);
- }
- };
- });
- });
- inject(function(log, $compile, $rootScope) {
- element = $compile('
')($rootScope);
- expect(log).toEqual('dep:c1-c2');
- });
-
- });
- });
-
-
- describe('transclude', function() {
- it('should compile get templateFn', function() {
- module(function($compileProvider) {
- $compileProvider.directive('trans', function(log) {
- return {
- transclude: 'element',
- priority: 2,
- controller: function($transclude) { this.$transclude = $transclude; },
- compile: function(element, attrs, template) {
- log('compile: ' + angular.mock.dump(element));
- return function(scope, element, attrs, ctrl) {
- log('link');
- var cursor = element;
- template(scope.$new(), function(clone) {cursor.after(cursor = clone)});
- ctrl.$transclude(function(clone) {cursor.after(clone)});
- };
- }
- }
- });
- });
- inject(function(log, $rootScope, $compile) {
- element = $compile('
')
- ($rootScope);
- $rootScope.$apply();
- expect(log).toEqual('compile: ; HIGH; link; LOG; LOG');
- expect(element.text()).toEqual('001-002;001-003;');
- });
- });
-
-
- it('should support transclude directive', function() {
- module(function($compileProvider) {
- $compileProvider.directive('trans', function() {
- return {
- transclude: 'content',
- replace: true,
- scope: true,
- template: '
W:{{$parent.$id}}-{{$id}}; '
- }
- });
- });
- inject(function(log, $rootScope, $compile) {
- element = $compile('
T:{{$parent.$id}}-{{$id}};
')
- ($rootScope);
- $rootScope.$apply();
- expect(element.text()).toEqual('W:001-002;T:001-003;');
- expect(jqLite(element.find('span')[0]).text()).toEqual('T:001-003');
- expect(jqLite(element.find('span')[1]).text()).toEqual(';');
- });
- });
-
-
- it('should transclude transcluded content', function() {
- module(function($compileProvider) {
- $compileProvider.directive('book', valueFn({
- transclude: 'content',
- template: '
'
- }));
- $compileProvider.directive('chapter', valueFn({
- transclude: 'content',
- templateUrl: 'chapter.html'
- }));
- $compileProvider.directive('section', valueFn({
- transclude: 'content',
- template: '
'
- }));
- return function($httpBackend) {
- $httpBackend.
- expect('GET', 'chapter.html').
- respond('
');
- }
- });
- inject(function(log, $rootScope, $compile, $httpBackend) {
- element = $compile('
')($rootScope);
- $rootScope.$apply();
-
- expect(element.text()).toEqual('book-');
-
- $httpBackend.flush();
- $rootScope.$apply();
- expect(element.text()).toEqual('book-chapter-section-![(paragraph)]!');
- });
- });
-
-
- it('should only allow one transclude per element', function() {
- module(function($compileProvider) {
- $compileProvider.directive('first', valueFn({
- scope: {},
- restrict: 'CA',
- transclude: 'content'
- }));
- $compileProvider.directive('second', valueFn({
- restrict: 'CA',
- transclude: 'content'
- }));
- });
- inject(function($compile) {
- expect(function() {
- $compile('
');
- }).toThrow('Multiple directives [first, second] asking for transclusion on: <' +
- (msie <= 8 ? 'DIV' : 'div') + ' class="first second ng-isolate-scope ng-scope">');
- });
- });
-
-
- it('should remove transclusion scope, when the DOM is destroyed', function() {
- module(function($compileProvider) {
- $compileProvider.directive('box', valueFn({
- transclude: 'content',
- scope: { name: 'evaluate', show: 'accessor' },
- template: '
',
- link: function(scope, element) {
- scope.$watch(
- function() { return scope.show(); },
- function(show) {
- if (!show) {
- element.find('div').find('div').remove();
- }
- }
- );
- }
- }));
- });
- inject(function($compile, $rootScope) {
- $rootScope.username = 'Misko';
- $rootScope.select = true;
- element = $compile(
- '
')
- ($rootScope);
- $rootScope.$apply();
- expect(element.text()).toEqual('Hello: Misko!user: Misko');
-
- var widgetScope = $rootScope.$$childHead;
- var transcludeScope = widgetScope.$$nextSibling;
- expect(widgetScope.name).toEqual('Misko');
- expect(widgetScope.$parent).toEqual($rootScope);
- expect(transcludeScope.$parent).toEqual($rootScope);
-
- $rootScope.select = false;
- $rootScope.$apply();
- expect(element.text()).toEqual('Hello: Misko!');
- expect(widgetScope.$$nextSibling).toEqual(null);
- });
- });
-
-
- it('should support transcluded element on root content', function() {
- var comment;
- module(function($compileProvider) {
- $compileProvider.directive('transclude', valueFn({
- transclude: 'element',
- compile: function(element, attr, linker) {
- return function(scope, element, attr) {
- comment = element;
- };
- }
- }));
- });
- inject(function($compile, $rootScope) {
- var element = jqLite('
').contents();
- expect(element.length).toEqual(3);
- expect(nodeName_(element[1])).toBe('DIV');
- $compile(element)($rootScope);
- expect(nodeName_(element[1])).toBe('#comment');
- expect(nodeName_(comment)).toBe('#comment');
- });
- });
- });
-});
--
cgit v1.2.3