From 950c6f3712062d22afeb93ee31b21b3b809be397 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 7 Jan 2014 16:22:05 +1000 Subject: [PATCH 1/6] watchCollection: Return correct oldCollection argument to listener --- src/ng/rootScope.js | 115 +++++++++++++++++++++++---------------- test/ng/rootScopeSpec.js | 24 +++++++- 2 files changed, 91 insertions(+), 48 deletions(-) diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index 8259bf881bd9..a12154902802 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -416,79 +416,102 @@ function $RootScopeProvider(){ var self = this; var oldValue; var newValue; - var changeDetected = 0; + var changeDetected; + var changeFlipFlop = 0; var objGetter = $parse(obj); + var internalValue; // Holds simple value or reference to internalArray or internalObject var internalArray = []; var internalObject = {}; - var oldLength = 0; + var internalLength = 0; function $watchCollectionWatch() { + var newLength, key, i; + newValue = objGetter(self); - var newLength, key; + changeDetected = 0; if (!isObject(newValue)) { - if (oldValue !== newValue) { - oldValue = newValue; + if (internalValue !== newValue) { + oldValue = copy(internalValue); + internalValue = newValue; changeDetected++; } } else if (isArrayLike(newValue)) { - if (oldValue !== internalArray) { - // we are transitioning from something which was not an array into array. - oldValue = internalArray; - oldLength = oldValue.length = 0; - changeDetected++; - } - newLength = newValue.length; - - if (oldLength !== newLength) { - // if lengths do not match we need to trigger change notification + if (internalValue !== internalArray) { + // we are transitioning from something which was not an array into array. changeDetected++; - oldValue.length = oldLength = newLength; - } - // copy the items to oldValue and look for changes. - for (var i = 0; i < newLength; i++) { - if (oldValue[i] !== newValue[i]) { + } else { + if (internalLength !== newLength) { + // if lengths do not match we need to trigger change notification changeDetected++; - oldValue[i] = newValue[i]; + } else { + // look for item changes + for (i = 0; i < newLength; i++) { + if (internalValue[i] !== newValue[i]) { + changeDetected++; + break; + } + } + } + } + if (changeDetected) { + // deep copy for report to listener + oldValue = copy(internalValue); + // copy the items to array cache + internalValue = internalArray; + internalValue.length = internalLength = newLength; + for (i = 0; i < newLength; i++) { + internalValue[i] = newValue[i]; } } } else { - if (oldValue !== internalObject) { - // we are transitioning from something which was not an object into object. - oldValue = internalObject = {}; - oldLength = 0; + if (internalValue !== internalObject) { + // we are transitioning from something which was not an object into object changeDetected++; - } - // copy the items to oldValue and look for changes. - newLength = 0; - for (key in newValue) { - if (newValue.hasOwnProperty(key)) { - newLength++; - if (oldValue.hasOwnProperty(key)) { - if (oldValue[key] !== newValue[key]) { + } else { + // look for item changes + newLength = 0; + for (key in newValue) { + if (newValue.hasOwnProperty(key)) { + newLength++; + if (! (internalValue.hasOwnProperty(key) && + internalValue[key] === newValue[key])) { changeDetected++; - oldValue[key] = newValue[key]; + break; } - } else { - oldLength++; - oldValue[key] = newValue[key]; - changeDetected++; } } + if (internalLength !== newLength) { + changeDetected++; + } } - if (oldLength > newLength) { - // we used to have more keys, need to find them and destroy them. - changeDetected++; - for(key in oldValue) { - if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) { - oldLength--; - delete oldValue[key]; + if (changeDetected) { + // deep copy for report to listener + oldValue = copy(internalValue); + // copy the items to object cache + internalValue = internalObject; + internalLength = 0; + for (key in newValue) { + if (newValue.hasOwnProperty(key)) { + internalLength++; + internalValue[key] = newValue[key]; + } + } + for(key in internalValue) { + if (internalValue.hasOwnProperty(key) && + !newValue.hasOwnProperty(key)) { + delete internalValue[key]; } } } } - return changeDetected; + + if (changeDetected) { + changeFlipFlop = 1 - changeFlipFlop; + } + + return changeFlipFlop; } function $watchCollectionAction() { diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index e47111e2d0eb..71368d2da5df 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -456,13 +456,15 @@ describe('Scope', function() { describe('$watchCollection', function() { - var log, $rootScope, deregister; + var log, logb, $rootScope, deregister; beforeEach(inject(function(_$rootScope_) { log = []; + logb = []; $rootScope = _$rootScope_; - deregister = $rootScope.$watchCollection('obj', function logger(obj) { + deregister = $rootScope.$watchCollection('obj', function logger(obj, objb) { log.push(toJson(obj)); + logb.push(toJson(objb)); }); })); @@ -495,22 +497,27 @@ describe('Scope', function() { $rootScope.obj = 'test'; $rootScope.$digest(); expect(log).toEqual(['"test"']); + expect(logb).toEqual([undefined]); $rootScope.obj = []; $rootScope.$digest(); expect(log).toEqual(['"test"', '[]']); + expect(logb).toEqual([undefined, '"test"']); $rootScope.obj = {}; $rootScope.$digest(); expect(log).toEqual(['"test"', '[]', '{}']); + expect(logb).toEqual([undefined, '"test"', '[]']); $rootScope.obj = []; $rootScope.$digest(); expect(log).toEqual(['"test"', '[]', '{}', '[]']); + expect(logb).toEqual([undefined, '"test"', '[]', '{}']); $rootScope.obj = undefined; $rootScope.$digest(); expect(log).toEqual(['"test"', '[]', '{}', '[]', undefined]); + expect(logb).toEqual([undefined, '"test"', '[]', '{}', '[]']); }); @@ -533,22 +540,27 @@ describe('Scope', function() { $rootScope.obj.push('a'); $rootScope.$digest(); expect(log).toEqual(['[]', '["a"]']); + expect(logb).toEqual([undefined, '[]']); $rootScope.obj[0] = 'b'; $rootScope.$digest(); expect(log).toEqual(['[]', '["a"]', '["b"]']); + expect(logb).toEqual([undefined, '[]', '["a"]']); $rootScope.obj.push([]); $rootScope.obj.push({}); log = []; + logb = []; $rootScope.$digest(); expect(log).toEqual(['["b",[],{}]']); + expect(logb).toEqual(['["b"]']); var temp = $rootScope.obj[1]; $rootScope.obj[1] = $rootScope.obj[2]; $rootScope.obj[2] = temp; $rootScope.$digest(); expect(log).toEqual([ '["b",[],{}]', '["b",{},[]]' ]); + expect(logb).toEqual([ '["b"]', '["b",[],{}]']); $rootScope.obj.shift(); log = []; @@ -584,6 +596,7 @@ describe('Scope', function() { $rootScope.obj = {}; $rootScope.$digest(); expect(log).toEqual(['"test"', '{}']); + expect(logb).toEqual([undefined, '"test"']); }); @@ -606,27 +619,34 @@ describe('Scope', function() { $rootScope.obj.a= 'A'; $rootScope.$digest(); expect(log).toEqual(['{}', '{"a":"A"}']); + expect(logb).toEqual([undefined, '{}']); $rootScope.obj.a = 'B'; $rootScope.$digest(); expect(log).toEqual(['{}', '{"a":"A"}', '{"a":"B"}']); + expect(logb).toEqual([undefined, '{}', '{"a":"A"}']); $rootScope.obj.b = []; $rootScope.obj.c = {}; log = []; + logb = []; $rootScope.$digest(); expect(log).toEqual(['{"a":"B","b":[],"c":{}}']); + expect(logb).toEqual(['{"a":"B"}']); var temp = $rootScope.obj.a; $rootScope.obj.a = $rootScope.obj.b; $rootScope.obj.c = temp; $rootScope.$digest(); expect(log).toEqual([ '{"a":"B","b":[],"c":{}}', '{"a":[],"b":[],"c":"B"}' ]); + expect(logb).toEqual([ '{"a":"B"}', '{"a":"B","b":[],"c":{}}']); delete $rootScope.obj.a; log = []; + logb = []; $rootScope.$digest(); expect(log).toEqual([ '{"b":[],"c":"B"}' ]); + expect(logb).toEqual([ '{"a":[],"b":[],"c":"B"}' ]); }); }); }); From ca6776af44a9eff0b90a251fb79441fe8dac901c Mon Sep 17 00:00:00 2001 From: John Murphy Date: Wed, 8 Jan 2014 00:17:13 +1000 Subject: [PATCH 2/6] watchCollection: Ensure that newCollection = oldCollection on first call to listener --- src/ng/rootScope.js | 27 +++++++++++++-------------- test/ng/rootScopeSpec.js | 20 ++++++++++---------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index a12154902802..b88d75bf9741 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -419,11 +419,15 @@ function $RootScopeProvider(){ var changeDetected; var changeFlipFlop = 0; var objGetter = $parse(obj); - var internalValue; // Holds simple value or reference to internalArray or internalObject var internalArray = []; var internalObject = {}; var internalLength = 0; + // Holds simple value or reference to internalArray or internalObject. + // The special initial value is used to ensure that the listener is called + // when the watch is established and that oldValue = newValue. + var internalValue = initWatchVal; + function $watchCollectionWatch() { var newLength, key, i; @@ -432,7 +436,7 @@ function $RootScopeProvider(){ if (!isObject(newValue)) { if (internalValue !== newValue) { - oldValue = copy(internalValue); + oldValue = internalValue; internalValue = newValue; changeDetected++; } @@ -456,10 +460,9 @@ function $RootScopeProvider(){ } } if (changeDetected) { - // deep copy for report to listener - oldValue = copy(internalValue); + oldValue = internalValue; // copy the items to array cache - internalValue = internalArray; + internalValue = internalArray = []; internalValue.length = internalLength = newLength; for (i = 0; i < newLength; i++) { internalValue[i] = newValue[i]; @@ -487,10 +490,9 @@ function $RootScopeProvider(){ } } if (changeDetected) { - // deep copy for report to listener - oldValue = copy(internalValue); + oldValue = internalValue; // copy the items to object cache - internalValue = internalObject; + internalValue = internalObject = {}; internalLength = 0; for (key in newValue) { if (newValue.hasOwnProperty(key)) { @@ -498,17 +500,14 @@ function $RootScopeProvider(){ internalValue[key] = newValue[key]; } } - for(key in internalValue) { - if (internalValue.hasOwnProperty(key) && - !newValue.hasOwnProperty(key)) { - delete internalValue[key]; - } - } } } if (changeDetected) { changeFlipFlop = 1 - changeFlipFlop; + if (oldValue === initWatchVal) { + oldValue = internalValue; + } } return changeFlipFlop; diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index 71368d2da5df..a8a606f542cd 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -497,27 +497,27 @@ describe('Scope', function() { $rootScope.obj = 'test'; $rootScope.$digest(); expect(log).toEqual(['"test"']); - expect(logb).toEqual([undefined]); + expect(logb).toEqual(['"test"']); $rootScope.obj = []; $rootScope.$digest(); expect(log).toEqual(['"test"', '[]']); - expect(logb).toEqual([undefined, '"test"']); + expect(logb).toEqual(['"test"', '"test"']); $rootScope.obj = {}; $rootScope.$digest(); expect(log).toEqual(['"test"', '[]', '{}']); - expect(logb).toEqual([undefined, '"test"', '[]']); + expect(logb).toEqual(['"test"', '"test"', '[]']); $rootScope.obj = []; $rootScope.$digest(); expect(log).toEqual(['"test"', '[]', '{}', '[]']); - expect(logb).toEqual([undefined, '"test"', '[]', '{}']); + expect(logb).toEqual(['"test"', '"test"', '[]', '{}']); $rootScope.obj = undefined; $rootScope.$digest(); expect(log).toEqual(['"test"', '[]', '{}', '[]', undefined]); - expect(logb).toEqual([undefined, '"test"', '[]', '{}', '[]']); + expect(logb).toEqual(['"test"', '"test"', '[]', '{}', '[]']); }); @@ -540,12 +540,12 @@ describe('Scope', function() { $rootScope.obj.push('a'); $rootScope.$digest(); expect(log).toEqual(['[]', '["a"]']); - expect(logb).toEqual([undefined, '[]']); + expect(logb).toEqual(['[]', '[]']); $rootScope.obj[0] = 'b'; $rootScope.$digest(); expect(log).toEqual(['[]', '["a"]', '["b"]']); - expect(logb).toEqual([undefined, '[]', '["a"]']); + expect(logb).toEqual(['[]', '[]', '["a"]']); $rootScope.obj.push([]); $rootScope.obj.push({}); @@ -596,7 +596,7 @@ describe('Scope', function() { $rootScope.obj = {}; $rootScope.$digest(); expect(log).toEqual(['"test"', '{}']); - expect(logb).toEqual([undefined, '"test"']); + expect(logb).toEqual(['"test"', '"test"']); }); @@ -619,12 +619,12 @@ describe('Scope', function() { $rootScope.obj.a= 'A'; $rootScope.$digest(); expect(log).toEqual(['{}', '{"a":"A"}']); - expect(logb).toEqual([undefined, '{}']); + expect(logb).toEqual(['{}', '{}']); $rootScope.obj.a = 'B'; $rootScope.$digest(); expect(log).toEqual(['{}', '{"a":"A"}', '{"a":"B"}']); - expect(logb).toEqual([undefined, '{}', '{"a":"A"}']); + expect(logb).toEqual(['{}', '{}', '{"a":"A"}']); $rootScope.obj.b = []; $rootScope.obj.c = {}; From 42dd6a17632106e447b2e5846fd79190ef7eb804 Mon Sep 17 00:00:00 2001 From: John Murphy Date: Wed, 8 Jan 2014 00:56:42 +1000 Subject: [PATCH 3/6] Refactor oldValue initialisation --- src/ng/rootScope.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index b88d75bf9741..5aae41145c4d 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -416,7 +416,6 @@ function $RootScopeProvider(){ var self = this; var oldValue; var newValue; - var changeDetected; var changeFlipFlop = 0; var objGetter = $parse(obj); var internalArray = []; @@ -429,14 +428,14 @@ function $RootScopeProvider(){ var internalValue = initWatchVal; function $watchCollectionWatch() { - var newLength, key, i; + var newLength, key, i, changeDetected; newValue = objGetter(self); + oldValue = internalValue; changeDetected = 0; if (!isObject(newValue)) { if (internalValue !== newValue) { - oldValue = internalValue; internalValue = newValue; changeDetected++; } @@ -460,7 +459,6 @@ function $RootScopeProvider(){ } } if (changeDetected) { - oldValue = internalValue; // copy the items to array cache internalValue = internalArray = []; internalValue.length = internalLength = newLength; @@ -490,7 +488,6 @@ function $RootScopeProvider(){ } } if (changeDetected) { - oldValue = internalValue; // copy the items to object cache internalValue = internalObject = {}; internalLength = 0; From 69ea4bcd43169b7488120a429d22ff8c5fe806cc Mon Sep 17 00:00:00 2001 From: John Murphy Date: Wed, 8 Jan 2014 01:20:32 +1000 Subject: [PATCH 4/6] watchCollection: Re-order tests for clarity --- test/ng/rootScopeSpec.js | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index a8a606f542cd..1c6d1c6ebc5b 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -459,12 +459,12 @@ describe('Scope', function() { var log, logb, $rootScope, deregister; beforeEach(inject(function(_$rootScope_) { - log = []; - logb = []; + logb = []; // list of before values + log = []; // list of after values $rootScope = _$rootScope_; deregister = $rootScope.$watchCollection('obj', function logger(obj, objb) { - log.push(toJson(obj)); logb.push(toJson(objb)); + log.push(toJson(obj)); }); })); @@ -496,28 +496,28 @@ describe('Scope', function() { it('should trigger when property changes into array', function() { $rootScope.obj = 'test'; $rootScope.$digest(); - expect(log).toEqual(['"test"']); expect(logb).toEqual(['"test"']); + expect(log).toEqual(['"test"']); $rootScope.obj = []; $rootScope.$digest(); - expect(log).toEqual(['"test"', '[]']); expect(logb).toEqual(['"test"', '"test"']); + expect(log).toEqual(['"test"', '[]']); $rootScope.obj = {}; $rootScope.$digest(); - expect(log).toEqual(['"test"', '[]', '{}']); expect(logb).toEqual(['"test"', '"test"', '[]']); + expect(log).toEqual(['"test"', '[]', '{}']); $rootScope.obj = []; $rootScope.$digest(); - expect(log).toEqual(['"test"', '[]', '{}', '[]']); expect(logb).toEqual(['"test"', '"test"', '[]', '{}']); + expect(log).toEqual(['"test"', '[]', '{}', '[]']); $rootScope.obj = undefined; $rootScope.$digest(); - expect(log).toEqual(['"test"', '[]', '{}', '[]', undefined]); expect(logb).toEqual(['"test"', '"test"', '[]', '{}', '[]']); + expect(log).toEqual(['"test"', '[]', '{}', '[]', undefined]); }); @@ -539,28 +539,28 @@ describe('Scope', function() { $rootScope.obj.push('a'); $rootScope.$digest(); - expect(log).toEqual(['[]', '["a"]']); expect(logb).toEqual(['[]', '[]']); + expect(log).toEqual(['[]', '["a"]']); $rootScope.obj[0] = 'b'; $rootScope.$digest(); - expect(log).toEqual(['[]', '["a"]', '["b"]']); expect(logb).toEqual(['[]', '[]', '["a"]']); + expect(log).toEqual(['[]', '["a"]', '["b"]']); $rootScope.obj.push([]); $rootScope.obj.push({}); - log = []; logb = []; + log = []; $rootScope.$digest(); - expect(log).toEqual(['["b",[],{}]']); expect(logb).toEqual(['["b"]']); + expect(log).toEqual(['["b",[],{}]']); var temp = $rootScope.obj[1]; $rootScope.obj[1] = $rootScope.obj[2]; $rootScope.obj[2] = temp; $rootScope.$digest(); - expect(log).toEqual([ '["b",[],{}]', '["b",{},[]]' ]); expect(logb).toEqual([ '["b"]', '["b",[],{}]']); + expect(log).toEqual([ '["b",[],{}]', '["b",{},[]]' ]); $rootScope.obj.shift(); log = []; @@ -595,8 +595,8 @@ describe('Scope', function() { $rootScope.obj = {}; $rootScope.$digest(); - expect(log).toEqual(['"test"', '{}']); expect(logb).toEqual(['"test"', '"test"']); + expect(log).toEqual(['"test"', '{}']); }); @@ -618,35 +618,35 @@ describe('Scope', function() { $rootScope.obj.a= 'A'; $rootScope.$digest(); - expect(log).toEqual(['{}', '{"a":"A"}']); expect(logb).toEqual(['{}', '{}']); + expect(log).toEqual(['{}', '{"a":"A"}']); $rootScope.obj.a = 'B'; $rootScope.$digest(); - expect(log).toEqual(['{}', '{"a":"A"}', '{"a":"B"}']); expect(logb).toEqual(['{}', '{}', '{"a":"A"}']); + expect(log).toEqual(['{}', '{"a":"A"}', '{"a":"B"}']); $rootScope.obj.b = []; $rootScope.obj.c = {}; log = []; logb = []; $rootScope.$digest(); - expect(log).toEqual(['{"a":"B","b":[],"c":{}}']); expect(logb).toEqual(['{"a":"B"}']); + expect(log).toEqual(['{"a":"B","b":[],"c":{}}']); var temp = $rootScope.obj.a; $rootScope.obj.a = $rootScope.obj.b; $rootScope.obj.c = temp; $rootScope.$digest(); - expect(log).toEqual([ '{"a":"B","b":[],"c":{}}', '{"a":[],"b":[],"c":"B"}' ]); expect(logb).toEqual([ '{"a":"B"}', '{"a":"B","b":[],"c":{}}']); + expect(log).toEqual([ '{"a":"B","b":[],"c":{}}', '{"a":[],"b":[],"c":"B"}' ]); delete $rootScope.obj.a; - log = []; logb = []; + log = []; $rootScope.$digest(); - expect(log).toEqual([ '{"b":[],"c":"B"}' ]); expect(logb).toEqual([ '{"a":[],"b":[],"c":"B"}' ]); + expect(log).toEqual([ '{"b":[],"c":"B"}' ]); }); }); }); From 34333fb3691ad2622029d127bde307cdd7e4e1cd Mon Sep 17 00:00:00 2001 From: John Murphy Date: Wed, 8 Jan 2014 13:12:39 +1000 Subject: [PATCH 5/6] Ensure oldCollection === newCollection on first call (only) to listener - include a test. --- src/ng/rootScope.js | 2 +- test/ng/rootScopeSpec.js | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index 5aae41145c4d..77715f1b3a46 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -503,7 +503,7 @@ function $RootScopeProvider(){ if (changeDetected) { changeFlipFlop = 1 - changeFlipFlop; if (oldValue === initWatchVal) { - oldValue = internalValue; + oldValue = newValue; } } diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index 1c6d1c6ebc5b..60e34bfe392c 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -458,6 +458,28 @@ describe('Scope', function() { describe('$watchCollection', function() { var log, logb, $rootScope, deregister; + it('should return oldCollection === newCollection on first call (only) to listener', inject(function($rootScope) { + var paramsEqual; + $rootScope.obj = {'a': 'b'}; + deregister = $rootScope.$watchCollection('obj', function listener(obj, objb) { + paramsEqual = (obj === objb); + }); + + // equal first time + $rootScope.$digest(); + expect(paramsEqual).toBeTruthy(); + + $rootScope.obj.a = 'c'; + $rootScope.$digest(); + expect(paramsEqual).toBeFalsy(); + + $rootScope.obj = undefined; + $rootScope.$digest(); + expect(paramsEqual).toBeFalsy(); + + })); + + beforeEach(inject(function(_$rootScope_) { logb = []; // list of before values log = []; // list of after values From b809444a8e8cbdb6116fe52f967c1b66825dee35 Mon Sep 17 00:00:00 2001 From: johnsoftek Date: Tue, 18 Mar 2014 14:39:48 +1000 Subject: [PATCH 6/6] Simplify oldCollection parameter checking --- test/ng/rootScopeSpec.js | 95 +++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index 60e34bfe392c..473b50c928f9 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -454,53 +454,44 @@ describe('Scope', function() { })); }); - describe('$watchCollection', function() { - var log, logb, $rootScope, deregister; + var log, $rootScope, deregister, prevval, firstWatch, oldValueCorrect; + + beforeEach(inject(function (_$rootScope_) { + log = []; + $rootScope = _$rootScope_; + firstWatch = true; + oldValueCorrect = true; + deregister = $rootScope.$watchCollection('obj', function listener(obj, oldobj) { + log.push(toJson(obj)); + if (firstWatch) { + firstWatch = false; + if (toJson(obj) !== toJson(oldobj)) { + oldValueCorrect = false; + } + } else { + if (toJson(oldobj) !== prevval) { + oldValueCorrect = false; + } + } + prevval = toJson(obj); + }); + })); - it('should return oldCollection === newCollection on first call (only) to listener', inject(function($rootScope) { - var paramsEqual; - $rootScope.obj = {'a': 'b'}; - deregister = $rootScope.$watchCollection('obj', function listener(obj, objb) { - paramsEqual = (obj === objb); + afterEach(function () { + deregister(); }); - // equal first time - $rootScope.$digest(); - expect(paramsEqual).toBeTruthy(); - - $rootScope.obj.a = 'c'; - $rootScope.$digest(); - expect(paramsEqual).toBeFalsy(); - - $rootScope.obj = undefined; - $rootScope.$digest(); - expect(paramsEqual).toBeFalsy(); - - })); - - - beforeEach(inject(function(_$rootScope_) { - logb = []; // list of before values - log = []; // list of after values - $rootScope = _$rootScope_; - deregister = $rootScope.$watchCollection('obj', function logger(obj, objb) { - logb.push(toJson(objb)); - log.push(toJson(obj)); - }); - })); - - - it('should not trigger if nothing change', inject(function($rootScope) { + it('should not trigger if nothing change', function() { $rootScope.$digest(); expect(log).toEqual([undefined]); $rootScope.$digest(); expect(log).toEqual([undefined]); - })); + }); - it('should allow deregistration', inject(function($rootScope) { + it('should allow deregistration', function() { $rootScope.obj = []; $rootScope.$digest(); @@ -511,35 +502,33 @@ describe('Scope', function() { $rootScope.$digest(); expect(log).toEqual(['[]']); - })); + }); describe('array', function() { it('should trigger when property changes into array', function() { $rootScope.obj = 'test'; $rootScope.$digest(); - expect(logb).toEqual(['"test"']); expect(log).toEqual(['"test"']); $rootScope.obj = []; $rootScope.$digest(); - expect(logb).toEqual(['"test"', '"test"']); expect(log).toEqual(['"test"', '[]']); $rootScope.obj = {}; $rootScope.$digest(); - expect(logb).toEqual(['"test"', '"test"', '[]']); expect(log).toEqual(['"test"', '[]', '{}']); $rootScope.obj = []; $rootScope.$digest(); - expect(logb).toEqual(['"test"', '"test"', '[]', '{}']); expect(log).toEqual(['"test"', '[]', '{}', '[]']); $rootScope.obj = undefined; $rootScope.$digest(); - expect(logb).toEqual(['"test"', '"test"', '[]', '{}', '[]']); expect(log).toEqual(['"test"', '[]', '{}', '[]', undefined]); + + expect(oldValueCorrect).toBe(true); + }); @@ -561,33 +550,31 @@ describe('Scope', function() { $rootScope.obj.push('a'); $rootScope.$digest(); - expect(logb).toEqual(['[]', '[]']); expect(log).toEqual(['[]', '["a"]']); $rootScope.obj[0] = 'b'; $rootScope.$digest(); - expect(logb).toEqual(['[]', '[]', '["a"]']); expect(log).toEqual(['[]', '["a"]', '["b"]']); $rootScope.obj.push([]); $rootScope.obj.push({}); - logb = []; log = []; $rootScope.$digest(); - expect(logb).toEqual(['["b"]']); expect(log).toEqual(['["b",[],{}]']); var temp = $rootScope.obj[1]; $rootScope.obj[1] = $rootScope.obj[2]; $rootScope.obj[2] = temp; $rootScope.$digest(); - expect(logb).toEqual([ '["b"]', '["b",[],{}]']); expect(log).toEqual([ '["b",[],{}]', '["b",{},[]]' ]); $rootScope.obj.shift(); log = []; $rootScope.$digest(); expect(log).toEqual([ '[{},[]]' ]); + + expect(oldValueCorrect).toBe(true); + }); it('should watch array-like objects like arrays', function () { @@ -605,6 +592,9 @@ describe('Scope', function() { $rootScope.arrayLikeObject = document.getElementsByTagName('a'); $rootScope.$digest(); expect(arrayLikelog).toEqual(['x', 'y']); + + expect(oldValueCorrect).toBe(true); + }); }); @@ -617,7 +607,6 @@ describe('Scope', function() { $rootScope.obj = {}; $rootScope.$digest(); - expect(logb).toEqual(['"test"', '"test"']); expect(log).toEqual(['"test"', '{}']); }); @@ -640,35 +629,31 @@ describe('Scope', function() { $rootScope.obj.a= 'A'; $rootScope.$digest(); - expect(logb).toEqual(['{}', '{}']); expect(log).toEqual(['{}', '{"a":"A"}']); $rootScope.obj.a = 'B'; $rootScope.$digest(); - expect(logb).toEqual(['{}', '{}', '{"a":"A"}']); expect(log).toEqual(['{}', '{"a":"A"}', '{"a":"B"}']); $rootScope.obj.b = []; $rootScope.obj.c = {}; log = []; - logb = []; $rootScope.$digest(); - expect(logb).toEqual(['{"a":"B"}']); expect(log).toEqual(['{"a":"B","b":[],"c":{}}']); var temp = $rootScope.obj.a; $rootScope.obj.a = $rootScope.obj.b; $rootScope.obj.c = temp; $rootScope.$digest(); - expect(logb).toEqual([ '{"a":"B"}', '{"a":"B","b":[],"c":{}}']); expect(log).toEqual([ '{"a":"B","b":[],"c":{}}', '{"a":[],"b":[],"c":"B"}' ]); delete $rootScope.obj.a; - logb = []; log = []; $rootScope.$digest(); - expect(logb).toEqual([ '{"a":[],"b":[],"c":"B"}' ]); expect(log).toEqual([ '{"b":[],"c":"B"}' ]); + + expect(oldValueCorrect).toBe(true); + }); }); });