From 100cf7245eff0cf9d6d19d8bb5f26852d78581a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski-Owczarek?= Date: Mon, 15 Oct 2018 11:05:14 +0200 Subject: [PATCH 1/6] chore(*): update Sauce Connect from 4.4.12 to 4.5.1 Sauce Connect 4.5.0 fixes "a major bug in connection state logic that caused clients to exit prematurely". See https://wiki.saucelabs.com/display/DOCS/Sauce+Connect+Proxy+Change+Logs This will possibly improve the stability of our test runs. Closes #16730 --- lib/saucelabs/start_tunnel.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/saucelabs/start_tunnel.sh b/lib/saucelabs/start_tunnel.sh index 13088c8b0976..c9df2715e79d 100755 --- a/lib/saucelabs/start_tunnel.sh +++ b/lib/saucelabs/start_tunnel.sh @@ -11,7 +11,7 @@ set -e # Curl and run this script as part of your .travis.yml before_script section: # before_script: # - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash -SC_VERSION="4.4.12" +SC_VERSION="4.5.1" CONNECT_URL="https://saucelabs.com/downloads/sc-$SC_VERSION-linux.tar.gz" CONNECT_DIR="/tmp/sauce-connect-$RANDOM" CONNECT_DOWNLOAD="sc-$SC_VERSION-linux.tar.gz" From d86f6be57bbfc154c36e42469008a87bfaeef1d2 Mon Sep 17 00:00:00 2001 From: Martin Staffa Date: Fri, 5 Oct 2018 21:45:32 +0200 Subject: [PATCH 2/6] fix(ngMock): make matchLatestDefinitionEnabled work Fixes #16702 --- src/ngMock/angular-mocks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js index 7537dcab463a..8f9a901d2169 100644 --- a/src/ngMock/angular-mocks.js +++ b/src/ngMock/angular-mocks.js @@ -1650,7 +1650,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * as a getter */ $httpBackend.matchLatestDefinitionEnabled = function(value) { - if (isDefined(value)) { + if (angular.isDefined(value)) { matchLatestDefinition = value; return this; } else { From 52ad819d2f986590069bcb6fa9d46cfc8ce6a5ae Mon Sep 17 00:00:00 2001 From: Martin Staffa Date: Mon, 15 Oct 2018 10:51:27 +0200 Subject: [PATCH 3/6] docs(ngMock/ngMockE2E.$httpBackend): fix method name to matchLatestDefinitionEnabled See #16702 --- src/ngMock/angular-mocks.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js index 8f9a901d2169..12ab4ecf20db 100644 --- a/src/ngMock/angular-mocks.js +++ b/src/ngMock/angular-mocks.js @@ -1619,12 +1619,12 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { /** * @ngdoc method - * @name $httpBackend#matchLatestDefinition + * @name $httpBackend#matchLatestDefinitionEnabled * @description * This method can be used to change which mocked responses `$httpBackend` returns, when defining * them with {@link ngMock.$httpBackend#when $httpBackend.when()} (and shortcut methods). * By default, `$httpBackend` returns the first definition that matches. When setting - * `$http.matchLatestDefinition(true)`, it will use the last response that matches, i.e. the + * `$http.matchLatestDefinitionEnabled(true)`, it will use the last response that matches, i.e. the * one that was added last. * * ```js @@ -1632,7 +1632,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * hb.when('GET', '/url1').respond(201, 'another', {}); * hb('GET', '/url1'); // receives "content" * - * $http.matchLatestDefinition(true) + * $http.matchLatestDefinitionEnabled(true) * hb('GET', '/url1'); // receives "another" * * hb.when('GET', '/url1').respond(201, 'onemore', {}); @@ -1641,7 +1641,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { * * This is useful if a you have a default response that is overriden inside specific tests. * - * Note that different from config methods on providers, `matchLatestDefinition()` can be changed + * Note that different from config methods on providers, `matchLatestDefinitionEnabled()` can be changed * even when the application is already running. * * @param {Boolean=} value value to set, either `true` or `false`. Default is `false`. @@ -2919,13 +2919,13 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { */ /** * @ngdoc method - * @name $httpBackend#matchLatestDefinition + * @name $httpBackend#matchLatestDefinitionEnabled * @module ngMockE2E * @description * This method can be used to change which mocked responses `$httpBackend` returns, when defining * them with {@link ngMock.$httpBackend#when $httpBackend.when()} (and shortcut methods). * By default, `$httpBackend` returns the first definition that matches. When setting - * `$http.matchLatestDefinition(true)`, it will use the last response that matches, i.e. the + * `$http.matchLatestDefinitionEnabled(true)`, it will use the last response that matches, i.e. the * one that was added last. * * ```js @@ -2933,7 +2933,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { * hb.when('GET', '/url1').respond(201, 'another', {}); * hb('GET', '/url1'); // receives "content" * - * $http.matchLatestDefinition(true) + * $http.matchLatestDefinitionEnabled(true) * hb('GET', '/url1'); // receives "another" * * hb.when('GET', '/url1').respond(201, 'onemore', {}); @@ -2942,7 +2942,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { * * This is useful if a you have a default response that is overriden inside specific tests. * - * Note that different from config methods on providers, `matchLatestDefinition()` can be changed + * Note that different from config methods on providers, `matchLatestDefinitionEnabled()` can be changed * even when the application is already running. * * @param {Boolean=} value value to set, either `true` or `false`. Default is `false`. From 393072081c64886211b3b312b2be76e8726e267a Mon Sep 17 00:00:00 2001 From: Martin Staffa Date: Mon, 15 Oct 2018 15:10:10 +0200 Subject: [PATCH 4/6] test(modules): properly isolate module tests Closes #16712 --- Gruntfile.js | 22 ++++- angularFiles.js | 99 ++++++++++++++++++--- karma-modules.conf.js | 11 +-- lib/grunt/plugins.js | 11 ++- src/ngMessageFormat/messageFormatService.js | 8 +- test/ngCookies/cookieWriterSpec.js | 1 + test/ngMock/angular-mocksSpec.js | 16 +++- test/ngResource/resourceSpec.js | 2 + 8 files changed, 139 insertions(+), 31 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 3e2b1630876e..23032360e849 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -141,7 +141,16 @@ module.exports = function(grunt) { 'jquery-2.2': 'karma-jquery-2.2.conf.js', 'jquery-2.1': 'karma-jquery-2.1.conf.js', docs: 'karma-docs.conf.js', - modules: 'karma-modules.conf.js' + 'modules-ngAnimate': 'ngAnimate', + 'modules-ngAria': 'ngAria', + 'modules-ngCookies': 'ngCookies', + 'modules-ngMessageFormat': 'ngMessageFormat', + 'modules-ngMessages': 'ngMessages', + 'modules-ngMock': 'ngMock', + 'modules-ngResource': 'ngResource', + 'modules-ngRoute': 'ngRoute', + 'modules-ngSanitize': 'ngSanitize', + 'modules-ngTouch': 'ngTouch' }, @@ -430,7 +439,16 @@ module.exports = function(grunt) { grunt.registerTask('test:jquery-2.1', 'Run the jQuery 2.1 unit tests with Karma', ['tests:jquery-2.1']); grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', [ 'build', - 'tests:modules' + 'tests:modules-ngAnimate', + 'tests:modules-ngAria', + 'tests:modules-ngCookies', + 'tests:modules-ngMessageFormat', + 'tests:modules-ngMessages', + 'tests:modules-ngMock', + 'tests:modules-ngResource', + 'tests:modules-ngRoute', + 'tests:modules-ngSanitize', + 'tests:modules-ngTouch' ]); grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']); grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', [ diff --git a/angularFiles.js b/angularFiles.js index 6c4bcab00cec..84f041c3065f 100644 --- a/angularFiles.js +++ b/angularFiles.js @@ -189,21 +189,84 @@ var angularFiles = { 'src/angular.bind.js' ], - 'karmaModules': [ + 'karmaModulesBase': [ 'build/angular.js', - '@angularSrcModules', + 'build/angular-mocks.js', 'test/modules/no_bootstrap.js', - 'test/helpers/*.js', - 'test/ngAnimate/*.js', - 'test/ngMessageFormat/*.js', - 'test/ngMessages/*.js', - 'test/ngMock/*.js', - 'test/ngCookies/*.js', - 'test/ngRoute/**/*.js', - 'test/ngResource/*.js', - 'test/ngSanitize/**/*.js', - 'test/ngTouch/**/*.js', - 'test/ngAria/*.js' + 'test/helpers/matchers.js', + 'test/helpers/privateMocks.js', + 'test/helpers/support.js', + 'test/helpers/testabilityPatch.js' + ], + + 'karmaModules-ngAnimate': [ + '@karmaModulesBase', + '@angularSrcModuleNgAnimate', + 'test/ngAnimate/**/*.js' + ], + + 'karmaModules-ngAria': [ + '@karmaModulesBase', + '@angularSrcModuleNgAria', + 'test/ngAria/**/*.js' + ], + + 'karmaModules-ngCookies': [ + '@karmaModulesBase', + '@angularSrcModuleNgCookies', + 'test/ngCookies/**/*.js' + ], + + 'karmaModules-ngMessageFormat': [ + '@karmaModulesBase', + '@angularSrcModuleNgMessageFormat', + 'test/ngMessageFormat/**/*.js' + ], + + 'karmaModules-ngMessages': [ + '@karmaModulesBase', + 'build/angular-animate.js', + '@angularSrcModuleNgMessages', + 'test/ngMessages/**/*.js' + ], + + // ngMock doesn't include the base because it must use the ngMock src files + 'karmaModules-ngMock': [ + 'build/angular.js', + 'src/ngMock/*.js', + 'test/modules/no_bootstrap.js', + 'test/helpers/matchers.js', + 'test/helpers/privateMocks.js', + 'test/helpers/support.js', + 'test/helpers/testabilityPatch.js', + 'src/routeToRegExp.js', + 'build/angular-animate.js', + 'test/ngMock/**/*.js' + ], + + 'karmaModules-ngResource': [ + '@karmaModulesBase', + '@angularSrcModuleNgResource', + 'test/ngResource/**/*.js' + ], + + 'karmaModules-ngRoute': [ + '@karmaModulesBase', + 'build/angular-animate.js', + '@angularSrcModuleNgRoute', + 'test/ngRoute/**/*.js' + ], + + 'karmaModules-ngSanitize': [ + '@karmaModulesBase', + '@angularSrcModuleNgSanitize', + 'test/ngSanitize/**/*.js' + ], + + 'karmaModules-ngTouch': [ + '@karmaModulesBase', + '@angularSrcModuleNgTouch', + 'test/ngTouch/**/*.js' ], 'karmaJquery': [ @@ -232,6 +295,16 @@ var angularFiles = { }); }); +angularFiles['angularSrcModuleNgAnimate'] = angularFiles['angularModules']['ngAnimate']; +angularFiles['angularSrcModuleNgAria'] = angularFiles['angularModules']['ngAria']; +angularFiles['angularSrcModuleNgCookies'] = angularFiles['angularModules']['ngCookies']; +angularFiles['angularSrcModuleNgMessageFormat'] = angularFiles['angularModules']['ngMessageFormat']; +angularFiles['angularSrcModuleNgMessages'] = angularFiles['angularModules']['ngMessages']; +angularFiles['angularSrcModuleNgResource'] = angularFiles['angularModules']['ngResource']; +angularFiles['angularSrcModuleNgRoute'] = angularFiles['angularModules']['ngRoute']; +angularFiles['angularSrcModuleNgSanitize'] = angularFiles['angularModules']['ngSanitize']; +angularFiles['angularSrcModuleNgTouch'] = angularFiles['angularModules']['ngTouch']; + angularFiles['angularSrcModules'] = [].concat( angularFiles['angularModules']['ngAnimate'], angularFiles['angularModules']['ngMessageFormat'], diff --git a/karma-modules.conf.js b/karma-modules.conf.js index 81ae2a069459..a4efc33b4b77 100644 --- a/karma-modules.conf.js +++ b/karma-modules.conf.js @@ -4,14 +4,11 @@ var angularFiles = require('./angularFiles'); var sharedConfig = require('./karma-shared.conf'); module.exports = function(config) { - sharedConfig(config, {testName: 'AngularJS: modules', logFile: 'karma-modules.log'}); + var angularModule = process.env.KARMA_MODULE; - config.set({ - files: angularFiles.mergeFilesFor('karmaModules'), + sharedConfig(config, {testName: 'AngularJS: module ' + angularModule, logFile: 'karma-modules-' + angularModule + '.log'}); - junitReporter: { - outputFile: 'test_out/modules.xml', - suite: 'modules' - } + config.set({ + files: angularFiles.mergeFilesFor('karmaModules-' + angularModule) }); }; diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js index 7e5dc290b533..a57cc29ac42b 100644 --- a/lib/grunt/plugins.js +++ b/lib/grunt/plugins.js @@ -43,7 +43,16 @@ module.exports = function(grunt) { grunt.registerMultiTask('tests', '**Use `grunt test` instead**', function() { - util.startKarma(this.data, true, this.async()); + var configFile; + + if (this.nameArgs.includes('modules')) { + configFile = 'karma-modules.conf.js'; + process.env.KARMA_MODULE = this.data; + } else { + configFile = this.data; + } + + util.startKarma(configFile, true, this.async()); }); diff --git a/src/ngMessageFormat/messageFormatService.js b/src/ngMessageFormat/messageFormatService.js index 9f93950073a5..c10ff8847baa 100644 --- a/src/ngMessageFormat/messageFormatService.js +++ b/src/ngMessageFormat/messageFormatService.js @@ -215,10 +215,10 @@ var noop; var toJson; var $$stringify; -var module = window['angular']['module']('ngMessageFormat', ['ng']); -module['info']({ 'angularVersion': '"NG_VERSION_FULL"' }); -module['factory']('$$messageFormat', $$MessageFormatFactory); -module['config'](['$provide', function($provide) { +var ngModule = window['angular']['module']('ngMessageFormat', ['ng']); +ngModule['info']({ 'angularVersion': '"NG_VERSION_FULL"' }); +ngModule['factory']('$$messageFormat', $$MessageFormatFactory); +ngModule['config'](['$provide', function($provide) { $interpolateMinErr = window['angular']['$interpolateMinErr']; isFunction = window['angular']['isFunction']; noop = window['angular']['noop']; diff --git a/test/ngCookies/cookieWriterSpec.js b/test/ngCookies/cookieWriterSpec.js index ddc0b590d663..71325b0a70bc 100644 --- a/test/ngCookies/cookieWriterSpec.js +++ b/test/ngCookies/cookieWriterSpec.js @@ -132,6 +132,7 @@ describe('$$cookieWriter', function() { describe('cookie options', function() { var fakeDocument, $$cookieWriter; + var isUndefined = angular.isUndefined; function getLastCookieAssignment(key) { return fakeDocument[0].cookie diff --git a/test/ngMock/angular-mocksSpec.js b/test/ngMock/angular-mocksSpec.js index 9dd950f51adc..f8777c517a70 100644 --- a/test/ngMock/angular-mocksSpec.js +++ b/test/ngMock/angular-mocksSpec.js @@ -1,7 +1,9 @@ 'use strict'; describe('ngMock', function() { + var noop = angular.noop; + var extend = angular.extend; describe('TzDate', function() { @@ -1107,11 +1109,13 @@ describe('ngMock', function() { var mock = { log: 'module' }; beforeEach(function() { + angular.module('stringRefModule', []).service('stringRef', function() {}); + module({ 'service': mock, 'other': { some: 'replacement'} }, - 'ngResource', + 'stringRefModule', function($provide) { $provide.value('example', 'win'); } ); }); @@ -1130,9 +1134,9 @@ describe('ngMock', function() { }); it('should integrate with string and function', function() { - inject(function(service, $resource, example) { + inject(function(service, stringRef, example) { expect(service).toEqual(mock); - expect($resource).toBeDefined(); + expect(stringRef).toBeDefined(); expect(example).toEqual('win'); }); }); @@ -2833,6 +2837,10 @@ describe('ngMock', function() { describe('ngMockE2E', function() { + + var noop = angular.noop; + var extend = angular.extend; + describe('$httpBackend', function() { var hb, realHttpBackend, realHttpBackendBrowser, $http, callback; @@ -3175,7 +3183,7 @@ describe('ngMockE2E', function() { if (!browserSupportsCssAnimations()) return; - var element = jqLite('
'); + var element = angular.element('
'); $rootElement.append(element); // Make sure the animation has valid $animateCss options diff --git a/test/ngResource/resourceSpec.js b/test/ngResource/resourceSpec.js index 077281a134ba..bfdac9c0b289 100644 --- a/test/ngResource/resourceSpec.js +++ b/test/ngResource/resourceSpec.js @@ -1,6 +1,8 @@ 'use strict'; describe('resource', function() { + var noop = angular.noop; + var extend = angular.extend; describe('basic usage', function() { var $resource, CreditCard, callback, $httpBackend, resourceProvider, $q; From fd28edfc5097febbf2b01119ab013832eaa96fe5 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Thu, 11 Oct 2018 19:40:33 +0300 Subject: [PATCH 5/6] refactor(ngAnimateSwap): remove unnecessary `inject()` from tests --- test/ngAnimate/ngAnimateSwapSpec.js | 54 +++++++++++++---------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/test/ngAnimate/ngAnimateSwapSpec.js b/test/ngAnimate/ngAnimateSwapSpec.js index 4cfedce7e0ce..c5d5392e3d33 100644 --- a/test/ngAnimate/ngAnimateSwapSpec.js +++ b/test/ngAnimate/ngAnimateSwapSpec.js @@ -20,7 +20,7 @@ describe('ngAnimateSwap', function() { })); - it('should render a new container when the expression changes', inject(function() { + it('should render a new container when the expression changes', function() { element = $compile('
{{ exp }}
')($rootScope); $rootScope.$digest(); @@ -40,9 +40,9 @@ describe('ngAnimateSwap', function() { expect(third.textContent).toBe('super'); expect(third).not.toEqual(second); expect(second.parentNode).toBeFalsy(); - })); + }); - it('should render a new container only when the expression property changes', inject(function() { + it('should render a new container only when the expression property changes', function() { element = $compile('
{{ exp.value }}
')($rootScope); $rootScope.exp = { prop: 'hello', @@ -66,9 +66,9 @@ describe('ngAnimateSwap', function() { var three = element.find('div')[0]; expect(three.textContent).toBe('planet'); expect(three).not.toBe(two); - })); + }); - it('should watch the expression as a collection', inject(function() { + it('should watch the expression as a collection', function() { element = $compile('
{{ exp.a }} {{ exp.b }} {{ exp.c }}
')($rootScope); $rootScope.exp = { a: 1, @@ -99,26 +99,24 @@ describe('ngAnimateSwap', function() { var four = element.find('div')[0]; expect(four.textContent.trim()).toBe('4'); expect(four).not.toEqual(three); - })); + }); they('should consider $prop as a falsy value', [false, undefined, null], function(value) { - inject(function() { - element = $compile('
{{ value }}
')($rootScope); - $rootScope.value = true; - $rootScope.$digest(); + element = $compile('
{{ value }}
')($rootScope); + $rootScope.value = true; + $rootScope.$digest(); - var one = element.find('div')[0]; - expect(one).toBeTruthy(); + var one = element.find('div')[0]; + expect(one).toBeTruthy(); - $rootScope.value = value; - $rootScope.$digest(); + $rootScope.value = value; + $rootScope.$digest(); - var two = element.find('div')[0]; - expect(two).toBeFalsy(); - }); + var two = element.find('div')[0]; + expect(two).toBeFalsy(); }); - it('should consider "0" as a truthy value', inject(function() { + it('should consider "0" as a truthy value', function() { element = $compile('
{{ value }}
')($rootScope); $rootScope.$digest(); @@ -130,9 +128,9 @@ describe('ngAnimateSwap', function() { var two = element.find('div')[0]; expect(two).toBeTruthy(); - })); + }); - it('should create a new (non-isolate) scope for each inserted clone', inject(function() { + it('should create a new (non-isolate) scope for each inserted clone', function() { var parentScope = $rootScope.$new(); parentScope.foo = 'bar'; @@ -147,9 +145,9 @@ describe('ngAnimateSwap', function() { expect(scopeTwo.foo).toBe('bar'); expect(scopeOne).not.toBe(scopeTwo); - })); + }); - it('should destroy the previous scope when removing the element', inject(function() { + it('should destroy the previous scope when removing the element', function() { element = $compile('
{{ value }}
')($rootScope); $rootScope.$apply('value = 1'); @@ -166,9 +164,9 @@ describe('ngAnimateSwap', function() { // Removing the old element (without inserting a new one). $rootScope.$apply('value = null'); expect(scopeTwo.$$destroyed).toBe(true); - })); + }); - it('should destroy the previous scope when swapping elements', inject(function() { + it('should destroy the previous scope when swapping elements', function() { element = $compile('
{{ value }}
')($rootScope); $rootScope.$apply('value = 1'); @@ -177,13 +175,11 @@ describe('ngAnimateSwap', function() { $rootScope.$apply('value = 2'); expect(scopeOne.$$destroyed).toBe(true); - })); + }); describe('animations', function() { - it('should trigger a leave animation followed by an enter animation upon swap', - inject(function() { - + it('should trigger a leave animation followed by an enter animation upon swap',function() { element = $compile('
{{ exp }}
')($rootScope); $rootScope.exp = 1; $rootScope.$digest(); @@ -208,6 +204,6 @@ describe('ngAnimateSwap', function() { var forth = $animate.queue.shift(); expect(forth.event).toBe('leave'); expect($animate.queue.length).toBe(0); - })); + }); }); }); From 3e380325d8f64f30a6715578b117c6c161aee57b Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Thu, 11 Oct 2018 19:28:29 +0300 Subject: [PATCH 6/6] fix(ngAnimateSwap): make it compatible with `ngIf` on the same element Previously, both `ngAnimateSwap` and `ngIf` had a priority of 600, which meant that (while both are [terminal directives][1]) they were executed on top of each other (essentially messing each other's comment node). This commit fixes it, by giving `ngAnimateSwap` a priority of 550, which is lower than `ngIf` but still higher than other directives. For reference, here is a list of built-in directive per priority: ``` -400: ngInclude, ngView -1: ngRef 1: ngMessage, ngMessageDefault, ngMessageExp, ngModel, select 10: ngModelOptions 99: ngHref, ngSrc, ngSrcset 100: attr interpolation, ngChecked, ngDisabled, ngList, ngMax, ngMaxlength, ngMin, ngMinlength, ngModel (aria), ngMultiple, ngOpen, ngPattern, ngProp*, ngReadonly, ngRequired, ngSelected, ngStep, ngValue, option 400: ngInclude, ngView 450: ngInit 500: ngController 600: ngAnimateSwap, ngIf 1000: ngNonBindable, ngRepeat 1200: ngSwitchDefault, ngSwitchWhen ``` [1]: https://docs.angularjs.org/api/ng/service/$compile#-terminal- Fixes #16616 Closes #16729 --- src/ngAnimate/ngAnimateSwap.js | 3 ++- test/ngAnimate/ngAnimateSwapSpec.js | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/ngAnimate/ngAnimateSwap.js b/src/ngAnimate/ngAnimateSwap.js index 83c128bb8944..ebb98d7d53d4 100644 --- a/src/ngAnimate/ngAnimateSwap.js +++ b/src/ngAnimate/ngAnimateSwap.js @@ -92,7 +92,8 @@ var ngAnimateSwapDirective = ['$animate', function($animate) { restrict: 'A', transclude: 'element', terminal: true, - priority: 600, // we use 600 here to ensure that the directive is caught before others + priority: 550, // We use 550 here to ensure that the directive is caught before others, + // but after `ngIf` (at priority 600). link: function(scope, $element, attrs, ctrl, $transclude) { var previousElement, previousScope; scope.$watchCollection(attrs.ngAnimateSwap || attrs['for'], function(value) { diff --git a/test/ngAnimate/ngAnimateSwapSpec.js b/test/ngAnimate/ngAnimateSwapSpec.js index c5d5392e3d33..8d6ebf866cae 100644 --- a/test/ngAnimate/ngAnimateSwapSpec.js +++ b/test/ngAnimate/ngAnimateSwapSpec.js @@ -177,6 +177,29 @@ describe('ngAnimateSwap', function() { expect(scopeOne.$$destroyed).toBe(true); }); + it('should work with `ngIf` on the same element', function() { + var tmpl = '
{{ exp }}
'; + element = $compile(tmpl)($rootScope); + $rootScope.$digest(); + + var first = element.find('div')[0]; + expect(first).toBeFalsy(); + + $rootScope.exp = 'yes'; + $rootScope.$digest(); + + var second = element.find('div')[0]; + expect(second.textContent).toBe('yes'); + + $rootScope.exp = 'super'; + $rootScope.$digest(); + + var third = element.find('div')[0]; + expect(third.textContent).toBe('super'); + expect(third).not.toEqual(second); + expect(second.parentNode).toBeFalsy(); + }); + describe('animations', function() { it('should trigger a leave animation followed by an enter animation upon swap',function() {