'use strict'; describe('ngInclude', function() { var element; afterEach(function(){ dealoc(element); }); function putIntoCache(url, content) { return function($templateCache) { $templateCache.put(url, [200, content, {}]); }; } it('should trust and use literal urls', inject(function( $rootScope, $httpBackend, $compile) { element = $compile('
')($rootScope); $httpBackend.expect('GET', 'url').respond('template text'); $rootScope.$digest(); $httpBackend.flush(); expect(element.text()).toEqual('template text'); dealoc($rootScope); })); it('should trust and use trusted urls', inject(function($rootScope, $httpBackend, $compile, $sce) { element = $compile('
')($rootScope); $httpBackend.expect('GET', 'http://foo.bar/url').respond('template text'); $rootScope.fooUrl = $sce.trustAsResourceUrl('http://foo.bar/url'); $rootScope.$digest(); $httpBackend.flush(); expect(element.text()).toEqual('template text'); dealoc($rootScope); })); it('should include an external file', inject(putIntoCache('myUrl', '{{name}}'), function($rootScope, $compile) { element = jqLite('
'); var body = jqLite(document.body); body.append(element); element = $compile(element)($rootScope); $rootScope.name = 'misko'; $rootScope.url = 'myUrl'; $rootScope.$digest(); expect(body.text()).toEqual('misko'); body.empty(); })); it('should support ng-include="src" syntax', inject(putIntoCache('myUrl', '{{name}}'), function($rootScope, $compile) { element = jqLite('
'); jqLite(document.body).append(element); element = $compile(element)($rootScope); $rootScope.name = 'Alibaba'; $rootScope.url = 'myUrl'; $rootScope.$digest(); expect(element.text()).toEqual('Alibaba'); jqLite(document.body).empty(); })); it('should NOT use untrusted URL expressions ', inject(putIntoCache('myUrl', '{{name}} text'), function($rootScope, $compile, $sce) { element = jqLite(''); jqLite(document.body).append(element); element = $compile(element)($rootScope); $rootScope.name = 'chirayu'; $rootScope.url = 'http://example.com/myUrl'; expect(function() { $rootScope.$digest(); }).toThrowMinErr( '$sce', 'insecurl', /Blocked loading resource from url not allowed by \$sceDelegate policy. URL: http:\/\/example.com\/myUrl.*/); jqLite(document.body).empty(); })); it('should NOT use mistyped expressions ', inject(putIntoCache('myUrl', '{{name}} text'), function($rootScope, $compile, $sce) { element = jqLite(''); jqLite(document.body).append(element); element = $compile(element)($rootScope); $rootScope.name = 'chirayu'; $rootScope.url = $sce.trustAsUrl('http://example.com/myUrl'); expect(function() { $rootScope.$digest(); }).toThrowMinErr( '$sce', 'insecurl', /Blocked loading resource from url not allowed by \$sceDelegate policy. URL: http:\/\/example.com\/myUrl.*/); jqLite(document.body).empty(); })); it('should remove previously included text if a falsy value is bound to src', inject( putIntoCache('myUrl', '{{name}}'), function($rootScope, $compile) { element = jqLite('
'); element = $compile(element)($rootScope); $rootScope.name = 'igor'; $rootScope.url = 'myUrl'; $rootScope.$digest(); expect(element.text()).toEqual('igor'); $rootScope.url = undefined; $rootScope.$digest(); expect(element.text()).toEqual(''); })); it('should fire $includeContentRequested event on scope after making the xhr call', inject( function ($rootScope, $compile, $httpBackend) { var contentRequestedSpy = jasmine.createSpy('content requested').andCallFake(function (event) { expect(event.targetScope).toBe($rootScope); }); $httpBackend.whenGET('url').respond('my partial'); $rootScope.$on('$includeContentRequested', contentRequestedSpy); element = $compile('
')($rootScope); $rootScope.$digest(); expect(contentRequestedSpy).toHaveBeenCalledOnce(); $httpBackend.flush(); })); it('should fire $includeContentLoaded event on child scope after linking the content', inject( function($rootScope, $compile, $templateCache) { var contentLoadedSpy = jasmine.createSpy('content loaded').andCallFake(function(event) { expect(event.targetScope.$parent).toBe($rootScope); expect(element.text()).toBe('partial content'); }); $templateCache.put('url', [200, 'partial content', {}]); $rootScope.$on('$includeContentLoaded', contentLoadedSpy); element = $compile('
')($rootScope); $rootScope.$digest(); expect(contentLoadedSpy).toHaveBeenCalledOnce(); })); it('should evaluate onload expression when a partial is loaded', inject( putIntoCache('myUrl', 'my partial'), function($rootScope, $compile) { element = jqLite('
'); element = $compile(element)($rootScope); expect($rootScope.loaded).not.toBeDefined(); $rootScope.url = 'myUrl'; $rootScope.$digest(); expect(element.text()).toEqual('my partial'); expect($rootScope.loaded).toBe(true); })); it('should create child scope and destroy old one', inject( function($rootScope, $compile, $httpBackend) { $httpBackend.whenGET('url1').respond('partial {{$parent.url}}'); $httpBackend.whenGET('url2').respond(404); element = $compile('
')($rootScope); expect(element.children().scope()).toBeFalsy(); $rootScope.url = 'url1'; $rootScope.$digest(); $httpBackend.flush(); expect(element.children().scope().$parent).toBe($rootScope); expect(element.text()).toBe('partial url1'); $rootScope.url = 'url2'; $rootScope.$digest(); $httpBackend.flush(); expect($rootScope.$$childHead).toBeFalsy(); expect(element.text()).toBe(''); $rootScope.url = 'url1'; $rootScope.$digest(); expect(element.children().scope().$parent).toBe($rootScope); $rootScope.url = null; $rootScope.$digest(); expect($rootScope.$$childHead).toBeFalsy(); })); it('should do xhr request and cache it', inject(function($rootScope, $httpBackend, $compile) { element = $compile('
')($rootScope); $httpBackend.expect('GET', 'myUrl').respond('my partial'); $rootScope.url = 'myUrl'; $rootScope.$digest(); $httpBackend.flush(); expect(element.text()).toEqual('my partial'); $rootScope.url = null; $rootScope.$digest(); expect(element.text()).toEqual(''); $rootScope.url = 'myUrl'; $rootScope.$digest(); expect(element.text()).toEqual('my partial'); dealoc($rootScope); })); it('should clear content when error during xhr request', inject(function($httpBackend, $compile, $rootScope) { element = $compile('
content
')($rootScope); $httpBackend.expect('GET', 'myUrl').respond(404, ''); $rootScope.url = 'myUrl'; $rootScope.$digest(); $httpBackend.flush(); expect(element.text()).toBe(''); })); it('should be async even if served from cache', inject( putIntoCache('myUrl', 'my partial'), function($rootScope, $compile) { element = $compile('
')($rootScope); $rootScope.url = 'myUrl'; var called = 0; // we want to assert only during first watch $rootScope.$watch(function() { if (!called++) expect(element.text()).toBe(''); }); $rootScope.$digest(); expect(element.text()).toBe('my partial'); })); it('should discard pending xhr callbacks if a new template is requested before the current ' + 'finished loading', inject(function($rootScope, $compile, $httpBackend) { element = jqLite("
"); var log = {}; $rootScope.templateUrl = 'myUrl1'; $rootScope.logger = function(msg) { log[msg] = true; } $compile(element)($rootScope); expect(log).toEqual({}); $httpBackend.expect('GET', 'myUrl1').respond('
{{logger("url1")}}
'); $rootScope.$digest(); expect(log).toEqual({}); $rootScope.templateUrl = 'myUrl2'; $httpBackend.expect('GET', 'myUrl2').respond('
{{logger("url2")}}
'); $httpBackend.flush(); // now that we have two requests pending, flush! expect(log).toEqual({ url2 : true }); })); it('should compile only the content', inject(function($compile, $rootScope, $templateCache) { // regression var onload = jasmine.createSpy('$includeContentLoaded'); $rootScope.$on('$includeContentLoaded', onload); $templateCache.put('tpl.html', [200, 'partial {{tpl}}', {}]); element = $compile('
' + '
')($rootScope); expect(onload).not.toHaveBeenCalled(); $rootScope.$apply(function() { $rootScope.tpl = 'tpl.html'; }); expect(onload).toHaveBeenCalledOnce(); $rootScope.tpl = ''; $rootScope.$digest(); dealoc(element); })); it('should not break attribute bindings on the same element', inject(function($compile, $rootScope, $httpBackend) { // regression #3793 element = $compile('
')($rootScope); $httpBackend.expect('GET', 'url1').respond('template text 1'); $rootScope.hrefUrl = 'fooUrl1'; $rootScope.includeUrl = 'url1'; $rootScope.$digest(); $httpBackend.flush(); expect(element.text()).toBe('template text 1'); expect(element.find('span').attr('foo')).toBe('#/fooUrl1'); $httpBackend.expect('GET', 'url2').respond('template text 2'); $rootScope.includeUrl = 'url2'; $rootScope.$digest(); $httpBackend.flush(); expect(element.text()).toBe('template text 2'); expect(element.find('span').attr('foo')).toBe('#/fooUrl1'); $rootScope.hrefUrl = 'fooUrl2'; $rootScope.$digest(); expect(element.text()).toBe('template text 2'); expect(element.find('span').attr('foo')).toBe('#/fooUrl2'); })); it('should exec scripts when jQuery is included', inject(function($compile, $rootScope, $httpBackend) { if (!jQuery) { return; } element = $compile('
')($rootScope); // the element needs to be appended for the script to run element.appendTo(document.body); window._ngIncludeCausesScriptToRun = false; $httpBackend.expect('GET', 'url1').respond(''); $rootScope.includeUrl = 'url1'; $rootScope.$digest(); $httpBackend.flush(); expect(window._ngIncludeCausesScriptToRun).toBe(true); // IE8 doesn't like deleting properties of window window._ngIncludeCausesScriptToRun = undefined; try { delete window._ngIncludeCausesScriptToRun; } catch (e) {} })); describe('autoscroll', function() { var autoScrollSpy; function spyOnAnchorScroll() { return function($provide) { autoScrollSpy = jasmine.createSpy('$anchorScroll'); $provide.value('$anchorScroll', autoScrollSpy); }; } function compileAndLink(tpl) { return function($compile, $rootScope) { element = $compile(tpl)($rootScope); }; } beforeEach(module(spyOnAnchorScroll(), 'ngAnimateMock')); beforeEach(inject( putIntoCache('template.html', 'CONTENT'), putIntoCache('another.html', 'CONTENT'))); it('should call $anchorScroll if autoscroll attribute is present', inject( compileAndLink('
'), function($rootScope, $animate, $timeout) { $rootScope.$apply(function () { $rootScope.tpl = 'template.html'; }); expect(autoScrollSpy).not.toHaveBeenCalled(); expect($animate.queue.shift().event).toBe('enter'); $animate.triggerCallbacks(); expect(autoScrollSpy).toHaveBeenCalledOnce(); })); it('should call $anchorScroll if autoscroll evaluates to true', inject(function($rootScope, $compile, $animate, $timeout) { element = $compile('
')($rootScope); $rootScope.$apply(function () { $rootScope.tpl = 'template.html'; $rootScope.value = true; }); expect($animate.queue.shift().event).toBe('enter'); $animate.triggerCallbacks(); $rootScope.$apply(function () { $rootScope.tpl = 'another.html'; $rootScope.value = 'some-string'; }); expect($animate.queue.shift().event).toBe('leave'); expect($animate.queue.shift().event).toBe('enter'); $animate.triggerCallbacks(); $rootScope.$apply(function() { $rootScope.tpl = 'template.html'; $rootScope.value = 100; }); expect($animate.queue.shift().event).toBe('leave'); expect($animate.queue.shift().event).toBe('enter'); $animate.triggerCallbacks(); expect(autoScrollSpy).toHaveBeenCalled(); expect(autoScrollSpy.callCount).toBe(3); })); it('should not call $anchorScroll if autoscroll attribute is not present', inject( compileAndLink('
'), function($rootScope, $animate, $timeout) { $rootScope.$apply(function () { $rootScope.tpl = 'template.html'; }); expect($animate.queue.shift().event).toBe('enter'); $animate.triggerCallbacks(); expect(autoScrollSpy).not.toHaveBeenCalled(); })); it('should not call $anchorScroll if autoscroll evaluates to false', inject(function($rootScope, $compile, $animate, $timeout) { element = $compile('
')($rootScope); $rootScope.$apply(function () { $rootScope.tpl = 'template.html'; $rootScope.value = false; }); expect($animate.queue.shift().event).toBe('enter'); $animate.triggerCallbacks(); $rootScope.$apply(function () { $rootScope.tpl = 'template.html'; $rootScope.value = undefined; }); $rootScope.$apply(function () { $rootScope.tpl = 'template.html'; $rootScope.value = null; }); expect(autoScrollSpy).not.toHaveBeenCalled(); })); it('should only call $anchorScroll after the "enter" animation completes', inject( compileAndLink('
'), function($rootScope, $animate, $timeout) { expect(autoScrollSpy).not.toHaveBeenCalled(); $rootScope.$apply("tpl = 'template.html'"); expect($animate.queue.shift().event).toBe('enter'); $animate.triggerCallbacks(); expect(autoScrollSpy).toHaveBeenCalledOnce(); })); }); }); describe('ngInclude and transcludes', function() { var element, directive; beforeEach(module(function($compileProvider) { element = null; directive = $compileProvider.directive; })); afterEach(function() { if (element) { dealoc(element); } }); it('should allow access to directive controller from children when used in a replace template', function() { var controller; module(function() { directive('template', valueFn({ template: '
', replace: true, controller: function() { this.flag = true; } })); directive('test', valueFn({ require: '^template', link: function(scope, el, attr, ctrl) { controller = ctrl; } })); }); inject(function($compile, $rootScope, $httpBackend) { $httpBackend.expectGET('include.html').respond('
'); element = $compile('
')($rootScope); $rootScope.$apply(); $httpBackend.flush(); expect(controller.flag).toBe(true); }); }); it("should compile it's content correctly (although we remove it later)", function() { var testElement; module(function() { directive('test', function() { return { link: function(scope, element) { testElement = element; } }; }); }); inject(function($compile, $rootScope, $httpBackend) { $httpBackend.expectGET('include.html').respond(' '); element = $compile('
')($rootScope); $rootScope.$apply(); $httpBackend.flush(); expect(testElement[0].nodeName).toBe('DIV'); }); }); it('should link directives on the same element after the content has been loaded', function() { var contentOnLink; module(function() { directive('test', function() { return { link: function(scope, element) { contentOnLink = element.text(); } }; }); }); inject(function($compile, $rootScope, $httpBackend) { $httpBackend.expectGET('include.html').respond('someContent'); element = $compile('
')($rootScope); $rootScope.$apply(); $httpBackend.flush(); expect(contentOnLink).toBe('someContent'); }); }); it('should add the content to the element before compiling it', function() { var root; module(function() { directive('test', function() { return { link: function(scope, element) { root = element.parent().parent(); } }; }); }); inject(function($compile, $rootScope, $httpBackend) { $httpBackend.expectGET('include.html').respond(''); element = $compile('
')($rootScope); $rootScope.$apply(); $httpBackend.flush(); expect(root[0]).toBe(element[0]); }); }); }); describe('ngInclude animations', function() { var body, element, $rootElement; function html(html) { $rootElement.html(html); element = $rootElement.children().eq(0); return element; } beforeEach(module(function() { // we need to run animation on attached elements; return function(_$rootElement_) { $rootElement = _$rootElement_; body = jqLite(document.body); body.append($rootElement); }; })); afterEach(function(){ dealoc(body); dealoc(element); }); beforeEach(module('ngAnimateMock')); afterEach(function(){ dealoc(element); }); it('should fire off the enter animation', inject(function($compile, $rootScope, $templateCache, $animate) { var item; $templateCache.put('enter', [200, '
data
', {}]); $rootScope.tpl = 'enter'; element = $compile(html( '
' + '
' ))($rootScope); $rootScope.$digest(); var animation = $animate.queue.pop(); expect(animation.event).toBe('enter'); expect(animation.element.text()).toBe('data'); })); it('should fire off the leave animation', inject(function($compile, $rootScope, $templateCache, $animate) { var item; $templateCache.put('enter', [200, '
data
', {}]); $rootScope.tpl = 'enter'; element = $compile(html( '
' + '
' ))($rootScope); $rootScope.$digest(); var animation = $animate.queue.shift(); expect(animation.event).toBe('enter'); expect(animation.element.text()).toBe('data'); $rootScope.tpl = ''; $rootScope.$digest(); animation = $animate.queue.shift(); expect(animation.event).toBe('leave'); expect(animation.element.text()).toBe('data'); })); it('should animate two separate ngInclude elements', inject(function($compile, $rootScope, $templateCache, $animate) { var item; $templateCache.put('one', [200, 'one', {}]); $templateCache.put('two', [200, 'two', {}]); $rootScope.tpl = 'one'; element = $compile(html( '
' + '
' ))($rootScope); $rootScope.$digest(); var item1 = $animate.queue.shift().element; expect(item1.text()).toBe('one'); $rootScope.tpl = 'two'; $rootScope.$digest(); var itemA = $animate.queue.shift().element; var itemB = $animate.queue.shift().element; expect(itemA.attr('ng-include')).toBe('tpl'); expect(itemB.attr('ng-include')).toBe('tpl'); expect(itemA).not.toEqual(itemB); })); it('should destroy the previous leave animation if a new one takes place', function() { module(function($provide) { $provide.value('$animate', { enabled : function() { return true; }, leave : function() { //DOM operation left blank }, enter : function(element, parent, after) { angular.element(after).after(element); } }); }); inject(function ($compile, $rootScope, $animate, $templateCache) { var item; var $scope = $rootScope.$new(); element = $compile(html( '
' + '
Yo
' + '
' ))($scope); $templateCache.put('one', [200, '
one
', {}]); $templateCache.put('two', [200, '
two
', {}]); $scope.$apply('inc = "one"'); var destroyed, inner = element.children(0); inner.on('$destroy', function() { destroyed = true; }); $scope.$apply('inc = "two"'); $scope.$apply('inc = "one"'); $scope.$apply('inc = "two"'); expect(destroyed).toBe(true); }); }); });