From 0b6f1ce5f89f47f9302ff1e8cd8f4b92f837c413 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Wed, 20 Mar 2013 16:24:23 -0700 Subject: feat(ngAnimate): add support for animation --- test/ng/animationSpec.js | 15 +++ test/ng/animatorSpec.js | 195 +++++++++++++++++++++++++++++++++ test/ng/directive/ngIncludeSpec.js | 113 +++++++++++++++++++ test/ng/directive/ngRepeatSpec.js | 211 +++++++++++++++++++++++++++++++++++- test/ng/directive/ngShowHideSpec.js | 101 +++++++++++++++++ test/ng/directive/ngSwitchSpec.js | 118 ++++++++++++++++++++ test/ng/directive/ngViewSpec.js | 104 +++++++++++++++++- test/ng/snifferSpec.js | 43 +++++++- 8 files changed, 893 insertions(+), 7 deletions(-) create mode 100644 test/ng/animationSpec.js create mode 100644 test/ng/animatorSpec.js (limited to 'test') diff --git a/test/ng/animationSpec.js b/test/ng/animationSpec.js new file mode 100644 index 00000000..86592643 --- /dev/null +++ b/test/ng/animationSpec.js @@ -0,0 +1,15 @@ +'use strict'; + +describe('$animation', function() { + + it('should allow animation registration', function() { + var noopCustom = function(){}; + module(function($animationProvider) { + $animationProvider.register('noop-custom', valueFn(noopCustom)); + }); + inject(function($animation) { + expect($animation('noop-custom')).toBe(noopCustom); + }); + }); + +}); diff --git a/test/ng/animatorSpec.js b/test/ng/animatorSpec.js new file mode 100644 index 00000000..d8ceffab --- /dev/null +++ b/test/ng/animatorSpec.js @@ -0,0 +1,195 @@ +'use strict'; + +describe("$animator", function() { + + var element; + + afterEach(function(){ + dealoc(element); + }); + + describe("without animation", function() { + var window, animator; + + beforeEach(function() { + module(function($animationProvider, $provide) { + $provide.value('$window', window = angular.mock.createMockWindow()); + }) + inject(function($animator, $compile, $rootScope) { + animator = $animator($rootScope, {}); + element = $compile('
')($rootScope); + }) + }); + + it("should add element at the start of enter animation", inject(function($animator, $compile, $rootScope) { + var child = $compile('
')($rootScope); + expect(element.contents().length).toBe(0); + animator.enter(child, element); + expect(element.contents().length).toBe(1); + })); + + it("should remove the element at the end of leave animation", inject(function($animator, $compile, $rootScope) { + var child = $compile('
')($rootScope); + element.append(child); + expect(element.contents().length).toBe(1); + animator.leave(child, element); + expect(element.contents().length).toBe(0); + })); + + it("should reorder the move animation", inject(function($animator, $compile, $rootScope) { + var child1 = $compile('
1
')($rootScope); + var child2 = $compile('
2
')($rootScope); + element.append(child1); + element.append(child2); + expect(element.text()).toBe('12'); + animator.move(child1, element, child2); + expect(element.text()).toBe('21'); + })); + + it("should animate the show animation event", inject(function($animator, $compile, $rootScope) { + element.css('display','none'); + expect(element.css('display')).toBe('none'); + animator.show(element); + expect(element.css('display')).toBe('block'); + })); + + it("should animate the hide animation event", inject(function($animator, $compile, $rootScope) { + element.css('display','block'); + expect(element.css('display')).toBe('block'); + animator.hide(element); + expect(element.css('display')).toBe('none'); + })); + + }); + + describe("with polyfill", function() { + + var child, after, window, animator; + + beforeEach(function() { + module(function($animationProvider, $provide) { + $provide.value('$window', window = angular.mock.createMockWindow()); + $animationProvider.register('custom', function() { + return { + start: function(element, done) { + done(); + } + } + }); + }) + inject(function($animator, $compile, $rootScope) { + element = $compile('
')($rootScope); + child = $compile('
')($rootScope); + after = $compile('
')($rootScope); + }); + }) + + it("should animate the enter animation event", inject(function($animator, $rootScope) { + animator = $animator($rootScope, { + ngAnimate : '{enter: \'custom\'}' + }); + expect(element.contents().length).toBe(0); + animator.enter(child, element); + window.setTimeout.expect(1).process(); + })); + + it("should animate the leave animation event", inject(function($animator, $rootScope) { + animator = $animator($rootScope, { + ngAnimate : '{leave: \'custom\'}' + }); + element.append(child); + expect(element.contents().length).toBe(1); + animator.leave(child, element); + window.setTimeout.expect(1).process(); + expect(element.contents().length).toBe(0); + })); + + it("should animate the move animation event", inject(function($animator, $compile, $rootScope) { + animator = $animator($rootScope, { + ngAnimate : '{move: \'custom\'}' + }); + var child1 = $compile('
1
')($rootScope); + var child2 = $compile('
2
')($rootScope); + element.append(child1); + element.append(child2); + expect(element.text()).toBe('12'); + animator.move(child1, element, child2); + expect(element.text()).toBe('21'); + window.setTimeout.expect(1).process(); + })); + + it("should animate the show animation event", inject(function($animator, $rootScope) { + animator = $animator($rootScope, { + ngAnimate : '{show: \'custom\'}' + }); + element.css('display','none'); + expect(element.css('display')).toBe('none'); + animator.show(element); + expect(element.css('display')).toBe('block'); + window.setTimeout.expect(1).process(); + expect(element.css('display')).toBe('block'); + })); + + it("should animate the hide animation event", inject(function($animator, $rootScope) { + animator = $animator($rootScope, { + ngAnimate : '{hide: \'custom\'}' + }); + element.css('display','block'); + expect(element.css('display')).toBe('block'); + animator.hide(element); + expect(element.css('display')).toBe('block'); + window.setTimeout.expect(1).process(); + expect(element.css('display')).toBe('none'); + })); + + it("should assign the ngAnimate string to all events if a string is given", + inject(function($animator, $sniffer, $rootScope) { + if (!$sniffer.supportsTransitions) return; + animator = $animator($rootScope, { + ngAnimate : '"custom"' + }); + + //enter + animator.enter(child, element); + expect(child.attr('class')).toContain('custom-enter-setup'); + window.setTimeout.expect(1).process(); + expect(child.attr('class')).toContain('custom-enter-start'); + window.setTimeout.expect(0).process(); + + //leave + element.append(after); + animator.move(child, element, after); + expect(child.attr('class')).toContain('custom-move-setup'); + window.setTimeout.expect(1).process(); + expect(child.attr('class')).toContain('custom-move-start'); + window.setTimeout.expect(0).process(); + + //hide + animator.hide(child); + expect(child.attr('class')).toContain('custom-hide-setup'); + window.setTimeout.expect(1).process(); + expect(child.attr('class')).toContain('custom-hide-start'); + window.setTimeout.expect(0).process(); + + //show + animator.show(child); + expect(child.attr('class')).toContain('custom-show-setup'); + window.setTimeout.expect(1).process(); + expect(child.attr('class')).toContain('custom-show-start'); + window.setTimeout.expect(0).process(); + + //leave + animator.leave(child); + expect(child.attr('class')).toContain('custom-leave-setup'); + window.setTimeout.expect(1).process(); + expect(child.attr('class')).toContain('custom-leave-start'); + window.setTimeout.expect(0).process(); + })); + }); + + it("should throw an error when an invalid ng-animate syntax is provided", inject(function($compile, $rootScope) { + expect(function() { + element = $compile('
')($rootScope); + }).toThrow("Syntax Error: Token ':' not a primary expression at column 1 of the expression [:] starting at [:]."); + })); +}); diff --git a/test/ng/directive/ngIncludeSpec.js b/test/ng/directive/ngIncludeSpec.js index dce803b5..191eaa05 100644 --- a/test/ng/directive/ngIncludeSpec.js +++ b/test/ng/directive/ngIncludeSpec.js @@ -280,3 +280,116 @@ describe('ngInclude', function() { })); }); }); + +describe('ngInclude ngAnimate', function() { + var element, vendorPrefix, window; + + beforeEach(module(function($animationProvider, $provide) { + $provide.value('$window', window = angular.mock.createMockWindow()); + return function($sniffer) { + vendorPrefix = '-' + $sniffer.vendorPrefix + '-'; + }; + })); + + afterEach(function(){ + dealoc(element); + }); + + it('should fire off the enter animation + add and remove the css classes', + inject(function($compile, $rootScope, $templateCache, $sniffer) { + + $templateCache.put('enter', [200, '
data
', {}]); + $rootScope.tpl = 'enter'; + element = $compile( + '
' + + '
' + )($rootScope); + $rootScope.$digest(); + + //if we add the custom css stuff here then it will get picked up before the animation takes place + var child = jqLite(element.children()[0]); + var cssProp = vendorPrefix + 'transition'; + var cssValue = '1s linear all'; + child.css(cssProp, cssValue); + + if ($sniffer.supportsTransitions) { + expect(child.attr('class')).toContain('custom-enter-setup'); + window.setTimeout.expect(1).process(); + + expect(child.attr('class')).toContain('custom-enter-start'); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + expect(child.attr('class')).not.toContain('custom-enter-setup'); + expect(child.attr('class')).not.toContain('custom-enter-start'); + })); + + it('should fire off the leave animation + add and remove the css classes', + inject(function($compile, $rootScope, $templateCache, $sniffer) { + $templateCache.put('enter', [200, '
data
', {}]); + $rootScope.tpl = 'enter'; + element = $compile( + '
' + + '
' + )($rootScope); + $rootScope.$digest(); + + //if we add the custom css stuff here then it will get picked up before the animation takes place + var child = jqLite(element.children()[0]); + var cssProp = vendorPrefix + 'transition'; + var cssValue = '1s linear all'; + child.css(cssProp, cssValue); + + $rootScope.tpl = ''; + $rootScope.$digest(); + + if ($sniffer.supportsTransitions) { + expect(child.attr('class')).toContain('custom-leave-setup'); + window.setTimeout.expect(1).process(); + + expect(child.attr('class')).toContain('custom-leave-start'); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + expect(child.attr('class')).not.toContain('custom-leave-setup'); + expect(child.attr('class')).not.toContain('custom-leave-start'); + })); + + it('should catch and use the correct duration for animation', + inject(function($compile, $rootScope, $templateCache, $sniffer) { + $templateCache.put('enter', [200, '
data
', {}]); + $rootScope.tpl = 'enter'; + element = $compile( + '
' + + '
' + )($rootScope); + $rootScope.$digest(); + + //if we add the custom css stuff here then it will get picked up before the animation takes place + var child = jqLite(element.children()[0]); + var cssProp = vendorPrefix + 'transition'; + var cssValue = '0.5s linear all'; + child.css(cssProp, cssValue); + + $rootScope.tpl = 'enter'; + $rootScope.$digest(); + + if ($sniffer.supportsTransitions) { + window.setTimeout.expect(1).process(); + window.setTimeout.expect(500).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + })); + +}); diff --git a/test/ng/directive/ngRepeatSpec.js b/test/ng/directive/ngRepeatSpec.js index 44406d6d..533b83c8 100644 --- a/test/ng/directive/ngRepeatSpec.js +++ b/test/ng/directive/ngRepeatSpec.js @@ -189,11 +189,11 @@ describe('ngRepeat', function() { element = $compile( '')(scope); + '' + + '')(scope); scope.items = {misko: true, shyam: true, zhenbo:true}; scope.$digest(); @@ -410,6 +410,24 @@ describe('ngRepeat', function() { }); + it('should preserve data on move of elements', function() { + element = $compile('')(scope); + scope.array = ['a', 'b']; + scope.$digest(); + + var lis = element.find('li'); + lis.eq(0).data('mark', 'a'); + lis.eq(1).data('mark', 'b'); + + scope.array = ['b', 'a']; + scope.$digest(); + + var lis = element.find('li'); + expect(lis.eq(0).data('mark')).toEqual('b'); + expect(lis.eq(1).data('mark')).toEqual('a'); + }); + + describe('stability', function() { var a, b, c, d, lis; @@ -481,6 +499,7 @@ describe('ngRepeat', function() { scope.items = ['hello', 'cau', 'ahoj']; scope.$digest(); lis = element.find('li'); + lis[2].id = 'yes'; scope.items = ['ahoj', 'hello', 'cau']; scope.$digest(); @@ -492,3 +511,189 @@ describe('ngRepeat', function() { }); }); }); + +describe('ngRepeat ngAnimate', function() { + var element, vendorPrefix, window; + + beforeEach(module(function($animationProvider, $provide) { + $provide.value('$window', window = angular.mock.createMockWindow()); + return function($sniffer) { + vendorPrefix = '-' + $sniffer.vendorPrefix + '-'; + }; + })); + + afterEach(function(){ + dealoc(element); + }); + + it('should fire off the enter animation + add and remove the css classes', + inject(function($compile, $rootScope, $sniffer) { + + element = $compile( + '
' + + '{{ item }}' + + '
' + )($rootScope); + + $rootScope.items = ['1','2','3']; + $rootScope.$digest(); + + //if we add the custom css stuff here then it will get picked up before the animation takes place + var cssProp = vendorPrefix + 'transition'; + var cssValue = '1s linear all'; + var kids = element.children(); + for(var i=0;i
' + + '{{ item }}' + + '
' + )($rootScope); + + $rootScope.items = ['1','2','3']; + $rootScope.$digest(); + + //if we add the custom css stuff here then it will get picked up before the animation takes place + var cssProp = vendorPrefix + 'transition'; + var cssValue = '1s linear all'; + var kids = element.children(); + for(var i=0;i' + + '
' + + '{{ item }}' + + '
' + + '' + )($rootScope); + + $rootScope.items = ['1','2','3']; + $rootScope.$digest(); + + //if we add the custom css stuff here then it will get picked up before the animation takes place + var cssProp = '-' + $sniffer.vendorPrefix + '-transition'; + var cssValue = '1s linear all'; + var kids = element.children(); + for(var i=0;i
' + + '{{ item }}' + + '
' + )($rootScope); + + $rootScope.items = ['a','b']; + $rootScope.$digest(); + + //if we add the custom css stuff here then it will get picked up before the animation takes place + var kids = element.children(); + var first = jqLite(kids[0]); + var second = jqLite(kids[1]); + var cssProp = '-' + $sniffer.vendorPrefix + '-transition'; + var cssValue = '0.5s linear all'; + first.css(cssProp, cssValue); + second.css(cssProp, cssValue); + + if ($sniffer.supportsTransitions) { + window.setTimeout.expect(1).process(); + window.setTimeout.expect(1).process(); + window.setTimeout.expect(500).process(); + window.setTimeout.expect(500).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + })); + +}); diff --git a/test/ng/directive/ngShowHideSpec.js b/test/ng/directive/ngShowHideSpec.js index ee251dbf..d1d314e7 100644 --- a/test/ng/directive/ngShowHideSpec.js +++ b/test/ng/directive/ngShowHideSpec.js @@ -41,3 +41,104 @@ describe('ngShow / ngHide', function() { })); }); }); + +describe('ngShow / ngHide - ngAnimate', function() { + var element, window; + var vendorPrefix; + + beforeEach(module(function($animationProvider, $provide) { + $provide.value('$window', window = angular.mock.createMockWindow()); + return function($sniffer) { + vendorPrefix = '-' + $sniffer.vendorPrefix + '-'; + }; + })); + + afterEach(function() { + dealoc(element); + }); + + describe('ngShow', function() { + it('should fire off the animator.show and animator.hide animation', inject(function($compile, $rootScope, $sniffer) { + var $scope = $rootScope.$new(); + $scope.on = true; + element = $compile( + '
' + + '
' + )($scope); + $scope.$digest(); + + if ($sniffer.supportsTransitions) { + expect(element.attr('class')).toContain('custom-show-setup'); + window.setTimeout.expect(1).process(); + + expect(element.attr('class')).toContain('custom-show-start'); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + expect(element.attr('class')).not.toContain('custom-show-start'); + expect(element.attr('class')).not.toContain('custom-show-setup'); + + $scope.on = false; + $scope.$digest(); + if ($sniffer.supportsTransitions) { + expect(element.attr('class')).toContain('custom-hide-setup'); + window.setTimeout.expect(1).process(); + expect(element.attr('class')).toContain('custom-hide-start'); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + expect(element.attr('class')).not.toContain('custom-hide-start'); + expect(element.attr('class')).not.toContain('custom-hide-setup'); + })); + }); + + describe('ngHide', function() { + it('should fire off the animator.show and animator.hide animation', inject(function($compile, $rootScope, $sniffer) { + var $scope = $rootScope.$new(); + $scope.off = true; + element = $compile( + '
' + + '
' + )($scope); + $scope.$digest(); + + if ($sniffer.supportsTransitions) { + expect(element.attr('class')).toContain('custom-hide-setup'); + window.setTimeout.expect(1).process(); + + expect(element.attr('class')).toContain('custom-hide-start'); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + expect(element.attr('class')).not.toContain('custom-hide-start'); + expect(element.attr('class')).not.toContain('custom-hide-setup'); + + $scope.off = false; + $scope.$digest(); + + if ($sniffer.supportsTransitions) { + expect(element.attr('class')).toContain('custom-show-setup'); + window.setTimeout.expect(1).process(); + expect(element.attr('class')).toContain('custom-show-start'); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + expect(element.attr('class')).not.toContain('custom-show-start'); + expect(element.attr('class')).not.toContain('custom-show-setup'); + })); + }); +}); diff --git a/test/ng/directive/ngSwitchSpec.js b/test/ng/directive/ngSwitchSpec.js index 85240b19..9d3eceaa 100644 --- a/test/ng/directive/ngSwitchSpec.js +++ b/test/ng/directive/ngSwitchSpec.js @@ -213,3 +213,121 @@ describe('ngSwitch', function() { // afterwards a global afterEach will check for leaks in jq data cache object })); }); + +describe('ngSwitch ngAnimate', function() { + var element, vendorPrefix, window; + + beforeEach(module(function($animationProvider, $provide) { + $provide.value('$window', window = angular.mock.createMockWindow()); + return function($sniffer) { + vendorPrefix = '-' + $sniffer.vendorPrefix + '-'; + }; + })); + + afterEach(function(){ + dealoc(element); + }); + + it('should fire off the enter animation + set and remove the classes', + inject(function($compile, $rootScope, $sniffer) { + var $scope = $rootScope.$new(); + var style = vendorPrefix + 'transition: 1s linear all'; + element = $compile( + '
' + + '
one
' + + '
two
' + + '
three
' + + '
' + )($scope); + + $scope.val = 'one'; + $scope.$digest(); + + expect(element.children().length).toBe(1); + var first = element.children()[0]; + + if ($sniffer.supportsTransitions) { + expect(first.className).toContain('cool-enter-setup'); + window.setTimeout.expect(1).process(); + + expect(first.className).toContain('cool-enter-start'); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + expect(first.className).not.toContain('cool-enter-setup'); + expect(first.className).not.toContain('cool-enter-start'); + })); + + + it('should fire off the leave animation + set and remove the classes', + inject(function($compile, $rootScope, $sniffer) { + var $scope = $rootScope.$new(); + var style = vendorPrefix + 'transition: 1s linear all'; + element = $compile( + '
' + + '
one
' + + '
two
' + + '
three
' + + '
' + )($scope); + + $scope.val = 'two'; + $scope.$digest(); + + if ($sniffer.supportsTransitions) { + window.setTimeout.expect(1).process(); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + $scope.val = 'three'; + $scope.$digest(); + + expect(element.children().length).toBe($sniffer.supportsTransitions ? 2 : 1); + var first = element.children()[0]; + + + if ($sniffer.supportsTransitions) { + expect(first.className).toContain('cool-leave-setup'); + window.setTimeout.expect(1).process(); + window.setTimeout.expect(1).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + + if ($sniffer.supportsTransitions) { + expect(first.className).toContain('cool-leave-start'); + window.setTimeout.expect(1000).process(); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + expect(first.className).not.toContain('cool-leave-setup'); + expect(first.className).not.toContain('cool-leave-start'); + })); + + it('should catch and use the correct duration for animation', + inject(function($compile, $rootScope, $sniffer) { + element = $compile( + '
' + + '
one
' + + '
' + )($rootScope); + + $rootScope.val = 'one'; + $rootScope.$digest(); + + if ($sniffer.supportsTransitions) { + window.setTimeout.expect(1).process(); + window.setTimeout.expect(500).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + })); + +}); diff --git a/test/ng/directive/ngViewSpec.js b/test/ng/directive/ngViewSpec.js index e781b98b..dcdfe686 100644 --- a/test/ng/directive/ngViewSpec.js +++ b/test/ng/directive/ngViewSpec.js @@ -473,7 +473,7 @@ describe('ngView', function() { $rootScope.$digest(); forEach(element.contents(), function(node) { - if ( node.nodeType == 3 ) { + if ( node.nodeType == 3 /* text node */) { expect(jqLite(node).scope()).not.toBe($route.current.scope); expect(jqLite(node).controller()).not.toBeDefined(); } else { @@ -484,3 +484,105 @@ describe('ngView', function() { }); }); }); + +describe('ngAnimate', function() { + var element, window; + + beforeEach(module(function($provide, $routeProvider) { + $provide.value('$window', window = angular.mock.createMockWindow()); + $routeProvider.when('/foo', {controller: noop, templateUrl: '/foo.html'}); + return function($templateCache) { + $templateCache.put('/foo.html', [200, '
data
', {}]); + } + })); + + afterEach(function(){ + dealoc(element); + }); + + it('should fire off the enter animation + add and remove the css classes', + inject(function($compile, $rootScope, $sniffer, $location, $templateCache) { + element = $compile('
')($rootScope); + + $location.path('/foo'); + $rootScope.$digest(); + + //if we add the custom css stuff here then it will get picked up before the animation takes place + var child = jqLite(element.children()[0]); + var cssProp = '-' + $sniffer.vendorPrefix + '-transition'; + var cssValue = '1s linear all'; + child.css(cssProp, cssValue); + + if ($sniffer.supportsTransitions) { + expect(child.attr('class')).toContain('custom-enter-setup'); + window.setTimeout.expect(1).process(); + + expect(child.attr('class')).toContain('custom-enter-start'); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + expect(child.attr('class')).not.toContain('custom-enter-setup'); + expect(child.attr('class')).not.toContain('custom-enter-start'); + })); + + it('should fire off the leave animation + add and remove the css classes', + inject(function($compile, $rootScope, $sniffer, $location, $templateCache) { + $templateCache.put('/foo.html', [200, '
foo
', {}]); + element = $compile('
')($rootScope); + + $location.path('/foo'); + $rootScope.$digest(); + + //if we add the custom css stuff here then it will get picked up before the animation takes place + var child = jqLite(element.children()[0]); + var cssProp = '-' + $sniffer.vendorPrefix + '-transition'; + var cssValue = '1s linear all'; + child.css(cssProp, cssValue); + + $location.path('/'); + $rootScope.$digest(); + + if ($sniffer.supportsTransitions) { + expect(child.attr('class')).toContain('custom-leave-setup'); + window.setTimeout.expect(1).process(); + + expect(child.attr('class')).toContain('custom-leave-start'); + window.setTimeout.expect(1000).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + + expect(child.attr('class')).not.toContain('custom-leave-setup'); + expect(child.attr('class')).not.toContain('custom-leave-start'); + })); + + it('should catch and use the correct duration for animations', + inject(function($compile, $rootScope, $sniffer, $location, $templateCache) { + $templateCache.put('/foo.html', [200, '
foo
', {}]); + element = $compile( + '
' + + '
' + )($rootScope); + + $location.path('/foo'); + $rootScope.$digest(); + + //if we add the custom css stuff here then it will get picked up before the animation takes place + var child = jqLite(element.children()[0]); + var cssProp = '-' + $sniffer.vendorPrefix + '-transition'; + var cssValue = '0.5s linear all'; + child.css(cssProp, cssValue); + + if($sniffer.supportsTransitions) { + window.setTimeout.expect(1).process(); + window.setTimeout.expect($sniffer.supportsTransitions ? 500 : 0).process(); + } else { + expect(window.setTimeout.queue).toEqual([]); + } + })); + +}); diff --git a/test/ng/snifferSpec.js b/test/ng/snifferSpec.js index 2369deaf..d791c17b 100644 --- a/test/ng/snifferSpec.js +++ b/test/ng/snifferSpec.js @@ -5,6 +5,9 @@ describe('$sniffer', function() { function sniffer($window, $document) { $window.navigator = {}; $document = jqLite($document || {}); + if (!$document[0].body) { + $document[0].body = window.document.body; + } return new $SnifferProvider().$get[2]($window, $document); } @@ -21,11 +24,11 @@ describe('$sniffer', function() { describe('hashchange', function() { it('should be true if onhashchange property defined', function() { - expect(sniffer({onhashchange: true}, {}).hashchange).toBe(true); + expect(sniffer({onhashchange: true}).hashchange).toBe(true); }); it('should be false if onhashchange property not defined', function() { - expect(sniffer({}, {}).hashchange).toBe(false); + expect(sniffer({}).hashchange).toBe(false); }); it('should be false if documentMode is 7 (IE8 comp mode)', function() { @@ -83,7 +86,7 @@ describe('$sniffer', function() { describe('csp', function() { it('should be false if document.securityPolicy.isActive not available', function() { - expect(sniffer({}, {}).csp).toBe(false); + expect(sniffer({}).csp).toBe(false); }); @@ -96,4 +99,38 @@ describe('$sniffer', function() { expect(sniffer({}, createDocumentWithCSP(true)).csp).toBe(true); }); }); + + describe('vendorPrefix', function() { + + it('should return the correct vendor prefix based on the browser', function() { + inject(function($sniffer, $window) { + var expectedPrefix; + var ua = $window.navigator.userAgent.toLowerCase(); + if(/chrome/i.test(ua) || /safari/i.test(ua) || /webkit/i.test(ua)) { + expectedPrefix = 'Webkit'; + } + else if(/firefox/i.test(ua)) { + expectedPrefix = 'Moz'; + } + else if(/ie/i.test(ua)) { + expectedPrefix = 'Ms'; + } + else if(/opera/i.test(ua)) { + expectedPrefix = 'O'; + } + expect($sniffer.vendorPrefix).toBe(expectedPrefix); + }); + }); + + }); + + describe('supportsTransitions', function() { + + it('should be either true or false', function() { + inject(function($sniffer) { + expect($sniffer.supportsTransitions).not.toBe(undefined); + }); + }); + + }); }); -- cgit v1.2.3