From ec1c5dfaee32f9638cedd28bb96bbbecce9d0cf0 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Wed, 9 May 2012 19:27:15 -0400 Subject: fix(jqLite): .data()/.bind() memory leak Since angular attaches scope/injector/controller into DOM it should clean up after itself. No need to complain about memory leaks, since they can only happened on detached DOM. Detached DOM would only be in tests, since in production the DOM would be attached to render tree and removal would automatically clear memory. --- test/jqLiteSpec.js | 30 ++++++++++++++++++++++++++++-- test/ngMock/angular-mocksSpec.js | 40 ++++++++++++++++++++++++++++++++++++++++ test/testabilityPatch.js | 1 - 3 files changed, 68 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/jqLiteSpec.js b/test/jqLiteSpec.js index f159e08f..406b7a5a 100644 --- a/test/jqLiteSpec.js +++ b/test/jqLiteSpec.js @@ -291,11 +291,37 @@ describe('jqLite', function() { expect(element.data()).toEqual({meLike: 'turtles', youLike: 'carrots', existing: 'val'}); expect(element.data()).toBe(oldData); // merge into the old object }); + + describe('data cleanup', function() { + it('should remove data on element removal', function() { + var div = jqLite('
text
'), + span = div.find('span'); + + span.data('name', 'angular'); + span.remove(); + expect(span.data('name')).toBeUndefined(); + }); + + it('should remove event listeners on element removal', function() { + var div = jqLite('
text
'), + span = div.find('span'), + log = ''; + + span.bind('click', function() { log+= 'click;'}); + browserTrigger(span); + expect(log).toEqual('click;'); + + span.remove(); + + browserTrigger(span); + expect(log).toEqual('click;'); + }); + }); }); describe('attr', function() { - it('shoul read write and remove attr', function() { + it('should read write and remove attr', function() { var selector = jqLite([a, b]); expect(selector.attr('prop', 'value')).toEqual(selector); @@ -667,7 +693,7 @@ describe('jqLite', function() { var jWindow = jqLite(window).bind('hashchange', function() { log = 'works!'; }); - eventFn({}); + eventFn({type: 'hashchange'}); expect(log).toEqual('works!'); dealoc(jWindow); }); diff --git a/test/ngMock/angular-mocksSpec.js b/test/ngMock/angular-mocksSpec.js index 22c91a4d..88946ab9 100644 --- a/test/ngMock/angular-mocksSpec.js +++ b/test/ngMock/angular-mocksSpec.js @@ -148,6 +148,7 @@ describe('ngMock', function() { }); }); + describe('$log', function() { var $log; beforeEach(inject(['$log', function(log) { @@ -229,6 +230,7 @@ describe('ngMock', function() { }); }); + describe('defer', function() { var browser, log; beforeEach(inject(function($browser) { @@ -341,6 +343,44 @@ describe('ngMock', function() { }); }); + + describe('angular.mock.clearDataCache', function() { + function keys(obj) { + var keys = []; + for(var key in obj) { + if (obj.hasOwnProperty(key)) keys.push(key); + } + return keys.sort(); + } + + it('should remove data', function() { + expect(angular.element.cache).toEqual({}); + var div = angular.element('
'); + div.data('name', 'angular'); + expect(keys(angular.element.cache)).not.toEqual([]); + angular.mock.clearDataCache(); + expect(keys(angular.element.cache)).toEqual([]); + }); + + it('should deregister event handlers', function() { + expect(keys(angular.element.cache)).toEqual([]); + + var div = angular.element('
'); + + div.bind('click', angular.noop); + div.bind('mousemove', angular.noop); + div.data('some', 'data'); + expect(keys(angular.element.cache).length).toBe(1); + + angular.mock.clearDataCache(); + expect(keys(angular.element.cache)).toEqual([]); + expect(div.data('some')).toBeUndefined(); + + div.remove(); + }); + }); + + describe('jasmine module and inject', function(){ var log; diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index 12724192..f033dda2 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -42,7 +42,6 @@ afterEach(function() { var count = 0; forEachSorted(jqCache, function(value, key){ count ++; - delete jqCache[key]; forEach(value, function(value, key){ if (value.$element) { dump('LEAK', key, value.$id, sortedHtml(value.$element)); -- cgit v1.2.3