From ba3d5ffedd04f964a0824bfb20dc5ba6034139ce Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Mon, 18 Jan 2016 16:24:39 -0600 Subject: [PATCH 01/10] adds a failing test for 12475 This adds a failing test for #12475. It appears that `obj.set('array', [a1, a2, a3])` fails to install contentKey observers on the new array value so that the subsequent changes to `foo` are ignored. --- .../computed/reduce_computed_macros_test.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js b/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js index b037f308862..b36276540d8 100644 --- a/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js +++ b/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js @@ -6,6 +6,7 @@ import { get } from 'ember-metal/property_get'; import { set } from 'ember-metal/property_set'; import { addObserver } from 'ember-metal/observer'; import { observer } from 'ember-metal/mixin'; +import { computed } from 'ember-metal/computed'; import { sum, min, @@ -427,6 +428,28 @@ QUnit.test('properties values can be replaced', function() { deepEqual(obj.get('a1bs').mapBy('name'), ['item1'], 'properties can be filtered by matching value'); }); +QUnit.test('responds to change of property value on element after replacing array', function() { + let a1 = { foo: true, id: 1 }; + let a2 = { foo: true, id: 2 }; + let a3 = { foo: true, id: 4 }; + + obj = EmberObject.extend({ + a: computed('array.@each.foo', function() { + return this.get('array').filter(elt => elt.foo).reduce(((a,b) => a + b.id), 0); + }) + }).create({ + array: emberA([a1, a2]) + }); + + deepEqual(obj.get('a'), 3, 'value is correct initially'); + set(a1, 'foo', false); + deepEqual(obj.get('a'), 2, 'responds to change of property on element'); + obj.set('array', [a1, a2, a3]); + deepEqual(obj.get('a'), 6, 'responds to content array change'); + set(a1, 'foo', true); + deepEqual(obj.get('a'), 7, 'still responds to change of property on element'); +}); + [ ['uniq', uniq], ['union', union] From bef723681fcc46dd46adab71317748451e6ad9c2 Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Mon, 18 Jan 2016 18:20:40 -0600 Subject: [PATCH 02/10] Failing tests for #12475 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * reduces EF4’s case further * move related tests to runtime/computed/chains * adds more thorough tests revealing more related issues --- .../tests/computed/chains_test.js | 74 +++++++++++++++++++ .../computed/reduce_computed_macros_test.js | 22 ------ 2 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 packages/ember-runtime/tests/computed/chains_test.js diff --git a/packages/ember-runtime/tests/computed/chains_test.js b/packages/ember-runtime/tests/computed/chains_test.js new file mode 100644 index 00000000000..422c6104d7e --- /dev/null +++ b/packages/ember-runtime/tests/computed/chains_test.js @@ -0,0 +1,74 @@ +import { computed } from 'ember-metal/computed'; +import { defineProperty } from 'ember-metal/properties'; +import { get } from 'ember-metal/property_get'; +import { set } from 'ember-metal/property_set'; +import { isWatching } from 'ember-metal/watching'; +import { A as emberA } from 'ember-runtime/system/native_array'; + +let a1, a2, a3, a4, obj; + +QUnit.module('chain watching', { + setup() { + a1 = { foo: true, id: 1 }; + a2 = { foo: true, id: 2 }; + + a3 = { foo: false, id: 3 }; + a4 = { foo: false, id: 4 }; + + obj = { }; + defineProperty(obj, 'a', computed('array.@each.foo', function() {})); + } +}); + +QUnit.test('replace array (no overlap)', function() { + set(obj, 'array', Ember.A([a1, a2])); + get(obj, 'a'); // kick CP; + + ok( isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); + ok( isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); + ok(!isWatching(a3, 'foo'), 'BEFORE: a3.foo is NOT watched'); + ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); + + set(obj, 'array', [a3, a4]); + + ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); + ok(!isWatching(a2, 'foo'), 'AFTER: a2.foo is NOT watched'); + ok( isWatching(a3, 'foo'), 'AFTER: a3.foo is watched'); + ok( isWatching(a4, 'foo'), 'AFTER: a4.foo is watched'); +}); + +QUnit.test('replace array (overlap)', function() { + set(obj, 'array', Ember.A([a1, a2, a3])); + get(obj, 'a'); // kick CP; + + ok( isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); + ok( isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); + ok( isWatching(a3, 'foo'), 'BEFORE: a3.foo is watched'); + ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); + + obj.set('array', [a2, a3, a4]); + + ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); + ok( isWatching(a2, 'foo'), 'AFTER: a2.foo is watched'); + ok( isWatching(a3, 'foo'), 'AFTER: a3.foo is watched'); + ok( isWatching(a4, 'foo'), 'AFTER: a4.foo is watched'); +}); + +QUnit.test('responds to change of property value on element after replacing array', function() { + let obj = { }; + + defineProperty(obj, 'a', computed('array.@each.foo', function() { + return this.array.filter(elt => elt.foo).reduce(((a,b) => a + b.id), 0); + })); + + set(obj, 'array', emberA([a1,a2])); + + deepEqual(get(obj, 'a'), 3, 'value is correct initially'); + set(a1, 'foo', false); + deepEqual(get(obj, 'a'), 2, 'responds to change of property on element'); + set(obj, 'array', [a1, a2, a3]); + deepEqual(get(obj, 'a'), 6, 'responds to content array change'); + set(a1, 'foo', true); + deepEqual(get(obj, 'a'), 7, 'still responds to change of property on element'); +}); + diff --git a/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js b/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js index b36276540d8..b66f7c95f24 100644 --- a/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js +++ b/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js @@ -428,28 +428,6 @@ QUnit.test('properties values can be replaced', function() { deepEqual(obj.get('a1bs').mapBy('name'), ['item1'], 'properties can be filtered by matching value'); }); -QUnit.test('responds to change of property value on element after replacing array', function() { - let a1 = { foo: true, id: 1 }; - let a2 = { foo: true, id: 2 }; - let a3 = { foo: true, id: 4 }; - - obj = EmberObject.extend({ - a: computed('array.@each.foo', function() { - return this.get('array').filter(elt => elt.foo).reduce(((a,b) => a + b.id), 0); - }) - }).create({ - array: emberA([a1, a2]) - }); - - deepEqual(obj.get('a'), 3, 'value is correct initially'); - set(a1, 'foo', false); - deepEqual(obj.get('a'), 2, 'responds to change of property on element'); - obj.set('array', [a1, a2, a3]); - deepEqual(obj.get('a'), 6, 'responds to content array change'); - set(a1, 'foo', true); - deepEqual(obj.get('a'), 7, 'still responds to change of property on element'); -}); - [ ['uniq', uniq], ['union', union] From d2486ecf0596e54cc90b774132641e56c2536ae0 Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Wed, 20 Jan 2016 09:45:15 -0800 Subject: [PATCH 03/10] Fix test, when EXTEND_PROTOTYPES: false, we need to use Ember.A in all scenarios were the array is potentially observable. --- packages/ember-runtime/tests/computed/chains_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ember-runtime/tests/computed/chains_test.js b/packages/ember-runtime/tests/computed/chains_test.js index 422c6104d7e..ed3783becf9 100644 --- a/packages/ember-runtime/tests/computed/chains_test.js +++ b/packages/ember-runtime/tests/computed/chains_test.js @@ -66,7 +66,7 @@ QUnit.test('responds to change of property value on element after replacing arra deepEqual(get(obj, 'a'), 3, 'value is correct initially'); set(a1, 'foo', false); deepEqual(get(obj, 'a'), 2, 'responds to change of property on element'); - set(obj, 'array', [a1, a2, a3]); + set(obj, 'array', emberA([a1, a2, a3])); deepEqual(get(obj, 'a'), 6, 'responds to content array change'); set(a1, 'foo', true); deepEqual(get(obj, 'a'), 7, 'still responds to change of property on element'); From 0a17891fa8f09f87831c238ca62f88db840e2ebe Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Wed, 20 Jan 2016 09:45:30 -0800 Subject: [PATCH 04/10] make jshint/jscs happy --- .../tests/computed/chains_test.js | 28 +++++++++---------- .../computed/reduce_computed_macros_test.js | 1 - 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/ember-runtime/tests/computed/chains_test.js b/packages/ember-runtime/tests/computed/chains_test.js index ed3783becf9..8aa4c22c404 100644 --- a/packages/ember-runtime/tests/computed/chains_test.js +++ b/packages/ember-runtime/tests/computed/chains_test.js @@ -21,11 +21,11 @@ QUnit.module('chain watching', { }); QUnit.test('replace array (no overlap)', function() { - set(obj, 'array', Ember.A([a1, a2])); + set(obj, 'array', emberA([a1, a2])); get(obj, 'a'); // kick CP; - ok( isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); - ok( isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); + ok(isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); + ok(isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); ok(!isWatching(a3, 'foo'), 'BEFORE: a3.foo is NOT watched'); ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); @@ -33,35 +33,35 @@ QUnit.test('replace array (no overlap)', function() { ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); ok(!isWatching(a2, 'foo'), 'AFTER: a2.foo is NOT watched'); - ok( isWatching(a3, 'foo'), 'AFTER: a3.foo is watched'); - ok( isWatching(a4, 'foo'), 'AFTER: a4.foo is watched'); + ok(isWatching(a3, 'foo'), 'AFTER: a3.foo is watched'); + ok(isWatching(a4, 'foo'), 'AFTER: a4.foo is watched'); }); QUnit.test('replace array (overlap)', function() { - set(obj, 'array', Ember.A([a1, a2, a3])); + set(obj, 'array', emberA([a1, a2, a3])); get(obj, 'a'); // kick CP; - ok( isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); - ok( isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); - ok( isWatching(a3, 'foo'), 'BEFORE: a3.foo is watched'); + ok(isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); + ok(isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); + ok(isWatching(a3, 'foo'), 'BEFORE: a3.foo is watched'); ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); obj.set('array', [a2, a3, a4]); ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); - ok( isWatching(a2, 'foo'), 'AFTER: a2.foo is watched'); - ok( isWatching(a3, 'foo'), 'AFTER: a3.foo is watched'); - ok( isWatching(a4, 'foo'), 'AFTER: a4.foo is watched'); + ok(isWatching(a2, 'foo'), 'AFTER: a2.foo is watched'); + ok(isWatching(a3, 'foo'), 'AFTER: a3.foo is watched'); + ok(isWatching(a4, 'foo'), 'AFTER: a4.foo is watched'); }); QUnit.test('responds to change of property value on element after replacing array', function() { let obj = { }; defineProperty(obj, 'a', computed('array.@each.foo', function() { - return this.array.filter(elt => elt.foo).reduce(((a,b) => a + b.id), 0); + return this.array.filter(elt => elt.foo).reduce((a, b) => a + b.id, 0); })); - set(obj, 'array', emberA([a1,a2])); + set(obj, 'array', emberA([a1, a2])); deepEqual(get(obj, 'a'), 3, 'value is correct initially'); set(a1, 'foo', false); diff --git a/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js b/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js index b66f7c95f24..b037f308862 100644 --- a/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js +++ b/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js @@ -6,7 +6,6 @@ import { get } from 'ember-metal/property_get'; import { set } from 'ember-metal/property_set'; import { addObserver } from 'ember-metal/observer'; import { observer } from 'ember-metal/mixin'; -import { computed } from 'ember-metal/computed'; import { sum, min, From fd9805438bfc34c2b9e33bd056d57d2d36dde53e Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Wed, 20 Jan 2016 11:30:22 -0800 Subject: [PATCH 05/10] Add Addition CP + Chains tests scenarios with: * object proxy * array proxy --- .../tests/computed/chains_test.js | 88 ++++++++++++++++++- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/packages/ember-runtime/tests/computed/chains_test.js b/packages/ember-runtime/tests/computed/chains_test.js index 8aa4c22c404..ad440cbe18f 100644 --- a/packages/ember-runtime/tests/computed/chains_test.js +++ b/packages/ember-runtime/tests/computed/chains_test.js @@ -4,6 +4,8 @@ import { get } from 'ember-metal/property_get'; import { set } from 'ember-metal/property_set'; import { isWatching } from 'ember-metal/watching'; import { A as emberA } from 'ember-runtime/system/native_array'; +import ObjectProxy from 'ember-runtime/system/object_proxy'; +import ArrayProxy from 'ember-runtime/system/array_proxy'; let a1, a2, a3, a4, obj; @@ -46,7 +48,7 @@ QUnit.test('replace array (overlap)', function() { ok(isWatching(a3, 'foo'), 'BEFORE: a3.foo is watched'); ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); - obj.set('array', [a2, a3, a4]); + set(obj, 'array', [a2, a3, a4]); ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); ok(isWatching(a2, 'foo'), 'AFTER: a2.foo is watched'); @@ -64,11 +66,91 @@ QUnit.test('responds to change of property value on element after replacing arra set(obj, 'array', emberA([a1, a2])); deepEqual(get(obj, 'a'), 3, 'value is correct initially'); + set(a1, 'foo', false); + deepEqual(get(obj, 'a'), 2, 'responds to change of property on element'); + set(obj, 'array', emberA([a1, a2, a3])); - deepEqual(get(obj, 'a'), 6, 'responds to content array change'); + + deepEqual(get(obj, 'a'), 2, 'responds to content array change'); + + set(a1, 'foo', true); + + deepEqual(get(obj, 'a'), 3, 'still responds to change of property on element'); + set(a3, 'foo', true); + + deepEqual(get(obj, 'a'), 6, 'still responds to change of property on element'); +}); + + +QUnit.test('responds to change of property value on element after replacing array (object proxy)', function() { + let obj = { }; + + defineProperty(obj, 'a', computed('array.@each.foo', function() { + return get(this, 'array').filter(elt => get(elt, 'foo')).reduce((a, b) => a + get(b, 'id'), 0); + })); + + set(obj, 'array', emberA([ + ObjectProxy.create({ content: a1 }), + ObjectProxy.create({ content: a2 }) + ])); + + deepEqual(get(obj, 'a'), 3, 'value is correct initially'); + + set(a1, 'foo', false); + + deepEqual(get(obj, 'a'), 2, 'responds to change of property on element'); + set(obj, 'array', emberA([ + ObjectProxy.create({ content: a1 }), + ObjectProxy.create({ content: a2 }), + ObjectProxy.create({ content: a3 }) + ])); + + deepEqual(get(obj, 'a'), 2, 'responds to content array change'); + set(a1, 'foo', true); - deepEqual(get(obj, 'a'), 7, 'still responds to change of property on element'); + + deepEqual(get(obj, 'a'), 3, 'still responds to change of property on element'); + set(a3, 'foo', true); + + deepEqual(get(obj, 'a'), 6, 'still responds to change of property on element'); }); + +QUnit.test('responds to change of property value on element after replacing array (array proxy)', function() { + let obj = { }; + + defineProperty(obj, 'a', computed('array.@each.foo', function() { + return get(this, 'array').filter(elt => get(elt, 'foo')).reduce((a, b) => a + get(b, 'id'), 0); + })); + + set(obj, 'array', ArrayProxy.create({ + content: emberA([ + ObjectProxy.create({ content: a1 }), + ObjectProxy.create({ content: a2 }) + ]) + })); + + deepEqual(get(obj, 'a'), 3, 'value is correct initially'); + + set(a1, 'foo', false); + + deepEqual(get(obj, 'a'), 2, 'responds to change of property on element'); + set(obj, 'array', ArrayProxy.create({ + content: emberA([ + ObjectProxy.create({ content: a1 }), + ObjectProxy.create({ content: a2 }), + ObjectProxy.create({ content: a3 }) + ]) + })); + + deepEqual(get(obj, 'a'), 2, 'responds to content array change'); + + set(a1, 'foo', true); + + deepEqual(get(obj, 'a'), 3, 'still responds to change of property on element'); + set(a3, 'foo', true); + + deepEqual(get(obj, 'a'), 6, 'still responds to change of property on element'); +}); From c6b298fdc6e4e177d76c8a43f91493aaa0bff8c6 Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Wed, 20 Jan 2016 16:20:17 -0800 Subject: [PATCH 06/10] Add cp + chains async proxy tests --- .../tests/computed/chains_test.js | 213 +++++++++++++++--- 1 file changed, 179 insertions(+), 34 deletions(-) diff --git a/packages/ember-runtime/tests/computed/chains_test.js b/packages/ember-runtime/tests/computed/chains_test.js index ad440cbe18f..06803fd0301 100644 --- a/packages/ember-runtime/tests/computed/chains_test.js +++ b/packages/ember-runtime/tests/computed/chains_test.js @@ -6,6 +6,12 @@ import { isWatching } from 'ember-metal/watching'; import { A as emberA } from 'ember-runtime/system/native_array'; import ObjectProxy from 'ember-runtime/system/object_proxy'; import ArrayProxy from 'ember-runtime/system/array_proxy'; +import PromiseProxyMixin from 'ember-runtime/mixins/promise_proxy'; +import RSVP from 'ember-runtime/ext/rsvp'; +import run from 'ember-metal/run_loop'; + +const ArrayPromiseProxy = ArrayProxy.reopen(PromiseProxyMixin); +const ObjectPromiseProxy = ObjectProxy.reopen(PromiseProxyMixin); let a1, a2, a3, a4, obj; @@ -56,75 +62,88 @@ QUnit.test('replace array (overlap)', function() { ok(isWatching(a4, 'foo'), 'AFTER: a4.foo is watched'); }); -QUnit.test('responds to change of property value on element after replacing array', function() { - let obj = { }; +let a1Remote, a2Remote, a3Remote; - defineProperty(obj, 'a', computed('array.@each.foo', function() { - return this.array.filter(elt => elt.foo).reduce((a, b) => a + b.id, 0); - })); +QUnit.module('chain watching (filter)', { + setup() { + a1 = { foo: true, id: 1 }; + a2 = { foo: true, id: 2 }; + + a3 = { foo: false, id: 3 }; + a4 = { foo: false, id: 4 }; + + run(_ => { + // pre-settle the promises; + a1Remote = ObjectPromiseProxy.create({ + promise: RSVP.Promise.resolve(a1) + }); + a2Remote = ObjectPromiseProxy.create({ + promise: RSVP.Promise.resolve(a2) + }); + + a3Remote = ObjectPromiseProxy.create({ + promise: RSVP.Promise.resolve(a3) + }); + }); + + obj = { }; + + defineProperty(obj, 'a', computed('array.@each.foo', function() { + return this.array.filter(elt => get(elt, 'foo')).reduce((a, b) => a + get(b, 'id'), 0); + })); + } +}); + +QUnit.test('responds to change of property value on element after replacing array', function() { set(obj, 'array', emberA([a1, a2])); - deepEqual(get(obj, 'a'), 3, 'value is correct initially'); + equal(get(obj, 'a'), 3, 'value is correct initially'); set(a1, 'foo', false); - deepEqual(get(obj, 'a'), 2, 'responds to change of property on element'); + equal(get(obj, 'a'), 2, 'responds to change of property on element'); set(obj, 'array', emberA([a1, a2, a3])); - deepEqual(get(obj, 'a'), 2, 'responds to content array change'); + equal(get(obj, 'a'), 2, 'responds to content array change'); set(a1, 'foo', true); - deepEqual(get(obj, 'a'), 3, 'still responds to change of property on element'); + equal(get(obj, 'a'), 3, 'still responds to change of property on element'); set(a3, 'foo', true); - deepEqual(get(obj, 'a'), 6, 'still responds to change of property on element'); + equal(get(obj, 'a'), 6, 'still responds to change of property on element'); }); - QUnit.test('responds to change of property value on element after replacing array (object proxy)', function() { - let obj = { }; - - defineProperty(obj, 'a', computed('array.@each.foo', function() { - return get(this, 'array').filter(elt => get(elt, 'foo')).reduce((a, b) => a + get(b, 'id'), 0); - })); - set(obj, 'array', emberA([ ObjectProxy.create({ content: a1 }), ObjectProxy.create({ content: a2 }) ])); - deepEqual(get(obj, 'a'), 3, 'value is correct initially'); + equal(get(obj, 'a'), 3, 'value is correct initially'); set(a1, 'foo', false); - deepEqual(get(obj, 'a'), 2, 'responds to change of property on element'); + equal(get(obj, 'a'), 2, 'responds to change of property on element'); set(obj, 'array', emberA([ ObjectProxy.create({ content: a1 }), ObjectProxy.create({ content: a2 }), ObjectProxy.create({ content: a3 }) ])); - deepEqual(get(obj, 'a'), 2, 'responds to content array change'); + equal(get(obj, 'a'), 2, 'responds to content array change'); set(a1, 'foo', true); - deepEqual(get(obj, 'a'), 3, 'still responds to change of property on element'); + equal(get(obj, 'a'), 3, 'still responds to change of property on element'); set(a3, 'foo', true); - deepEqual(get(obj, 'a'), 6, 'still responds to change of property on element'); + equal(get(obj, 'a'), 6, 'still responds to change of property on element'); }); - QUnit.test('responds to change of property value on element after replacing array (array proxy)', function() { - let obj = { }; - - defineProperty(obj, 'a', computed('array.@each.foo', function() { - return get(this, 'array').filter(elt => get(elt, 'foo')).reduce((a, b) => a + get(b, 'id'), 0); - })); - set(obj, 'array', ArrayProxy.create({ content: emberA([ ObjectProxy.create({ content: a1 }), @@ -132,11 +151,11 @@ QUnit.test('responds to change of property value on element after replacing arra ]) })); - deepEqual(get(obj, 'a'), 3, 'value is correct initially'); + equal(get(obj, 'a'), 3, 'value is correct initially'); set(a1, 'foo', false); - deepEqual(get(obj, 'a'), 2, 'responds to change of property on element'); + equal(get(obj, 'a'), 2, 'responds to change of property on element'); set(obj, 'array', ArrayProxy.create({ content: emberA([ ObjectProxy.create({ content: a1 }), @@ -145,12 +164,138 @@ QUnit.test('responds to change of property value on element after replacing arra ]) })); - deepEqual(get(obj, 'a'), 2, 'responds to content array change'); + equal(get(obj, 'a'), 2, 'responds to content array change'); set(a1, 'foo', true); - deepEqual(get(obj, 'a'), 3, 'still responds to change of property on element'); + equal(get(obj, 'a'), 3, 'still responds to change of property on element'); + + set(a3, 'foo', true); + + equal(get(obj, 'a'), 6, 'still responds to change of property on element'); +}); + +QUnit.test('responds to change of property value on element after replacing array (object promise proxy-settled)', function() { + run(_ => { + set(obj, 'array', ArrayProxy.create({ + content: emberA([a1Remote, a2Remote, a3Remote]) + })); + }); + + equal(get(obj, 'a'), 3, 'value is correct initially'); + + set(a1, 'foo', false); + + equal(get(obj, 'a'), 2, 'responds to change of property on element'); + run(_ => { + set(obj, 'array', ArrayProxy.create({ + content: emberA([a1Remote, a2Remote, a3Remote]) + })); + }); + + equal(get(obj, 'a'), 2, 'responds to content array change'); + + set(a1, 'foo', true); + + equal(get(obj, 'a'), 3, 'still responds to change of property on element'); + + set(a3, 'foo', true); + + equal(get(obj, 'a'), 6, 'still responds to change of property on element'); +}); + +QUnit.test('responds to change of property value on element after replacing array (object promise proxy-un-settled)', function() { + run(_ => { + set(obj, 'array', emberA([ + ObjectProxy.create({ promise: RSVP.Promise.resolve(a1) }), + ObjectProxy.create({ promise: RSVP.Promise.resolve(a2) }), + ObjectProxy.create({ promise: RSVP.Promise.resolve(a3) }) + ])); + + equal(get(obj, 'a'), 0, 'value is correct initially'); + set(a1, 'foo', false); + equal(get(obj, 'a'), 0, 'value is correct initially'); + }); + + equal(get(obj, 'a'), 2, 'responds to change of property on element'); + + run(_ => { + set(obj, 'array', emberA([ + ObjectProxy.create({ promise: RSVP.Promise.resolve(a1) }), + ObjectProxy.create({ promise: RSVP.Promise.resolve(a2) }), + ObjectProxy.create({ promise: RSVP.Promise.resolve(a3) }) + ])); + + equal(get(obj, 'a'), 2, 'expected no change'); + set(a1, 'foo', true); + equal(get(obj, 'a'), 2, 'expected no change'); + set(a3, 'foo', true); + equal(get(obj, 'a'), 2, 'expected no change'); + }); + + equal(get(obj, 'a'), 3, 'still responds to change of property on element'); + + set(a3, 'foo', true); + + equal(get(obj, 'a'), 6, 'still responds to change of property on element'); +}); + +QUnit.test('responds to change of property value on element after replacing array (array promise proxy)', function() { + run(_ => { + set(obj, 'array', ArrayPromiseProxy.create({ + promise: RSVP.Promise.resolve(emberA([a1, a2])) + })); + }); + + equal(get(obj, 'a'), 3, 'value is correct initially'); + + set(a1, 'foo', false); + + equal(get(obj, 'a'), 2, 'responds to change of property on element'); + + run(_ => { + set(obj, 'array', ArrayPromiseProxy.create({ + promise: RSVP.Promise.resolve(emberA([a1, a2, a3])) + })); + }); + + equal(get(obj, 'a'), 2, 'responds to content array change'); + + set(a1, 'foo', true); + + equal(get(obj, 'a'), 3, 'still responds to change of property on element'); + + set(a3, 'foo', true); + + equal(get(obj, 'a'), 6, 'still responds to change of property on element'); +}); + +QUnit.test('responds to change of property value on element after replacing array (array & object promise proxy)', function() { + run(_ => { + set(obj, 'array', ArrayPromiseProxy.create({ + promise: RSVP.Promise.resolve(emberA([a1, a2])) + })); + }); + + equal(get(obj, 'a'), 3, 'value is correct initially'); + + set(a1, 'foo', false); + + equal(get(obj, 'a'), 2, 'responds to change of property on element'); + + run(_ => { + set(obj, 'array', ArrayPromiseProxy.create({ + promise: RSVP.Promise.resolve(emberA([a1, a2, a3])) + })); + }); + + equal(get(obj, 'a'), 2, 'responds to content array change'); + + set(a1, 'foo', true); + + equal(get(obj, 'a'), 3, 'still responds to change of property on element'); + set(a3, 'foo', true); - deepEqual(get(obj, 'a'), 6, 'still responds to change of property on element'); + equal(get(obj, 'a'), 6, 'still responds to change of property on element'); }); From d00bbb943c5a67096e9df4f855eac9404c515207 Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Wed, 20 Jan 2016 21:00:41 -0800 Subject: [PATCH 07/10] mixup cp+chains tests --- .../tests/computed/chains_test.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/ember-runtime/tests/computed/chains_test.js b/packages/ember-runtime/tests/computed/chains_test.js index 06803fd0301..23c280b2e50 100644 --- a/packages/ember-runtime/tests/computed/chains_test.js +++ b/packages/ember-runtime/tests/computed/chains_test.js @@ -207,9 +207,8 @@ QUnit.test('responds to change of property value on element after replacing arra QUnit.test('responds to change of property value on element after replacing array (object promise proxy-un-settled)', function() { run(_ => { set(obj, 'array', emberA([ - ObjectProxy.create({ promise: RSVP.Promise.resolve(a1) }), - ObjectProxy.create({ promise: RSVP.Promise.resolve(a2) }), - ObjectProxy.create({ promise: RSVP.Promise.resolve(a3) }) + ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a1) }), + ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a2) }), ])); equal(get(obj, 'a'), 0, 'value is correct initially'); @@ -221,23 +220,24 @@ QUnit.test('responds to change of property value on element after replacing arra run(_ => { set(obj, 'array', emberA([ - ObjectProxy.create({ promise: RSVP.Promise.resolve(a1) }), - ObjectProxy.create({ promise: RSVP.Promise.resolve(a2) }), - ObjectProxy.create({ promise: RSVP.Promise.resolve(a3) }) + ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a2) }), + ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a3) }) ])); - equal(get(obj, 'a'), 2, 'expected no change'); + equal(get(obj, 'a'), 0, 'expected no change'); set(a1, 'foo', true); - equal(get(obj, 'a'), 2, 'expected no change'); - set(a3, 'foo', true); - equal(get(obj, 'a'), 2, 'expected no change'); + equal(get(obj, 'a'), 0, 'expected no change'); }); - equal(get(obj, 'a'), 3, 'still responds to change of property on element'); + equal(get(obj, 'a'), 2, 'still responds to change of property on element'); set(a3, 'foo', true); - equal(get(obj, 'a'), 6, 'still responds to change of property on element'); + equal(get(obj, 'a'), 5, 'still responds to change of property on element'); + + set(a3, 'foo', false); + + equal(get(obj, 'a'), 2, 'still responds to change of property on element'); }); QUnit.test('responds to change of property value on element after replacing array (array promise proxy)', function() { From 653b340369a45ad5124f2621705e654b73441efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Mu=C3=B1oz?= Date: Fri, 22 Jan 2016 12:34:38 -0500 Subject: [PATCH 08/10] Make JSCS happy --- packages/ember-runtime/tests/computed/chains_test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ember-runtime/tests/computed/chains_test.js b/packages/ember-runtime/tests/computed/chains_test.js index 23c280b2e50..a2c42ba28bd 100644 --- a/packages/ember-runtime/tests/computed/chains_test.js +++ b/packages/ember-runtime/tests/computed/chains_test.js @@ -207,8 +207,8 @@ QUnit.test('responds to change of property value on element after replacing arra QUnit.test('responds to change of property value on element after replacing array (object promise proxy-un-settled)', function() { run(_ => { set(obj, 'array', emberA([ - ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a1) }), - ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a2) }), + ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a1) }), + ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a2) }) ])); equal(get(obj, 'a'), 0, 'value is correct initially'); @@ -220,8 +220,8 @@ QUnit.test('responds to change of property value on element after replacing arra run(_ => { set(obj, 'array', emberA([ - ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a2) }), - ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a3) }) + ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a2) }), + ObjectPromiseProxy.create({ promise: RSVP.Promise.resolve(a3) }) ])); equal(get(obj, 'a'), 0, 'expected no change'); From 8527c249fa6170d189020e6f6a9284784c62fafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Mu=C3=B1oz?= Date: Fri, 22 Jan 2016 12:51:36 -0500 Subject: [PATCH 09/10] Add more tests with Array#replace --- .../tests/computed/chains_test.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/ember-runtime/tests/computed/chains_test.js b/packages/ember-runtime/tests/computed/chains_test.js index a2c42ba28bd..a8ddeb2b7c8 100644 --- a/packages/ember-runtime/tests/computed/chains_test.js +++ b/packages/ember-runtime/tests/computed/chains_test.js @@ -62,6 +62,44 @@ QUnit.test('replace array (overlap)', function() { ok(isWatching(a4, 'foo'), 'AFTER: a4.foo is watched'); }); +QUnit.test('splice array (no overlap)', function() { + let array = emberA([a1, a2]); + + set(obj, 'array', array); + get(obj, 'a'); // kick CP; + + ok(isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); + ok(isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); + ok(!isWatching(a3, 'foo'), 'BEFORE: a3.foo is NOT watched'); + ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); + + array.replace(0, 2, [a3, a4]); + + ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); + ok(!isWatching(a2, 'foo'), 'AFTER: a2.foo is NOT watched'); + ok(isWatching(a3, 'foo'), 'AFTER: a3.foo is watched'); + ok(isWatching(a4, 'foo'), 'AFTER: a4.foo is watched'); +}); + +QUnit.test('splice array (overlap)', function() { + let array = emberA([a1, a2, a3]); + + set(obj, 'array', array); + get(obj, 'a'); // kick CP; + + ok(isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); + ok(isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); + ok(isWatching(a3, 'foo'), 'BEFORE: a3.foo is watched'); + ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); + + array.replace(0, 3, [a2, a3, a4]); + + ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); + ok(isWatching(a2, 'foo'), 'AFTER: a2.foo is watched'); + ok(isWatching(a3, 'foo'), 'AFTER: a3.foo is watched'); + ok(isWatching(a4, 'foo'), 'AFTER: a4.foo is watched'); +}); + let a1Remote, a2Remote, a3Remote; QUnit.module('chain watching (filter)', { From 64a970ceb16eac30493c2d8b62387adefe3b7c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Mu=C3=B1oz?= Date: Fri, 22 Jan 2016 13:49:00 -0500 Subject: [PATCH 10/10] Fix tests --- .../ember-runtime/tests/computed/chains_test.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/ember-runtime/tests/computed/chains_test.js b/packages/ember-runtime/tests/computed/chains_test.js index a8ddeb2b7c8..42627c59c5a 100644 --- a/packages/ember-runtime/tests/computed/chains_test.js +++ b/packages/ember-runtime/tests/computed/chains_test.js @@ -24,13 +24,15 @@ QUnit.module('chain watching', { a4 = { foo: false, id: 4 }; obj = { }; - defineProperty(obj, 'a', computed('array.@each.foo', function() {})); + defineProperty(obj, 'a', computed('array.@each.foo', function() { + return this.array.map(item => get(item, 'foo')); + })); } }); QUnit.test('replace array (no overlap)', function() { set(obj, 'array', emberA([a1, a2])); - get(obj, 'a'); // kick CP; + get(obj, 'a'); // kick CP ok(isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); ok(isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); @@ -38,6 +40,7 @@ QUnit.test('replace array (no overlap)', function() { ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); set(obj, 'array', [a3, a4]); + get(obj, 'a'); // kick CP ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); ok(!isWatching(a2, 'foo'), 'AFTER: a2.foo is NOT watched'); @@ -47,7 +50,7 @@ QUnit.test('replace array (no overlap)', function() { QUnit.test('replace array (overlap)', function() { set(obj, 'array', emberA([a1, a2, a3])); - get(obj, 'a'); // kick CP; + get(obj, 'a'); // kick CP ok(isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); ok(isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); @@ -55,6 +58,7 @@ QUnit.test('replace array (overlap)', function() { ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); set(obj, 'array', [a2, a3, a4]); + get(obj, 'a'); // kick CP ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); ok(isWatching(a2, 'foo'), 'AFTER: a2.foo is watched'); @@ -66,7 +70,7 @@ QUnit.test('splice array (no overlap)', function() { let array = emberA([a1, a2]); set(obj, 'array', array); - get(obj, 'a'); // kick CP; + get(obj, 'a'); // kick CP ok(isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); ok(isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); @@ -74,6 +78,7 @@ QUnit.test('splice array (no overlap)', function() { ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); array.replace(0, 2, [a3, a4]); + get(obj, 'a'); // kick CP ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); ok(!isWatching(a2, 'foo'), 'AFTER: a2.foo is NOT watched'); @@ -85,7 +90,7 @@ QUnit.test('splice array (overlap)', function() { let array = emberA([a1, a2, a3]); set(obj, 'array', array); - get(obj, 'a'); // kick CP; + get(obj, 'a'); // kick CP ok(isWatching(a1, 'foo'), 'BEFORE: a1.foo is watched'); ok(isWatching(a2, 'foo'), 'BEFORE: a2.foo is watched'); @@ -93,6 +98,7 @@ QUnit.test('splice array (overlap)', function() { ok(!isWatching(a4, 'foo'), 'BEFORE: a4.foo is NOT watched'); array.replace(0, 3, [a2, a3, a4]); + get(obj, 'a'); // kick CP ok(!isWatching(a1, 'foo'), 'AFTER: a1.foo is NOT watched'); ok(isWatching(a2, 'foo'), 'AFTER: a2.foo is watched');