From beea3a4beda0aaed5fc54af1a992b1c161db7752 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Wed, 2 May 2012 14:32:27 -0700 Subject: style($compile): rename compiler.js to compile.js --- test/ng/compileSpec.js | 1869 +++++++++++++++++++++++++++++++++++++++++++++++ test/ng/compilerSpec.js | 1869 ----------------------------------------------- 2 files changed, 1869 insertions(+), 1869 deletions(-) create mode 100644 test/ng/compileSpec.js delete mode 100644 test/ng/compilerSpec.js (limited to 'test') diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js new file mode 100644 index 00000000..1aef24fe --- /dev/null +++ b/test/ng/compileSpec.js @@ -0,0 +1,1869 @@ +'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('ABC'); + })); + + 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('
01234
')($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("
A
"); + $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('
A
'); + $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('
OK
')($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( + '
' + + '
{{i}};
' + + '
')($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( + '
' + + '
{{i}};
' + + '
')($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('
ignoreignore
')($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('
ignoreignore
')($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('')($rootScope) + $rootScope.$digest(); + expect(sortedHtml(element).replace(' selected="true"', '')). + toEqual(''); + $rootScope.name = 'Misko'; + $rootScope.$digest(); + expect(sortedHtml(element).replace(' selected="true"', '')). + toEqual(''); + })); + }); + + + 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( + '
{{bind}}
') + ($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( + '
{{bind}}
') + ($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('
{{$parent.$id}}-{{$id}};
') + ($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: '
book-
(
)
' + })); + $compileProvider.directive('chapter', valueFn({ + transclude: 'content', + templateUrl: 'chapter.html' + })); + $compileProvider.directive('section', valueFn({ + transclude: 'content', + template: '
section-!
!
' + })); + return function($httpBackend) { + $httpBackend. + expect('GET', 'chapter.html'). + respond('
chapter-
[
]
'); + } + }); + inject(function(log, $rootScope, $compile, $httpBackend) { + element = $compile('
paragraph
')($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: '

Hello: {{name}}!

', + 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( + '
user: {{username}}
') + ($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('
before
after
').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('ABC'); - })); - - 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('
01234
')($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("
A
"); - $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('
A
'); - $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( - '
' + - '
{{i}};
' + - '
')($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( - '
' + - '
{{i}};
' + - '
')($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('
ignoreignore
')($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('
ignoreignore
')($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('')($rootScope) - $rootScope.$digest(); - expect(sortedHtml(element).replace(' selected="true"', '')). - toEqual(''); - $rootScope.name = 'Misko'; - $rootScope.$digest(); - expect(sortedHtml(element).replace(' selected="true"', '')). - toEqual(''); - })); - }); - - - 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( - '
{{bind}}
') - ($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( - '
{{bind}}
') - ($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('
{{$parent.$id}}-{{$id}};
') - ($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: '
book-
(
)
' - })); - $compileProvider.directive('chapter', valueFn({ - transclude: 'content', - templateUrl: 'chapter.html' - })); - $compileProvider.directive('section', valueFn({ - transclude: 'content', - template: '
section-!
!
' - })); - return function($httpBackend) { - $httpBackend. - expect('GET', 'chapter.html'). - respond('
chapter-
[
]
'); - } - }); - inject(function(log, $rootScope, $compile, $httpBackend) { - element = $compile('
paragraph
')($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: '

Hello: {{name}}!

', - 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( - '
user: {{username}}
') - ($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('
before
after
').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