1 // ========================================================================== 2 // Project: SproutCore Costello - Property Observing Library 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 /*globals module test ok equals same CoreTest */ 9 10 sc_require('debug/test_suites/array/base'); 11 12 SC.ArraySuite.define(function(T) { 13 14 var expected, array, observer, rangeObserver ; 15 16 // .......................................................... 17 // MODULE: isDeep = YES 18 // 19 module(T.desc("RangeObserver Methods"), { 20 setup: function() { 21 expected = T.objects(10); 22 array = T.newObject(expected); 23 24 observer = T.observer(); 25 rangeObserver = array.addRangeObserver(SC.IndexSet.create(2,3), 26 observer, observer.rangeDidChange, null, NO); 27 28 }, 29 30 teardown: function() { 31 T.destroyObject(array); 32 } 33 }); 34 35 test("returns RangeObserver object", function() { 36 ok(rangeObserver && rangeObserver.isRangeObserver, 'returns a range observer object'); 37 }); 38 39 // NOTE: Deep Property Observing is disabled for SproutCore 1.0 40 // 41 // // .......................................................... 42 // // EDIT PROPERTIES 43 // // 44 // 45 // test("editing property on object in range should fire observer", function() { 46 // var obj = array.objectAt(3); 47 // obj.set('foo', 'BAR'); 48 // observer.expectRangeChange(array, obj, 'foo', SC.IndexSet.create(3)); 49 // }); 50 // 51 // test("editing property on object outside of range should NOT fire observer", function() { 52 // var obj = array.objectAt(0); 53 // obj.set('foo', 'BAR'); 54 // equals(observer.callCount, 0, 'observer should not fire'); 55 // }); 56 // 57 // 58 // test("updating property after changing observer range", function() { 59 // array.updateRangeObserver(rangeObserver, SC.IndexSet.create(8,2)); 60 // observer.callCount = 0 ;// reset b/c callback should happen here 61 // 62 // var obj = array.objectAt(3); 63 // obj.set('foo', 'BAR'); 64 // equals(observer.callCount, 0, 'modifying object in old range should not fire observer'); 65 // 66 // obj = array.objectAt(9); 67 // obj.set('foo', 'BAR'); 68 // observer.expectRangeChange(array, obj, 'foo', SC.IndexSet.create(9)); 69 // 70 // }); 71 // 72 // test("updating a property after removing an range should not longer update", function() { 73 // array.removeRangeObserver(rangeObserver); 74 // 75 // observer.callCount = 0 ;// reset b/c callback should happen here 76 // 77 // var obj = array.objectAt(3); 78 // obj.set('foo', 'BAR'); 79 // equals(observer.callCount, 0, 'modifying object in old range should not fire observer'); 80 // 81 // }); 82 83 // .......................................................... 84 // REPLACE 85 // 86 87 test("replacing object in range fires observer with index set covering only the effected item", function() { 88 array.replace(2, 1, T.objects(1)); 89 observer.expectRangeChange(array, null, '[]', SC.IndexSet.create(2,1)); 90 }); 91 92 test("replacing object before range", function() { 93 array.replace(0, 1, T.objects(1)); 94 equals(observer.callCount, 0, 'observer should not fire'); 95 }); 96 97 test("replacing object after range", function() { 98 array.replace(9, 1, T.objects(1)); 99 equals(observer.callCount, 0, 'observer should not fire'); 100 }); 101 102 test("updating range should be reflected by replace operations", function() { 103 array.updateRangeObserver(rangeObserver, SC.IndexSet.create(9,1)); 104 105 observer.callCount = 0 ; 106 array.replace(2, 1, T.objects(1)); 107 equals(observer.callCount, 0, 'observer should not fire'); 108 109 observer.callCount = 0 ; 110 array.replace(0, 1, T.objects(1)); 111 equals(observer.callCount, 0, 'observer should not fire'); 112 113 observer.callCount = 0 ; 114 array.replace(9, 1, T.objects(1)); 115 observer.expectRangeChange(array, null, '[]', SC.IndexSet.create(9)); 116 }); 117 118 test("removing range should no longer fire observers", function() { 119 array.removeRangeObserver(rangeObserver); 120 121 observer.callCount = 0 ; 122 array.replace(2, 1, T.objects(1)); 123 equals(observer.callCount, 0, 'observer should not fire'); 124 125 observer.callCount = 0 ; 126 array.replace(0, 1, T.objects(1)); 127 equals(observer.callCount, 0, 'observer should not fire'); 128 129 observer.callCount = 0 ; 130 array.replace(9, 1, T.objects(1)); 131 equals(observer.callCount, 0, 'observer should not fire'); 132 }); 133 134 // .......................................................... 135 // GROUPED CHANGES 136 // 137 138 test("grouping property changes should notify observer only once at end with single IndexSet", function() { 139 140 array.beginPropertyChanges(); 141 array.replace(2, 1, T.objects(1)); 142 array.replace(4, 1, T.objects(1)); 143 array.endPropertyChanges(); 144 145 var set = SC.IndexSet.create().add(2).add(4); // both edits 146 observer.expectRangeChange(array, null, '[]', set); 147 }); 148 149 test("should notify observer when some but not all grouped changes are inside range", function() { 150 151 array.beginPropertyChanges(); 152 array.replace(2, 1, T.objects(1)); 153 array.replace(9, 1, T.objects(1)); 154 array.endPropertyChanges(); 155 156 var set = SC.IndexSet.create().add(2).add(9); // both edits 157 observer.expectRangeChange(array, null, '[]', set); 158 }); 159 160 test("should NOT notify observer when grouping changes all outside of observer", function() { 161 162 array.beginPropertyChanges(); 163 array.replace(0, 1, T.objects(1)); 164 array.replace(9, 1, T.objects(1)); 165 array.endPropertyChanges(); 166 167 equals(observer.callCount, 0, 'observer should not fire'); 168 }); 169 170 // .......................................................... 171 // INSERTING 172 // 173 174 test("insertAt in range fires observer with index set covering edit to end of array", function() { 175 var newItem = T.objects(1)[0], 176 set = SC.IndexSet.create(3,array.get('length')-2); 177 178 array.insertAt(3, newItem); 179 observer.expectRangeChange(array, null, '[]', set); 180 }); 181 182 test("insertAt BEFORE range fires observer with index set covering edit to end of array", function() { 183 var newItem = T.objects(1)[0], 184 set = SC.IndexSet.create(0,array.get('length')+1); 185 186 array.insertAt(0, newItem); 187 observer.expectRangeChange(array, null, '[]', set); 188 }); 189 190 test("insertAt AFTER range does not fire observer", function() { 191 var newItem = T.objects(1)[0]; 192 193 array.insertAt(9, newItem); 194 equals(observer.callCount, 0, 'observer should not fire'); 195 }); 196 197 // .......................................................... 198 // REMOVING 199 // 200 201 test("removeAt IN range fires observer with index set covering edit to end of array plus delta", function() { 202 var set = SC.IndexSet.create(3,array.get('length')-3); 203 array.removeAt(3); 204 observer.expectRangeChange(array, null, '[]', set); 205 }); 206 207 test("removeAt BEFORE range fires observer with index set covering edit to end of array plus delta", function() { 208 var set = SC.IndexSet.create(0,array.get('length')); 209 array.removeAt(0); 210 observer.expectRangeChange(array, null, '[]', set); 211 }); 212 213 test("removeAt AFTER range does not fire observer", function() { 214 array.removeAt(9); 215 equals(observer.callCount, 0, 'observer should not fire'); 216 }); 217 218 219 220 221 // .......................................................... 222 // MODULE: No explicit range 223 // 224 module(T.desc("RangeObserver Methods - No explicit range"), { 225 setup: function() { 226 expected = T.objects(10); 227 array = T.newObject(expected); 228 229 observer = T.observer(); 230 rangeObserver = array.addRangeObserver(null, observer, 231 observer.rangeDidChange, null, NO); 232 233 }, 234 235 teardown: function() { 236 T.destroyObject(array); 237 } 238 }); 239 240 test("returns RangeObserver object", function() { 241 ok(rangeObserver && rangeObserver.isRangeObserver, 'returns a range observer object'); 242 }); 243 244 // .......................................................... 245 // REPLACE 246 // 247 248 test("replacing object in range fires observer with index set covering only the effected item", function() { 249 array.replace(2, 1, T.objects(1)); 250 observer.expectRangeChange(array, null, '[]', SC.IndexSet.create(2,1)); 251 }); 252 253 test("replacing at start of array", function() { 254 array.replace(0, 1, T.objects(1)); 255 observer.expectRangeChange(array, null, '[]', SC.IndexSet.create(0,1)); 256 }); 257 258 test("replacing object at end of array", function() { 259 array.replace(9, 1, T.objects(1)); 260 observer.expectRangeChange(array, null, '[]', SC.IndexSet.create(9,1)); 261 }); 262 263 test("removing range should no longer fire observers", function() { 264 array.removeRangeObserver(rangeObserver); 265 266 observer.callCount = 0 ; 267 array.replace(2, 1, T.objects(1)); 268 equals(observer.callCount, 0, 'observer should not fire'); 269 270 observer.callCount = 0 ; 271 array.replace(0, 1, T.objects(1)); 272 equals(observer.callCount, 0, 'observer should not fire'); 273 274 observer.callCount = 0 ; 275 array.replace(9, 1, T.objects(1)); 276 equals(observer.callCount, 0, 'observer should not fire'); 277 }); 278 279 // .......................................................... 280 // GROUPED CHANGES 281 // 282 283 test("grouping property changes should notify observer only once at end with single IndexSet", function() { 284 285 array.beginPropertyChanges(); 286 array.replace(2, 1, T.objects(1)); 287 array.replace(4, 1, T.objects(1)); 288 array.endPropertyChanges(); 289 290 var set = SC.IndexSet.create().add(2).add(4); // both edits 291 observer.expectRangeChange(array, null, '[]', set); 292 }); 293 294 // .......................................................... 295 // INSERTING 296 // 297 298 test("insertAt in range fires observer with index set covering edit to end of array", function() { 299 var newItem = T.objects(1)[0], 300 set = SC.IndexSet.create(3,array.get('length')-2); 301 302 array.insertAt(3, newItem); 303 observer.expectRangeChange(array, null, '[]', set); 304 }); 305 306 test("adding object fires observer", function() { 307 var newItem = T.objects(1)[0]; 308 var set = SC.IndexSet.create(array.get('length')); 309 310 array.pushObject(newItem); 311 observer.expectRangeChange(array, null, '[]', set); 312 }); 313 314 // .......................................................... 315 // REMOVING 316 // 317 318 test("removeAt fires observer with index set covering edit to end of array", function() { 319 var set = SC.IndexSet.create(3,array.get('length')-3); 320 array.removeAt(3); 321 observer.expectRangeChange(array, null, '[]', set); 322 }); 323 324 test("popObject fires observer with index set covering removed range", function() { 325 var set = SC.IndexSet.create(array.get('length')-1); 326 array.popObject(); 327 observer.expectRangeChange(array, null, '[]', set); 328 }); 329 330 331 // .......................................................... 332 // MODULE: isDeep = NO 333 // 334 module(T.desc("RangeObserver Methods - isDeep NO"), { 335 setup: function() { 336 expected = T.objects(10); 337 array = T.newObject(expected); 338 339 observer = T.observer(); 340 rangeObserver = array.addRangeObserver(SC.IndexSet.create(2,3), 341 observer, observer.rangeDidChange, null, NO); 342 343 }, 344 345 teardown: function() { 346 T.destroyObject(array); 347 } 348 }); 349 350 test("editing property on object at any point should not fire observer", function() { 351 352 var indexes = [0,3,9], 353 loc = 3, 354 obj,idx; 355 356 while(--loc>=0) { 357 idx = indexes[loc]; 358 obj = array.objectAt(idx); 359 obj.set('foo', 'BAR'); 360 equals(observer.callCount, 0, 'observer should not fire when editing object at index %@'.fmt(idx)); 361 } 362 }); 363 364 test("replacing object in range fires observer with index set", function() { 365 array.replace(2, 1, T.objects(1)); 366 observer.expectRangeChange(array, null, '[]', SC.IndexSet.create(2,1)); 367 }); 368 369 370 }); 371 372