Skip to content

Commit 2f24f89

Browse files
author
Chris Garrett
committed
[BUGFIX release] Ensure tag updates are buffered
This update ensures that various forms of tag updates are buffered, and also adds tests for edge cases concerning combinations of computed properties, lazy dependencies, and observers that triggered this assertion.
1 parent 3196d7a commit 2f24f89

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed

packages/@ember/-internals/metal/tests/computed_test.js

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,3 +1042,186 @@ moduleFor(
10421042
}
10431043
}
10441044
);
1045+
1046+
class LazyObject {
1047+
value = 123;
1048+
1049+
@computed('_value')
1050+
get value() {
1051+
return get(this, '_value');
1052+
}
1053+
1054+
set value(value) {
1055+
set(this, '_value', value);
1056+
}
1057+
1058+
static create() {
1059+
let obj = new LazyObject();
1060+
1061+
// ensure a tag exists for the value computed
1062+
get(obj, 'value');
1063+
1064+
return obj;
1065+
}
1066+
}
1067+
1068+
moduleFor(
1069+
'computed - lazy dependencies',
1070+
class extends AbstractTestCase {
1071+
'@test computed properties with lazy dependencies work as expected'(assert) {
1072+
let calledCount = 0;
1073+
let lazyObject = LazyObject.create();
1074+
1075+
class ObjectWithLazyDep {
1076+
@computed('lazyObject.value')
1077+
get someProp() {
1078+
return ++calledCount;
1079+
}
1080+
1081+
@computed('otherProp')
1082+
get lazyObject() {
1083+
return lazyObject;
1084+
}
1085+
}
1086+
1087+
let obj = new ObjectWithLazyDep();
1088+
1089+
// Get someProp and setup the lazy dependency
1090+
assert.equal(obj.someProp, 1, 'called the first time');
1091+
assert.equal(obj.someProp, 1, 'returned cached value the second time');
1092+
1093+
// Finish the lazy dependency
1094+
assert.equal(obj.lazyObject.value, 123, 'lazyObject returns expected value');
1095+
assert.equal(
1096+
obj.someProp,
1097+
1,
1098+
'someProp was not dirtied by propB being calculated for the first time'
1099+
);
1100+
1101+
set(lazyObject, 'value', 456);
1102+
assert.equal(obj.someProp, 2, 'someProp dirtied by lazyObject.value changing');
1103+
1104+
set(lazyObject, 'value', 789);
1105+
assert.equal(
1106+
obj.someProp,
1107+
3,
1108+
'someProp still dirtied by otherProp when lazyObject.value is dirty'
1109+
);
1110+
}
1111+
1112+
'@test computed properties with lazy dependencies do not dirty until dependencies have been read at least once'(
1113+
assert
1114+
) {
1115+
let calledCount = 0;
1116+
let lazyObject = LazyObject.create();
1117+
1118+
class ObjectWithLazyDep {
1119+
@computed('lazyObject.value')
1120+
get someProp() {
1121+
return ++calledCount;
1122+
}
1123+
1124+
@computed('otherProp')
1125+
get lazyObject() {
1126+
return lazyObject;
1127+
}
1128+
}
1129+
1130+
let obj = new ObjectWithLazyDep();
1131+
1132+
assert.equal(obj.someProp, 1, 'called the first time');
1133+
assert.equal(obj.someProp, 1, 'returned cached value the second time');
1134+
1135+
// dirty the object value before the dependency has been finished
1136+
set(lazyObject, 'value', 456);
1137+
1138+
assert.equal(obj.lazyObject.value, 456, 'propB returns expected value');
1139+
assert.equal(
1140+
obj.someProp,
1141+
1,
1142+
'someProp was not dirtied by propB being dirtied before it has been calculated'
1143+
);
1144+
}
1145+
1146+
'@test computed properties with lazy dependencies work correctly if lazy dependency is more recent'(
1147+
assert
1148+
) {
1149+
let calledCount = 0;
1150+
let lazyObject = LazyObject.create();
1151+
1152+
class ObjectWithLazyDep {
1153+
@computed('lazyObject.value')
1154+
get someProp() {
1155+
return ++calledCount;
1156+
}
1157+
1158+
@computed('otherProp')
1159+
get lazyObject() {
1160+
return lazyObject;
1161+
}
1162+
}
1163+
1164+
let obj = new ObjectWithLazyDep();
1165+
1166+
set(lazyObject, 'value', 456);
1167+
1168+
assert.equal(obj.someProp, 1, 'called the first time');
1169+
assert.equal(obj.someProp, 1, 'returned cached value the second time');
1170+
1171+
assert.equal(obj.lazyObject.value, 456, 'lazyObject returns expected value');
1172+
1173+
assert.equal(
1174+
obj.someProp,
1175+
1,
1176+
'someProp was not dirtied by lazyObject being dirtied before it has been calculated'
1177+
);
1178+
}
1179+
}
1180+
);
1181+
1182+
moduleFor(
1183+
'computed - observer interop',
1184+
class extends AbstractTestCase {
1185+
async '@test observers that do not consume computed properties still work'(assert) {
1186+
assert.expect(2);
1187+
1188+
class Foo {
1189+
otherProp = 123;
1190+
1191+
@computed('otherProp')
1192+
get someProp() {
1193+
return this.otherProp;
1194+
}
1195+
}
1196+
1197+
let foo = new Foo();
1198+
1199+
addObserver(
1200+
foo,
1201+
'otherProp',
1202+
foo,
1203+
() => assert.ok(true, 'otherProp observer called when it was changed'),
1204+
false
1205+
);
1206+
1207+
addObserver(
1208+
foo,
1209+
'someProp',
1210+
foo,
1211+
() => assert.ok(false, 'someProp observer called when it was not changed'),
1212+
false
1213+
);
1214+
1215+
set(foo, 'otherProp', 456);
1216+
1217+
await runLoopSettled();
1218+
1219+
assert.equal(get(foo, 'someProp'), 456, '');
1220+
1221+
addObserver(foo, 'anotherProp', foo, () => {}, false);
1222+
set(foo, 'anotherProp', 123);
1223+
1224+
await runLoopSettled();
1225+
}
1226+
}
1227+
);

0 commit comments

Comments
 (0)