From a34944db216e9e2b8e658a31ff9a36a4622cd34e Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Fri, 9 Sep 2016 20:42:45 +0300 Subject: [PATCH 1/3] test(hashKey): add tests for `hashKey()` --- test/ApiSpecs.js | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/test/ApiSpecs.js b/test/ApiSpecs.js index 349dfe17e662..789625419ba8 100644 --- a/test/ApiSpecs.js +++ b/test/ApiSpecs.js @@ -1,6 +1,72 @@ 'use strict'; describe('api', function() { + describe('hashKey()', function() { + it('should use an existing `$$hashKey`', function() { + var obj = {$$hashKey: 'foo'}; + expect(hashKey(obj)).toBe('foo'); + }); + + it('should support a function as `$$hashKey` (and call it)', function() { + var obj = {$$hashKey: valueFn('foo')}; + expect(hashKey(obj)).toBe('foo'); + }); + + it('should create a new `$$hashKey` if none exists (and return it)', function() { + var obj = {}; + expect(hashKey(obj)).toBe(obj.$$hashKey); + expect(obj.$$hashKey).toBeDefined(); + }); + + it('should create appropriate `$$hashKey`s for primitive values', function() { + expect(hashKey(undefined)).toBe(hashKey(undefined)); + expect(hashKey(null)).toBe(hashKey(null)); + expect(hashKey(null)).not.toBe(hashKey(undefined)); + expect(hashKey(true)).toBe(hashKey(true)); + expect(hashKey(false)).toBe(hashKey(false)); + expect(hashKey(false)).not.toBe(hashKey(true)); + expect(hashKey(42)).toBe(hashKey(42)); + expect(hashKey(1337)).toBe(hashKey(1337)); + expect(hashKey(1337)).not.toBe(hashKey(42)); + expect(hashKey('foo')).toBe(hashKey('foo')); + expect(hashKey('foo')).not.toBe(hashKey('bar')); + }); + + it('should create appropriate `$$hashKey`s for non-primitive values', function() { + var fn = function() {}; + var arr = []; + var obj = {}; + var date = new Date(); + + expect(hashKey(fn)).toBe(hashKey(fn)); + expect(hashKey(fn)).not.toBe(hashKey(function() {})); + expect(hashKey(arr)).toBe(hashKey(arr)); + expect(hashKey(arr)).not.toBe(hashKey([])); + expect(hashKey(obj)).toBe(hashKey(obj)); + expect(hashKey(obj)).not.toBe(hashKey({})); + expect(hashKey(date)).toBe(hashKey(date)); + expect(hashKey(date)).not.toBe(hashKey(new Date())); + }); + + it('should support a custom `nextUidFn`', function() { + var nextUidFn = jasmine.createSpy('nextUidFn').and.returnValues('foo', 'bar', 'baz', 'qux'); + + var fn = function() {}; + var arr = []; + var obj = {}; + var date = new Date(); + + hashKey(fn, nextUidFn); + hashKey(arr, nextUidFn); + hashKey(obj, nextUidFn); + hashKey(date, nextUidFn); + + expect(fn.$$hashKey).toBe('function:foo'); + expect(arr.$$hashKey).toBe('object:bar'); + expect(obj.$$hashKey).toBe('object:baz'); + expect(date.$$hashKey).toBe('object:qux'); + }); + }); describe('HashMap', function() { it('should do basic crud', function() { From 3a7be65436513de718a5f85e88a8c0022cf1c082 Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Fri, 9 Dec 2016 14:24:00 +0200 Subject: [PATCH 2/3] refactor(*): replace `HashMap` with `NgMap` For the time being, we will be using `NgMap`, which is an API-compatible implementation of native `Map` (for the features required in Angular). This will make it easy to switch to using the native implementations, once they become more stable. Note: At the momntm some native implementations are still buggy (often in subtle ways) and can cause hard-to-debug failures.) --- src/.eslintrc.json | 2 +- src/AngularPublic.js | 4 +- src/apis.js | 91 +++++++++++++++++++------------- src/auto/injector.js | 4 +- src/ng/animate.js | 6 +-- src/ng/directive/select.js | 12 ++--- src/ngAnimate/animateQueue.js | 16 +++--- src/ngAnimate/animation.js | 12 ++--- src/ngMock/angular-mocks.js | 6 --- test/.eslintrc.json | 2 +- test/ApiSpecs.js | 71 ++++++++++--------------- test/auto/injectorSpec.js | 15 +++--- test/helpers/testabilityPatch.js | 19 +++---- test/ng/directive/selectSpec.js | 20 +++---- test/ngAnimate/animateSpec.js | 2 +- test/ngMock/angular-mocksSpec.js | 17 ------ 16 files changed, 139 insertions(+), 160 deletions(-) diff --git a/src/.eslintrc.json b/src/.eslintrc.json index 6022ac25baa9..5c73075f10a8 100644 --- a/src/.eslintrc.json +++ b/src/.eslintrc.json @@ -150,7 +150,7 @@ /* apis.js */ "hashKey": false, - "HashMap": false, + "NgMap": false, /* urlUtils.js */ "urlResolve": false, diff --git a/src/AngularPublic.js b/src/AngularPublic.js index b1b5f332ce8a..b17eb237d15a 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -70,7 +70,6 @@ $$ForceReflowProvider, $InterpolateProvider, $IntervalProvider, - $$HashMapProvider, $HttpProvider, $HttpParamSerializerProvider, $HttpParamSerializerJQLikeProvider, @@ -79,6 +78,7 @@ $jsonpCallbacksProvider, $LocationProvider, $LogProvider, + $$MapProvider, $ParseProvider, $RootScopeProvider, $QProvider, @@ -260,7 +260,7 @@ function publishExternalAPI(angular) { $window: $WindowProvider, $$rAF: $$RAFProvider, $$jqLite: $$jqLiteProvider, - $$HashMap: $$HashMapProvider, + $$Map: $$MapProvider, $$cookieReader: $$CookieReaderProvider }); } diff --git a/src/apis.js b/src/apis.js index 457eb9d1c9b4..856b86e8b709 100644 --- a/src/apis.js +++ b/src/apis.js @@ -1,6 +1,5 @@ 'use strict'; - /** * Computes a hash of an 'obj'. * Hash of a: @@ -33,49 +32,69 @@ function hashKey(obj, nextUidFn) { return key; } -/** - * HashMap which can use objects as keys - */ -function HashMap(array, isolatedUid) { - if (isolatedUid) { - var uid = 0; - this.nextUid = function() { - return ++uid; - }; - } - forEach(array, this.put, this); +// A minimal ES2015 Map implementation. +// Should be bug/feature equivalent to the native implementations of supported browsers +// (for the features required in Angular). +// See https://kangax.github.io/compat-table/es6/#test-Map +var nanKey = Object.create(null); +function NgMapShim() { + this._keys = []; + this._values = []; + this._lastKey = NaN; + this._lastIndex = -1; } -HashMap.prototype = { - /** - * Store key value pair - * @param key key to store can be any type - * @param value value to store can be any type - */ - put: function(key, value) { - this[hashKey(key, this.nextUid)] = value; +NgMapShim.prototype = { + _idx: function(key) { + if (key === this._lastKey) { + return this._lastIndex; + } + this._lastKey = key; + this._lastIndex = this._keys.indexOf(key); + return this._lastIndex; + }, + _transformKey: function(key) { + return isNumberNaN(key) ? nanKey : key; }, - - /** - * @param key - * @returns {Object} the value for the key - */ get: function(key) { - return this[hashKey(key, this.nextUid)]; + key = this._transformKey(key); + var idx = this._idx(key); + if (idx !== -1) { + return this._values[idx]; + } }, + set: function(key, value) { + key = this._transformKey(key); + var idx = this._idx(key); + if (idx === -1) { + idx = this._lastIndex = this._keys.length; + } + this._keys[idx] = key; + this._values[idx] = value; - /** - * Remove the key/value pair - * @param key - */ - remove: function(key) { - var value = this[key = hashKey(key, this.nextUid)]; - delete this[key]; - return value; + // Support: IE11 + // Do not `return this` to simulate the partial IE11 implementation + }, + delete: function(key) { + key = this._transformKey(key); + var idx = this._idx(key); + if (idx === -1) { + return false; + } + this._keys.splice(idx, 1); + this._values.splice(idx, 1); + this._lastKey = NaN; + this._lastIndex = -1; + return true; } }; -var $$HashMapProvider = [/** @this */function() { +// For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations +// are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map` +// implementations get more stable, we can reconsider switching to `window.Map` (when available). +var NgMap = NgMapShim; + +var $$MapProvider = [/** @this */function() { this.$get = [function() { - return HashMap; + return NgMap; }]; }]; diff --git a/src/auto/injector.js b/src/auto/injector.js index 15edc30c7923..9c28e9cad4e5 100644 --- a/src/auto/injector.js +++ b/src/auto/injector.js @@ -649,7 +649,7 @@ function createInjector(modulesToLoad, strictDi) { var INSTANTIATING = {}, providerSuffix = 'Provider', path = [], - loadedModules = new HashMap([], true), + loadedModules = new NgMap(), providerCache = { $provide: { provider: supportObject(provider), @@ -757,7 +757,7 @@ function createInjector(modulesToLoad, strictDi) { var runBlocks = [], moduleFn; forEach(modulesToLoad, function(module) { if (loadedModules.get(module)) return; - loadedModules.put(module, true); + loadedModules.set(module, true); function runInvokeQueue(queue) { var i, ii; diff --git a/src/ng/animate.js b/src/ng/animate.js index 923126eed045..99d8f74ffa0b 100644 --- a/src/ng/animate.js +++ b/src/ng/animate.js @@ -60,7 +60,7 @@ var $$CoreAnimateJsProvider = /** @this */ function() { // this is prefixed with Core since it conflicts with // the animateQueueProvider defined in ngAnimate/animateQueue.js var $$CoreAnimateQueueProvider = /** @this */ function() { - var postDigestQueue = new HashMap(); + var postDigestQueue = new NgMap(); var postDigestElements = []; this.$get = ['$$AnimateRunner', '$rootScope', @@ -139,7 +139,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() { jqLiteRemoveClass(elm, toRemove); } }); - postDigestQueue.remove(element); + postDigestQueue.delete(element); } }); postDigestElements.length = 0; @@ -154,7 +154,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() { if (classesAdded || classesRemoved) { - postDigestQueue.put(element, data); + postDigestQueue.set(element, data); postDigestElements.push(element); if (postDigestElements.length === 1) { diff --git a/src/ng/directive/select.js b/src/ng/directive/select.js index 55381ff72835..b3de78164fcb 100644 --- a/src/ng/directive/select.js +++ b/src/ng/directive/select.js @@ -16,7 +16,7 @@ var SelectController = ['$element', '$scope', /** @this */ function($element, $scope) { var self = this, - optionsMap = new HashMap(); + optionsMap = new NgMap(); self.selectValueMap = {}; // Keys are the hashed values, values the original values @@ -137,7 +137,7 @@ var SelectController = self.emptyOption = element; } var count = optionsMap.get(value) || 0; - optionsMap.put(value, count + 1); + optionsMap.set(value, count + 1); // Only render at the end of a digest. This improves render performance when many options // are added during a digest and ensures all relevant options are correctly marked as selected scheduleRender(); @@ -148,13 +148,13 @@ var SelectController = var count = optionsMap.get(value); if (count) { if (count === 1) { - optionsMap.remove(value); + optionsMap.delete(value); if (value === '') { self.hasEmptyOption = false; self.emptyOption = undefined; } } else { - optionsMap.put(value, count - 1); + optionsMap.set(value, count - 1); } } }; @@ -606,9 +606,9 @@ var selectDirective = function() { // Write value now needs to set the selected property of each matching option selectCtrl.writeValue = function writeMultipleValue(value) { - var items = new HashMap(value); forEach(element.find('option'), function(option) { - option.selected = isDefined(items.get(option.value)) || isDefined(items.get(selectCtrl.selectValueMap[option.value])); + option.selected = !!value && (includes(value, option.value) || + includes(value, selectCtrl.selectValueMap[option.value])); }); }; diff --git a/src/ngAnimate/animateQueue.js b/src/ngAnimate/animateQueue.js index bf99c58d6a05..c0de81d4db18 100644 --- a/src/ngAnimate/animateQueue.js +++ b/src/ngAnimate/animateQueue.js @@ -100,15 +100,15 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA); }); - this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap', + this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$Map', '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow', '$$isDocumentHidden', - function($$rAF, $rootScope, $rootElement, $document, $$HashMap, + function($$rAF, $rootScope, $rootElement, $document, $$Map, $$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow, $$isDocumentHidden) { - var activeAnimationsLookup = new $$HashMap(); - var disabledElementsLookup = new $$HashMap(); + var activeAnimationsLookup = new $$Map(); + var disabledElementsLookup = new $$Map(); var animationsEnabled = null; function postDigestTaskFactory() { @@ -294,7 +294,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate bool = !disabledElementsLookup.get(node); } else { // (element, bool) - Element setter - disabledElementsLookup.put(node, !bool); + disabledElementsLookup.set(node, !bool); } } } @@ -597,7 +597,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate animationDetails.runner.end(); /* falls through */ case PRE_DIGEST_STATE: - activeAnimationsLookup.remove(child); + activeAnimationsLookup.delete(child); break; } } @@ -607,7 +607,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate function clearElementAnimationState(element) { var node = getDomNode(element); node.removeAttribute(NG_ANIMATE_ATTR_NAME); - activeAnimationsLookup.remove(node); + activeAnimationsLookup.delete(node); } function isMatchingElement(nodeOrElmA, nodeOrElmB) { @@ -717,7 +717,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate var newValue = oldValue ? extend(oldValue, details) : details; - activeAnimationsLookup.put(node, newValue); + activeAnimationsLookup.set(node, newValue); } }]; }]; diff --git a/src/ngAnimate/animation.js b/src/ngAnimate/animation.js index 51f104ed7cb8..bd7748114ac2 100644 --- a/src/ngAnimate/animation.js +++ b/src/ngAnimate/animation.js @@ -21,21 +21,21 @@ var $$AnimationProvider = ['$animateProvider', /** @this */ function($animatePro return element.data(RUNNER_STORAGE_KEY); } - this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler', - function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap, $$rAFScheduler) { + this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$Map', '$$rAFScheduler', + function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$Map, $$rAFScheduler) { var animationQueue = []; var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); function sortAnimations(animations) { var tree = { children: [] }; - var i, lookup = new $$HashMap(); + var i, lookup = new $$Map(); - // this is done first beforehand so that the hashmap + // this is done first beforehand so that the map // is filled with a list of the elements that will be animated for (i = 0; i < animations.length; i++) { var animation = animations[i]; - lookup.put(animation.domNode, animations[i] = { + lookup.set(animation.domNode, animations[i] = { domNode: animation.domNode, fn: animation.fn, children: [] @@ -54,7 +54,7 @@ var $$AnimationProvider = ['$animateProvider', /** @this */ function($animatePro var elementNode = entry.domNode; var parentNode = elementNode.parentNode; - lookup.put(elementNode, entry); + lookup.set(elementNode, entry); var parentEntry; while (parentNode) { diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js index 50fff01fb012..f1fb063afe50 100644 --- a/src/ngMock/angular-mocks.js +++ b/src/ngMock/angular-mocks.js @@ -2964,12 +2964,6 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) { delete fn.$inject; }); - angular.forEach(currentSpec.$modules, function(module) { - if (module && module.$$hashKey) { - module.$$hashKey = undefined; - } - }); - currentSpec.$injector = null; currentSpec.$modules = null; currentSpec.$providerInjector = null; diff --git a/test/.eslintrc.json b/test/.eslintrc.json index 4ac15d192fa3..3831a78d8f47 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -143,7 +143,7 @@ /* apis.js */ "hashKey": false, - "HashMap": false, + "NgMapShim": false, /* urlUtils.js */ "urlResolve": false, diff --git a/test/ApiSpecs.js b/test/ApiSpecs.js index 789625419ba8..bc87caa07105 100644 --- a/test/ApiSpecs.js +++ b/test/ApiSpecs.js @@ -68,55 +68,42 @@ describe('api', function() { }); }); - describe('HashMap', function() { + describe('NgMapShim', function() { it('should do basic crud', function() { - var map = new HashMap(); - var key = {}; - var value1 = {}; - var value2 = {}; - map.put(key, value1); - map.put(key, value2); - expect(map.get(key)).toBe(value2); - expect(map.get({})).toBeUndefined(); - expect(map.remove(key)).toBe(value2); - expect(map.get(key)).toBeUndefined(); + var map = new NgMapShim(); + var keys = [{}, {}, {}]; + var values = [{}, {}, {}]; + + map.set(keys[0], values[1]); + map.set(keys[0], values[0]); + expect(map.get(keys[0])).toBe(values[0]); + expect(map.get(keys[1])).toBeUndefined(); + + map.set(keys[1], values[1]); + map.set(keys[2], values[2]); + expect(map.delete(keys[0])).toBe(true); + expect(map.delete(keys[0])).toBe(false); + + expect(map.get(keys[0])).toBeUndefined(); + expect(map.get(keys[1])).toBe(values[1]); + expect(map.get(keys[2])).toBe(values[2]); }); - it('should init from an array', function() { - var map = new HashMap(['a','b']); - expect(map.get('a')).toBe(0); - expect(map.get('b')).toBe(1); - expect(map.get('c')).toBeUndefined(); - }); + it('should be able to deal with `NaN` keys', function() { + var map = new NgMapShim(); - it('should maintain hashKey for object keys', function() { - var map = new HashMap(); - var key = {}; - map.get(key); - expect(key.$$hashKey).toBeDefined(); - }); + map.set('NaN', 'foo'); + map.set(NaN, 'bar'); + map.set(NaN, 'baz'); - it('should maintain hashKey for function keys', function() { - var map = new HashMap(); - var key = function() {}; - map.get(key); - expect(key.$$hashKey).toBeDefined(); - }); + expect(map.get('NaN')).toBe('foo'); + expect(map.get(NaN)).toBe('baz'); - it('should share hashKey between HashMap by default', function() { - var map1 = new HashMap(), map2 = new HashMap(); - var key1 = {}, key2 = {}; - map1.get(key1); - map2.get(key2); - expect(key1.$$hashKey).not.toEqual(key2.$$hashKey); - }); + expect(map.delete(NaN)).toBe(true); + expect(map.get(NaN)).toBeUndefined(); + expect(map.get('NaN')).toBe('foo'); - it('should maintain hashKey per HashMap if flag is passed', function() { - var map1 = new HashMap([], true), map2 = new HashMap([], true); - var key1 = {}, key2 = {}; - map1.get(key1); - map2.get(key2); - expect(key1.$$hashKey).toEqual(key2.$$hashKey); + expect(map.delete(NaN)).toBe(false); }); }); }); diff --git a/test/auto/injectorSpec.js b/test/auto/injectorSpec.js index 5a558d5631e6..2bb28841a27d 100644 --- a/test/auto/injectorSpec.js +++ b/test/auto/injectorSpec.js @@ -46,14 +46,13 @@ describe('injector', function() { it('should resolve dependency graph and instantiate all services just once', function() { var log = []; -// s1 -// / | \ -// / s2 \ -// / / | \ \ -// /s3 < s4 > s5 -// // -// s6 - + // s1 + // / | \ + // / s2 \ + // / / | \ \ + // /s3 < s4 > s5 + // // + // s6 providers('s1', function() { log.push('s1'); return {}; }, {$inject: ['s2', 's5', 's6']}); providers('s2', function() { log.push('s2'); return {}; }, {$inject: ['s3', 's4', 's5']}); diff --git a/test/helpers/testabilityPatch.js b/test/helpers/testabilityPatch.js index d589ac2c67df..9f9d9b5dd2ec 100644 --- a/test/helpers/testabilityPatch.js +++ b/test/helpers/testabilityPatch.js @@ -54,17 +54,14 @@ beforeEach(function() { afterEach(function() { var count, cache; - // both of these nodes are persisted across tests - // and therefore the hashCode may be cached - var node = window.document.querySelector('html'); - if (node) { - node.$$hashKey = null; - } - var bod = window.document.body; - if (bod) { - bod.$$hashKey = null; - } - window.document.$$hashKey = null; + // These Nodes are persisted across tests. + // They used to be assigned a `$$hashKey` when animated, but not any more. + var doc = window.document; + var html = doc.querySelector('html'); + var body = doc.body; + expect(doc.$$hashKey).toBeFalsy(); + expect(html && html.$$hashKey).toBeFalsy(); + expect(body && body.$$hashKey).toBeFalsy(); if (this.$injector) { var $rootScope = this.$injector.get('$rootScope'); diff --git a/test/ng/directive/selectSpec.js b/test/ng/directive/selectSpec.js index 6990cfeb9eb9..9596d3b4d792 100644 --- a/test/ng/directive/selectSpec.js +++ b/test/ng/directive/selectSpec.js @@ -1529,8 +1529,8 @@ describe('select', function() { 'number:1', 'boolean:true', 'object:null', + 'object:3', 'object:4', - 'object:5', 'number:NaN' ); @@ -1555,7 +1555,7 @@ describe('select', function() { browserTrigger(element, 'change'); var arrayVal = ['a']; - arrayVal.$$hashKey = 'object:5'; + arrayVal.$$hashKey = 'object:4'; expect(scope.selected).toEqual([ 'string', @@ -1563,7 +1563,7 @@ describe('select', function() { 1, true, null, - {prop: 'value', $$hashKey: 'object:4'}, + {prop: 'value', $$hashKey: 'object:3'}, arrayVal, NaN ]); @@ -1876,10 +1876,10 @@ describe('select', function() { scope.$digest(); optionElements = element.find('option'); - expect(element.val()).toBe(prop === 'ngValue' ? 'object:4' : 'C'); + expect(element.val()).toBe(prop === 'ngValue' ? 'object:3' : 'C'); expect(optionElements.length).toEqual(3); expect(optionElements[2].selected).toBe(true); - expect(scope.obj.value).toEqual(prop === 'ngValue' ? {name: 'C', $$hashKey: 'object:4'} : 'C'); + expect(scope.obj.value).toEqual(prop === 'ngValue' ? {name: 'C', $$hashKey: 'object:3'} : 'C'); }); @@ -2188,9 +2188,9 @@ describe('select', function() { expect(optionElements.length).toEqual(4); expect(scope.obj.value).toEqual(prop === 'ngValue' ? [ - {name: 'A', $$hashKey: 'object:4', disabled: true}, - {name: 'C', $$hashKey: 'object:6'}, - {name: 'D', $$hashKey: 'object:7', disabled: true} + {name: 'A', $$hashKey: 'object:3', disabled: true}, + {name: 'C', $$hashKey: 'object:5'}, + {name: 'D', $$hashKey: 'object:6', disabled: true} ] : ['A', 'C', 'D'] ); @@ -2242,13 +2242,13 @@ describe('select', function() { scope.$digest(); optionElements = element.find('option'); - expect(element.val()).toEqual(prop === 'ngValue' ? ['object:4', 'object:5'] : ['B', 'C']); + expect(element.val()).toEqual(prop === 'ngValue' ? ['object:4', 'object:7'] : ['B', 'C']); expect(optionElements.length).toEqual(3); expect(optionElements[1].selected).toBe(true); expect(optionElements[2].selected).toBe(true); expect(scope.obj.value).toEqual(prop === 'ngValue' ? [{ name: 'B', $$hashKey: 'object:4'}, - {name: 'C', $$hashKey: 'object:5'}] : + {name: 'C', $$hashKey: 'object:7'}] : ['B', 'C']); }); diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index e1f37c4e4462..f2705478a8a7 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -1385,7 +1385,7 @@ describe('animations', function() { expect(capturedAnimation[1]).toBe('leave'); // $$hashKey causes comparison issues - expect(element.parent()[0]).toEqual(parent[0]); + expect(element.parent()[0]).toBe(parent[0]); options = capturedAnimation[2]; expect(options.addClass).toEqual('pink'); diff --git a/test/ngMock/angular-mocksSpec.js b/test/ngMock/angular-mocksSpec.js index 8e696f58072a..c89ffd087536 100644 --- a/test/ngMock/angular-mocksSpec.js +++ b/test/ngMock/angular-mocksSpec.js @@ -795,23 +795,6 @@ describe('ngMock', function() { }); }); - describe('module cleanup', function() { - function testFn() { - - } - - it('should add hashKey to module function', function() { - module(testFn); - inject(function() { - expect(testFn.$$hashKey).toBeDefined(); - }); - }); - - it('should cleanup hashKey after previous test', function() { - expect(testFn.$$hashKey).toBeUndefined(); - }); - }); - describe('$inject cleanup', function() { function testFn() { From bc26d061d3d2d846107c61560ce9976462b70248 Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Wed, 25 Jan 2017 00:15:30 +0200 Subject: [PATCH 3/3] fixup! refactor(*): replace `HashMap` with `NgMap` --- test/helpers/testabilityPatch.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/helpers/testabilityPatch.js b/test/helpers/testabilityPatch.js index 9f9d9b5dd2ec..663c63d7572b 100644 --- a/test/helpers/testabilityPatch.js +++ b/test/helpers/testabilityPatch.js @@ -55,7 +55,9 @@ afterEach(function() { var count, cache; // These Nodes are persisted across tests. - // They used to be assigned a `$$hashKey` when animated, but not any more. + // They used to be assigned a `$$hashKey` when animated, which we needed to clear after each test + // to avoid affecting other tests. This is no longer the case, so we are just ensuring that there + // is indeed no `$$hachKey` on them. var doc = window.document; var html = doc.querySelector('html'); var body = doc.body;