Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(303)

Side by Side Diff: sky/tests/framework/observe.sky

Issue 694323003: Added tests for observe.sky (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | sky/tests/framework/observe-expected.txt » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 <html>
2 <link rel="import" href="../resources/chai.sky" />
3 <link rel="import" href="../resources/mocha.sky" />
4 <link rel="import" href="/sky/framework/sky-element/observe.sky" as="observe" />
5
6 <script>
7
8 var Path = observe.Path;
9 var PathObserver = observe.PathObserver;
10 var ArrayObserver = observe.ArrayObserver;
11 var ObjectObserver = observe.ObjectObserver;
12 var CompoundObserver = observe.CompoundObserver;
13 var ObserverTransform = observe.ObserverTransform;
14
15 var observer;
16 var callbackArgs = undefined;
17 var callbackInvoked = false;
18
19 function then(fn) {
20 setTimeout(function() {
21 fn();
22 }, 0);
23
24 return {
25 then: function(next) {
26 return then(next);
27 }
28 };
29 }
30
31 function noop() {}
32
33 function callback() {
34 callbackArgs = Array.prototype.slice.apply(arguments);
35 callbackInvoked = true;
36 }
37
38 function doSetup() {}
39 function doTeardown() {
40 callbackInvoked = false;
41 callbackArgs = undefined;
42 }
43
44 function assertNoChanges() {
45 if (observer)
46 observer.deliver();
47 assert.isFalse(callbackInvoked);
48 assert.isUndefined(callbackArgs);
49 }
50
51 function assertPathChanges(expectNewValue, expectOldValue, dontDeliver) {
52 if (!dontDeliver)
53 observer.deliver();
54
55 assert.isTrue(callbackInvoked);
56
57 var newValue = callbackArgs[0];
58 var oldValue = callbackArgs[1];
59 assert.deepEqual(expectNewValue, newValue);
60 assert.deepEqual(expectOldValue, oldValue);
61
62 if (!dontDeliver) {
63 assert.isTrue(window.dirtyCheckCycleCount === undefined ||
64 window.dirtyCheckCycleCount === 1);
65 }
66
67 callbackArgs = undefined;
68 callbackInvoked = false;
69 }
70
71 function assertCompoundPathChanges(expectNewValues, expectOldValues,
72 expectObserved, dontDeliver) {
73 if (!dontDeliver)
74 observer.deliver();
75
76 assert.isTrue(callbackInvoked);
77
78 var newValues = callbackArgs[0];
79 var oldValues = callbackArgs[1];
80 var observed = callbackArgs[2];
81 assert.deepEqual(expectNewValues, newValues);
82 assert.deepEqual(expectOldValues, oldValues);
83 assert.deepEqual(expectObserved, observed);
84
85 if (!dontDeliver) {
86 assert.isTrue(window.dirtyCheckCycleCount === undefined ||
87 window.dirtyCheckCycleCount === 1);
88 }
89
90 callbackArgs = undefined;
91 callbackInvoked = false;
92 }
93
94 var createObject = ('__proto__' in {}) ?
95 function(obj) { return obj; } :
96 function(obj) {
97 var proto = obj.__proto__;
98 if (!proto)
99 return obj;
100 var newObject = Object.create(proto);
101 Object.getOwnPropertyNames(obj).forEach(function(name) {
102 Object.defineProperty(newObject, name,
103 Object.getOwnPropertyDescriptor(obj, name));
104 });
105 return newObject;
106 };
107
108 function assertPath(pathString, expectKeys, expectSerialized) {
109 var path = Path.get(pathString);
110 if (!expectKeys) {
111 assert.isFalse(path.valid);
112 return;
113 }
114
115 assert.deepEqual(Array.prototype.slice.apply(path), expectKeys);
116 assert.strictEqual(path.toString(), expectSerialized);
117 }
118
119 function assertInvalidPath(pathString) {
120 assertPath(pathString);
121 }
122
123 describe('Path', function() {
124 it('constructor throws', function() {
125 assert.throws(function() {
126 new Path('foo')
127 });
128 });
129
130 it('path validity', function() {
131 // invalid path get value is always undefined
132 var p = Path.get('a b');
133 assert.isFalse(p.valid);
134 assert.isUndefined(p.getValueFrom({ a: { b: 2 }}));
135
136 assertPath('', [], '');
137 assertPath(' ', [], '');
138 assertPath(null, [], '');
139 assertPath(undefined, [], '');
140 assertPath('a', ['a'], 'a');
141 assertPath('a.b', ['a', 'b'], 'a.b');
142 assertPath('a. b', ['a', 'b'], 'a.b');
143 assertPath('a .b', ['a', 'b'], 'a.b');
144 assertPath('a . b', ['a', 'b'], 'a.b');
145 assertPath(' a . b ', ['a', 'b'], 'a.b');
146 assertPath('a[0]', ['a', '0'], 'a[0]');
147 assertPath('a [0]', ['a', '0'], 'a[0]');
148 assertPath('a[0][1]', ['a', '0', '1'], 'a[0][1]');
149 assertPath('a [ 0 ] [ 1 ] ', ['a', '0', '1'], 'a[0][1]');
150 assertPath('[1234567890] ', ['1234567890'], '[1234567890]');
151 assertPath(' [1234567890] ', ['1234567890'], '[1234567890]');
152 assertPath('opt0', ['opt0'], 'opt0');
153 assertPath('$foo.$bar._baz', ['$foo', '$bar', '_baz'], '$foo.$bar._baz');
154 assertPath('foo["baz"]', ['foo', 'baz'], 'foo.baz');
155 assertPath('foo["b\\"az"]', ['foo', 'b"az'], 'foo["b\\"az"]');
156 assertPath("foo['b\\'az']", ['foo', "b'az"], 'foo["b\'az"]');
157 assertPath(['a', 'b'], ['a', 'b'], 'a.b');
158 assertPath([''], [''], '[""]');
159
160 function Foo(val) { this.val = val; }
161 Foo.prototype.toString = function() { return 'Foo' + this.val; };
162 assertPath([new Foo('a'), new Foo('b')], ['Fooa', 'Foob'], 'Fooa.Foob');
163
164 assertInvalidPath('.');
165 assertInvalidPath(' . ');
166 assertInvalidPath('..');
167 assertInvalidPath('a[4');
168 assertInvalidPath('a.b.');
169 assertInvalidPath('a,b');
170 assertInvalidPath('a["foo]');
171 assertInvalidPath('[0x04]');
172 assertInvalidPath('[0foo]');
173 assertInvalidPath('[foo-bar]');
174 assertInvalidPath('foo-bar');
175 assertInvalidPath('42');
176 assertInvalidPath('a[04]');
177 assertInvalidPath(' a [ 04 ]');
178 assertInvalidPath(' 42 ');
179 assertInvalidPath('foo["bar]');
180 assertInvalidPath("foo['bar]");
181 });
182
183 it('Paths are interned', function() {
184 var p = Path.get('foo.bar');
185 var p2 = Path.get('foo.bar');
186 assert.strictEqual(p, p2);
187
188 var p3 = Path.get('');
189 var p4 = Path.get('');
190 assert.strictEqual(p3, p4);
191 });
192
193 it('null is empty path', function() {
194 assert.strictEqual(Path.get(''), Path.get(null));
195 });
196
197 it('undefined is empty path', function() {
198 assert.strictEqual(Path.get(undefined), Path.get(null));
199 });
200
201 it('Path.getValueFrom', function() {
202 var obj = {
203 a: {
204 b: {
205 c: 1
206 }
207 }
208 };
209
210 var p1 = Path.get('a');
211 var p2 = Path.get('a.b');
212 var p3 = Path.get('a.b.c');
213
214 assert.strictEqual(obj.a, p1.getValueFrom(obj));
215 assert.strictEqual(obj.a.b, p2.getValueFrom(obj));
216 assert.strictEqual(1, p3.getValueFrom(obj));
217
218 obj.a.b.c = 2;
219 assert.strictEqual(2, p3.getValueFrom(obj));
220
221 obj.a.b = {
222 c: 3
223 };
224 assert.strictEqual(3, p3.getValueFrom(obj));
225
226 obj.a = {
227 b: 4
228 };
229 assert.strictEqual(undefined, p3.getValueFrom(obj));
230 assert.strictEqual(4, p2.getValueFrom(obj));
231 });
232
233 it('Path.setValueFrom', function() {
234 var obj = {};
235 var p2 = Path.get('bar');
236
237 Path.get('foo').setValueFrom(obj, 3);
238 assert.equal(3, obj.foo);
239
240 var bar = { baz: 3 };
241
242 Path.get('bar').setValueFrom(obj, bar);
243 assert.equal(bar, obj.bar);
244
245 var p = Path.get('bar.baz.bat');
246 p.setValueFrom(obj, 'not here');
247 assert.equal(undefined, p.getValueFrom(obj));
248 });
249
250 it('Degenerate Values', function() {
251 var emptyPath = Path.get();
252 var foo = {};
253
254 assert.equal(null, emptyPath.getValueFrom(null));
255 assert.equal(foo, emptyPath.getValueFrom(foo));
256 assert.equal(3, emptyPath.getValueFrom(3));
257 assert.equal(undefined, Path.get('a').getValueFrom(undefined));
258 });
259 });
260
261 describe('Basic Tests', function() {
262
263 it('Exception Doesnt Stop Notification', function() {
264 var model = [1];
265 var count = 0;
266
267 var observer2 = new PathObserver(model, '[0]');
268 observer2.open(function() {
269 count++;
270 throw 'ouch';
271 });
272
273 var observer3 = new ArrayObserver(model);
274 observer3.open(function() {
275 count++;
276 throw 'ouch';
277 });
278
279 model[0] = 2;
280 model[1] = 2;
281
282 observer2.deliver();
283 observer3.deliver();
284
285 assert.equal(2, count);
286
287 observer2.close();
288 observer3.close();
289 });
290
291 it('Can only open once', function() {
292 observer = new PathObserver({ id: 1 }, 'id');
293 observer.open(callback);
294 assert.throws(function() {
295 observer.open(callback);
296 });
297 observer.close();
298
299 observer = new CompoundObserver();
300 observer.open(callback);
301 assert.throws(function() {
302 observer.open(callback);
303 });
304 observer.close();
305
306 observer = new ArrayObserver([], 'id');
307 observer.open(callback);
308 assert.throws(function() {
309 observer.open(callback);
310 });
311 observer.close();
312
313 });
314
315 });
316
317 describe('ObserverTransform', function() {
318
319 it('Close Invokes Close', function() {
320 var count = 0;
321 var observer = {
322 open: function() {},
323 close: function() { count++; }
324 };
325
326 var observer = new ObserverTransform(observer);
327 observer.open();
328 observer.close();
329 assert.strictEqual(1, count);
330 });
331
332 it('valueFn/setValueFn', function() {
333 var obj = { foo: 1 };
334
335 function valueFn(value) { return value * 2; }
336
337 function setValueFn(value) { return value / 2; }
338
339 observer = new ObserverTransform(new PathObserver(obj, 'foo'),
340 valueFn,
341 setValueFn);
342 observer.open(callback);
343
344 obj.foo = 2;
345
346 assert.strictEqual(4, observer.discardChanges());
347 assertNoChanges();
348
349 observer.setValue(2);
350 assert.strictEqual(obj.foo, 1);
351 assertPathChanges(2, 4);
352
353 obj.foo = 10;
354 assertPathChanges(20, 2);
355
356 observer.close();
357 });
358
359 it('valueFn - object literal', function() {
360 var model = {};
361
362 function valueFn(value) {
363 return [ value ];
364 }
365
366 observer = new ObserverTransform(new PathObserver(model, 'foo'), valueFn);
367 observer.open(callback);
368
369 model.foo = 1;
370 assertPathChanges([1], [undefined]);
371
372 model.foo = 3;
373 assertPathChanges([3], [1]);
374
375 observer.close();
376 });
377
378 it('CompoundObserver - valueFn reduction', function() {
379 var model = { a: 1, b: 2, c: 3 };
380
381 function valueFn(values) {
382 return values.reduce(function(last, cur) {
383 return typeof cur === 'number' ? last + cur : undefined;
384 }, 0);
385 }
386
387 var compound = new CompoundObserver();
388 compound.addPath(model, 'a');
389 compound.addPath(model, 'b');
390 compound.addPath(model, Path.get('c'));
391
392 observer = new ObserverTransform(compound, valueFn);
393 assert.strictEqual(6, observer.open(callback));
394
395 model.a = -10;
396 model.b = 20;
397 model.c = 30;
398 assertPathChanges(40, 6);
399
400 observer.close();
401 });
402 })
403
404 describe('PathObserver Tests', function() {
405
406 beforeEach(doSetup);
407
408 afterEach(doTeardown);
409
410 it('Callback args', function() {
411 var obj = {
412 foo: 'bar'
413 };
414
415 var path = Path.get('foo');
416 var observer = new PathObserver(obj, path);
417
418 var args;
419 observer.open(function() {
420 args = Array.prototype.slice.apply(arguments);
421 });
422
423 obj.foo = 'baz';
424 observer.deliver();
425 assert.strictEqual(args.length, 3);
426 assert.strictEqual(args[0], 'baz');
427 assert.strictEqual(args[1], 'bar');
428 assert.strictEqual(args[2], observer);
429 assert.strictEqual(args[2].path, path);
430 observer.close();
431 });
432
433 it('PathObserver.path', function() {
434 var obj = {
435 foo: 'bar'
436 };
437
438 var path = Path.get('foo');
439 var observer = new PathObserver(obj, 'foo');
440 assert.strictEqual(observer.path, Path.get('foo'));
441 });
442
443
444 it('invalid', function() {
445 var observer = new PathObserver({ a: { b: 1 }} , 'a b');
446 observer.open(callback);
447 assert.strictEqual(undefined, observer.value);
448 observer.deliver();
449 assert.isFalse(callbackInvoked);
450 });
451
452 it('Optional target for callback', function() {
453 var target = {
454 changed: function(value, oldValue) {
455 this.called = true;
456 }
457 };
458 var obj = { foo: 1 };
459 var observer = new PathObserver(obj, 'foo');
460 observer.open(target.changed, target);
461 obj.foo = 2;
462 observer.deliver();
463 assert.isTrue(target.called);
464
465 observer.close();
466 });
467
468 it('Delivery Until No Changes', function() {
469 var obj = { foo: { bar: 5 }};
470 var callbackCount = 0;
471 var observer = new PathObserver(obj, 'foo . bar');
472 observer.open(function() {
473 callbackCount++;
474 if (!obj.foo.bar)
475 return;
476
477 obj.foo.bar--;
478 });
479
480 obj.foo.bar--;
481 observer.deliver();
482
483 assert.equal(5, callbackCount);
484
485 observer.close();
486 });
487
488 it('Path disconnect', function() {
489 var arr = {};
490
491 arr.foo = 'bar';
492 observer = new PathObserver(arr, 'foo');
493 observer.open(callback);
494 arr.foo = 'baz';
495
496 assertPathChanges('baz', 'bar');
497 arr.foo = 'bar';
498
499 observer.close();
500
501 arr.foo = 'boo';
502 assertNoChanges();
503 });
504
505 it('Path discardChanges', function() {
506 var arr = {};
507
508 arr.foo = 'bar';
509 observer = new PathObserver(arr, 'foo');
510 observer.open(callback);
511 arr.foo = 'baz';
512
513 assertPathChanges('baz', 'bar');
514
515 arr.foo = 'bat';
516 observer.discardChanges();
517 assertNoChanges();
518
519 arr.foo = 'bag';
520 assertPathChanges('bag', 'bat');
521 observer.close();
522 });
523
524 it('Path setValue', function() {
525 var obj = {};
526
527 obj.foo = 'bar';
528 observer = new PathObserver(obj, 'foo');
529 observer.open(callback);
530 obj.foo = 'baz';
531
532 observer.setValue('bat');
533 assert.strictEqual(obj.foo, 'bat');
534 assertPathChanges('bat', 'bar');
535
536 observer.setValue('bot');
537 observer.discardChanges();
538 assertNoChanges();
539
540 observer.close();
541 });
542
543 it('Degenerate Values', function() {
544 var emptyPath = Path.get();
545 observer = new PathObserver(null, '');
546 observer.open(callback);
547 assert.equal(null, observer.value);
548 observer.close();
549
550 var foo = {};
551 observer = new PathObserver(foo, '');
552 assert.equal(foo, observer.open(callback));
553 observer.close();
554
555 observer = new PathObserver(3, '');
556 assert.equal(3, observer.open(callback));
557 observer.close();
558
559 observer = new PathObserver(undefined, 'a');
560 assert.equal(undefined, observer.open(callback));
561 observer.close();
562
563 var bar = { id: 23 };
564 observer = new PathObserver(undefined, 'a/3!');
565 assert.equal(undefined, observer.open(callback));
566 observer.close();
567 });
568
569 it('Path NaN', function() {
570 var foo = { val: 1 };
571 observer = new PathObserver(foo, 'val');
572 observer.open(callback);
573 foo.val = 0/0;
574
575 // Can't use assertSummary because deepEqual() will fail with NaN
576 observer.deliver();
577 assert.isTrue(callbackInvoked);
578 assert.isTrue(isNaN(callbackArgs[0]));
579 assert.strictEqual(1, callbackArgs[1]);
580 observer.close();
581 });
582
583 it('Path Set Value Back To Same', function() {
584 var obj = {};
585 var path = Path.get('foo');
586
587 path.setValueFrom(obj, 3);
588 assert.equal(3, obj.foo);
589
590 observer = new PathObserver(obj, 'foo');
591 assert.equal(3, observer.open(callback));
592
593 path.setValueFrom(obj, 2);
594 assert.equal(2, observer.discardChanges());
595
596 path.setValueFrom(obj, 3);
597 assert.equal(3, observer.discardChanges());
598
599 assertNoChanges();
600
601 observer.close();
602 });
603
604 it('Path Triple Equals', function() {
605 var model = { };
606
607 observer = new PathObserver(model, 'foo');
608 observer.open(callback);
609
610 model.foo = null;
611 assertPathChanges(null, undefined);
612
613 model.foo = undefined;
614 assertPathChanges(undefined, null);
615
616 observer.close();
617 });
618
619 it('Path Simple', function() {
620 var model = { };
621
622 observer = new PathObserver(model, 'foo');
623 observer.open(callback);
624
625 model.foo = 1;
626 assertPathChanges(1, undefined);
627
628 model.foo = 2;
629 assertPathChanges(2, 1);
630
631 delete model.foo;
632 assertPathChanges(undefined, 2);
633
634 observer.close();
635 });
636
637 it('Path Simple - path object', function() {
638 var model = { };
639
640 var path = Path.get('foo');
641 observer = new PathObserver(model, path);
642 observer.open(callback);
643
644 model.foo = 1;
645 assertPathChanges(1, undefined);
646
647 model.foo = 2;
648 assertPathChanges(2, 1);
649
650 delete model.foo;
651 assertPathChanges(undefined, 2);
652
653 observer.close();
654 });
655
656 it('Path - root is initially null', function(done) {
657 var model = { };
658
659 var path = Path.get('foo');
660 observer = new PathObserver(model, 'foo.bar');
661 observer.open(callback);
662
663 model.foo = { };
664 then(function() {
665 model.foo.bar = 1;
666
667 }).then(function() {
668 assertPathChanges(1, undefined, true);
669
670 observer.close();
671 done();
672 });
673 });
674
675 it('Path With Indices', function() {
676 var model = [];
677
678 observer = new PathObserver(model, '[0]');
679 observer.open(callback);
680
681 model.push(1);
682 assertPathChanges(1, undefined);
683
684 observer.close();
685 });
686
687 it('Path Observation', function() {
688 var model = {
689 a: {
690 b: {
691 c: 'hello, world'
692 }
693 }
694 };
695
696 observer = new PathObserver(model, 'a.b.c');
697 observer.open(callback);
698
699 model.a.b.c = 'hello, mom';
700 assertPathChanges('hello, mom', 'hello, world');
701
702 model.a.b = {
703 c: 'hello, dad'
704 };
705 assertPathChanges('hello, dad', 'hello, mom');
706
707 model.a = {
708 b: {
709 c: 'hello, you'
710 }
711 };
712 assertPathChanges('hello, you', 'hello, dad');
713
714 model.a.b = 1;
715 assertPathChanges(undefined, 'hello, you');
716
717 // Stop observing
718 observer.close();
719
720 model.a.b = {c: 'hello, back again -- but not observing'};
721 assertNoChanges();
722
723 // Resume observing
724 observer = new PathObserver(model, 'a.b.c');
725 observer.open(callback);
726
727 model.a.b.c = 'hello. Back for reals';
728 assertPathChanges('hello. Back for reals',
729 'hello, back again -- but not observing');
730
731 observer.close();
732 });
733
734 it('Path Set To Same As Prototype', function() {
735 var model = createObject({
736 __proto__: {
737 id: 1
738 }
739 });
740
741 observer = new PathObserver(model, 'id');
742 observer.open(callback);
743 model.id = 1;
744
745 assertNoChanges();
746 observer.close();
747 });
748
749 it('Path Set Read Only', function() {
750 var model = {};
751 Object.defineProperty(model, 'x', {
752 configurable: true,
753 writable: false,
754 value: 1
755 });
756 observer = new PathObserver(model, 'x');
757 observer.open(callback);
758
759 assert.throws(function() {
760 model.x = 2;
761 });
762
763 assertNoChanges();
764 observer.close();
765 });
766
767 it('Path Set Shadows', function() {
768 var model = createObject({
769 __proto__: {
770 x: 1
771 }
772 });
773
774 observer = new PathObserver(model, 'x');
775 observer.open(callback);
776 model.x = 2;
777 assertPathChanges(2, 1);
778 observer.close();
779 });
780
781 it('Delete With Same Value On Prototype', function() {
782 var model = createObject({
783 __proto__: {
784 x: 1,
785 },
786 x: 1
787 });
788
789 observer = new PathObserver(model, 'x');
790 observer.open(callback);
791 delete model.x;
792 assertNoChanges();
793 observer.close();
794 });
795
796 it('Delete With Different Value On Prototype', function() {
797 var model = createObject({
798 __proto__: {
799 x: 1,
800 },
801 x: 2
802 });
803
804 observer = new PathObserver(model, 'x');
805 observer.open(callback);
806 delete model.x;
807 assertPathChanges(1, 2);
808 observer.close();
809 });
810
811 it('Value Change On Prototype', function() {
812 var proto = {
813 x: 1
814 }
815 var model = createObject({
816 __proto__: proto
817 });
818
819 observer = new PathObserver(model, 'x');
820 observer.open(callback);
821 model.x = 2;
822 assertPathChanges(2, 1);
823
824 delete model.x;
825 assertPathChanges(1, 2);
826
827 proto.x = 3;
828 assertPathChanges(3, 1);
829 observer.close();
830 });
831
832 // FIXME: Need test of observing change on proto.
833
834 it('Delete Of Non Configurable', function() {
835 var model = {};
836 Object.defineProperty(model, 'x', {
837 configurable: false,
838 value: 1
839 });
840
841 observer = new PathObserver(model, 'x');
842 observer.open(callback);
843
844 assert.throws(function() {
845 delete model.x;
846 });
847
848 assertNoChanges();
849 observer.close();
850 });
851
852 it('Notify', function() {
853 if (typeof Object.getNotifier !== 'function')
854 return;
855
856 var model = {
857 a: {}
858 }
859
860 var _b = 2;
861
862 Object.defineProperty(model.a, 'b', {
863 get: function() { return _b; },
864 set: function(b) {
865 Object.getNotifier(this).notify({
866 type: 'update',
867 name: 'b',
868 oldValue: _b
869 });
870
871 _b = b;
872 }
873 });
874
875 observer = new PathObserver(model, 'a.b');
876 observer.open(callback);
877 _b = 3;
878 assertPathChanges(3, 2);
879
880 model.a.b = 4; // will be observed.
881 assertPathChanges(4, 3);
882
883 observer.close();
884 });
885
886 it('issue-161', function(done) {
887 var model = { model: 'model' };
888 var ob1 = new PathObserver(model, 'obj.bar');
889 var called = false
890 ob1.open(function() {
891 called = true;
892 });
893
894 var obj2 = new PathObserver(model, 'obj');
895 obj2.open(function() {
896 model.obj.bar = true;
897 });
898
899 model.obj = { 'obj': 'obj' };
900 model.obj.foo = true;
901
902 then(function() {
903 assert.strictEqual(called, true);
904 done();
905 });
906 });
907
908 it('object cycle', function(done) {
909 var model = { a: {}, c: 1 };
910 model.a.b = model;
911
912 var called = 0;
913 new PathObserver(model, 'a.b.c').open(function() {
914 called++;
915 });
916
917 // This change should be detected, even though it's a change to the root
918 // object and isn't a change to `a`.
919 model.c = 42;
920
921 then(function() {
922 assert.equal(called, 1);
923 done();
924 });
925 });
926
927 });
928
929
930 describe('CompoundObserver Tests', function() {
931
932 beforeEach(doSetup);
933
934 afterEach(doTeardown);
935
936 it('Simple', function() {
937 var model = { a: 1, b: 2, c: 3 };
938
939 observer = new CompoundObserver();
940 observer.addPath(model, 'a');
941 observer.addPath(model, 'b');
942 observer.addPath(model, Path.get('c'));
943 observer.open(callback);
944 assertNoChanges();
945
946 var observerCallbackArg = [model, Path.get('a'),
947 model, Path.get('b'),
948 model, Path.get('c')];
949 model.a = -10;
950 model.b = 20;
951 model.c = 30;
952 assertCompoundPathChanges([-10, 20, 30], [1, 2, 3],
953 observerCallbackArg);
954
955 model.a = 'a';
956 model.c = 'c';
957 assertCompoundPathChanges(['a', 20, 'c'], [-10,, 30],
958 observerCallbackArg);
959
960 model.a = 2;
961 model.b = 3;
962 model.c = 4;
963
964 assertCompoundPathChanges([2, 3, 4], ['a', 20, 'c'],
965 observerCallbackArg);
966
967 model.a = 'z';
968 model.b = 'y';
969 model.c = 'x';
970 assert.deepEqual(['z', 'y', 'x'], observer.discardChanges());
971 assertNoChanges();
972
973 assert.strictEqual('z', model.a);
974 assert.strictEqual('y', model.b);
975 assert.strictEqual('x', model.c);
976 assertNoChanges();
977
978 observer.close();
979 });
980
981 it('reportChangesOnOpen', function() {
982 var model = { a: 1, b: 2, c: 3 };
983
984 observer = new CompoundObserver(true);
985 observer.addPath(model, 'a');
986 observer.addPath(model, 'b');
987 observer.addPath(model, Path.get('c'));
988
989 model.a = -10;
990 model.b = 20;
991 observer.open(callback);
992 var observerCallbackArg = [model, Path.get('a'),
993 model, Path.get('b'),
994 model, Path.get('c')];
995 assertCompoundPathChanges([-10, 20, 3], [1, 2, ],
996 observerCallbackArg, true);
997 observer.close();
998 });
999
1000 it('Degenerate Values', function() {
1001 var model = {};
1002 observer = new CompoundObserver();
1003 observer.addPath({}, '.'); // invalid path
1004 observer.addPath('obj-value', ''); // empty path
1005 observer.addPath({}, 'foo'); // unreachable
1006 observer.addPath(3, 'bar'); // non-object with non-empty path
1007 var values = observer.open(callback);
1008 assert.strictEqual(4, values.length);
1009 assert.strictEqual(undefined, values[0]);
1010 assert.strictEqual('obj-value', values[1]);
1011 assert.strictEqual(undefined, values[2]);
1012 assert.strictEqual(undefined, values[3]);
1013 observer.close();
1014 });
1015
1016 it('valueFn - return object literal', function() {
1017 var model = { a: 1};
1018
1019 function valueFn(values) {
1020 return {};
1021 }
1022
1023 observer = new CompoundObserver(valueFn);
1024
1025 observer.addPath(model, 'a');
1026 observer.open(callback);
1027 model.a = 2;
1028
1029 observer.deliver();
1030 assert.isTrue(window.dirtyCheckCycleCount === undefined ||
1031 window.dirtyCheckCycleCount === 1);
1032 observer.close();
1033 });
1034
1035 it('reset', function() {
1036 var model = { a: 1, b: 2, c: 3 };
1037 var callCount = 0;
1038 function callback() {
1039 callCount++;
1040 }
1041
1042 observer = new CompoundObserver();
1043
1044 observer.addPath(model, 'a');
1045 observer.addPath(model, 'b');
1046 assert.deepEqual([1, 2], observer.open(callback));
1047
1048 model.a = 2;
1049 observer.deliver();
1050 assert.strictEqual(1, callCount);
1051
1052 model.b = 3;
1053 observer.deliver();
1054 assert.strictEqual(2, callCount);
1055
1056 model.c = 4;
1057 observer.deliver();
1058 assert.strictEqual(2, callCount);
1059
1060 observer.startReset();
1061 observer.addPath(model, 'b');
1062 observer.addPath(model, 'c');
1063 assert.deepEqual([3, 4], observer.finishReset())
1064
1065 model.a = 3;
1066 observer.deliver();
1067 assert.strictEqual(2, callCount);
1068
1069 model.b = 4;
1070 observer.deliver();
1071 assert.strictEqual(3, callCount);
1072
1073 model.c = 5;
1074 observer.deliver();
1075 assert.strictEqual(4, callCount);
1076
1077 observer.close();
1078 });
1079
1080 it('Heterogeneous', function() {
1081 var model = { a: 1, b: 2 };
1082 var otherModel = { c: 3 };
1083
1084 function valueFn(value) { return value * 2; }
1085 function setValueFn(value) { return value / 2; }
1086
1087 var compound = new CompoundObserver;
1088 compound.addPath(model, 'a');
1089 compound.addObserver(new ObserverTransform(new PathObserver(model, 'b'),
1090 valueFn, setValueFn));
1091 compound.addObserver(new PathObserver(otherModel, 'c'));
1092
1093 function combine(values) {
1094 return values[0] + values[1] + values[2];
1095 };
1096 observer = new ObserverTransform(compound, combine);
1097 assert.strictEqual(8, observer.open(callback));
1098
1099 model.a = 2;
1100 model.b = 4;
1101 assertPathChanges(13, 8);
1102
1103 model.b = 10;
1104 otherModel.c = 5;
1105 assertPathChanges(27, 13);
1106
1107 model.a = 20;
1108 model.b = 1;
1109 otherModel.c = 5;
1110 assertNoChanges();
1111
1112 observer.close();
1113 })
1114 });
1115
1116 describe('ArrayObserver Tests', function() {
1117
1118 beforeEach(doSetup);
1119
1120 afterEach(doTeardown);
1121
1122 function ensureNonSparse(arr) {
1123 for (var i = 0; i < arr.length; i++) {
1124 if (i in arr)
1125 continue;
1126 arr[i] = undefined;
1127 }
1128 }
1129
1130 function assertArrayChanges(expectSplices) {
1131 observer.deliver();
1132 var splices = callbackArgs[0];
1133
1134 assert.isTrue(callbackInvoked);
1135
1136 splices.forEach(function(splice) {
1137 ensureNonSparse(splice.removed);
1138 });
1139
1140 expectSplices.forEach(function(splice) {
1141 ensureNonSparse(splice.removed);
1142 });
1143
1144 assert.deepEqual(expectSplices, splices);
1145 callbackArgs = undefined;
1146 callbackInvoked = false;
1147 }
1148
1149 function applySplicesAndAssertDeepEqual(orig, copy) {
1150 observer.deliver();
1151 if (callbackInvoked) {
1152 var splices = callbackArgs[0];
1153 ArrayObserver.applySplices(copy, orig, splices);
1154 }
1155
1156 ensureNonSparse(orig);
1157 ensureNonSparse(copy);
1158 assert.deepEqual(orig, copy);
1159 callbackArgs = undefined;
1160 callbackInvoked = false;
1161 }
1162
1163 function assertEditDistance(orig, expectDistance) {
1164 observer.deliver();
1165 var splices = callbackArgs[0];
1166 var actualDistance = 0;
1167
1168 if (callbackInvoked) {
1169 splices.forEach(function(splice) {
1170 actualDistance += splice.addedCount + splice.removed.length;
1171 });
1172 }
1173
1174 assert.deepEqual(expectDistance, actualDistance);
1175 callbackArgs = undefined;
1176 callbackInvoked = false;
1177 }
1178
1179 function arrayMutationTest(arr, operations) {
1180 var copy = arr.slice();
1181 observer = new ArrayObserver(arr);
1182 observer.open(callback);
1183 operations.forEach(function(op) {
1184 switch(op.name) {
1185 case 'delete':
1186 delete arr[op.index];
1187 break;
1188
1189 case 'update':
1190 arr[op.index] = op.value;
1191 break;
1192
1193 default:
1194 arr[op.name].apply(arr, op.args);
1195 break;
1196 }
1197 });
1198
1199 applySplicesAndAssertDeepEqual(arr, copy);
1200 observer.close();
1201 }
1202
1203 it('Optional target for callback', function() {
1204 var target = {
1205 changed: function(splices) {
1206 this.called = true;
1207 }
1208 };
1209 var obj = [];
1210 var observer = new ArrayObserver(obj);
1211 observer.open(target.changed, target);
1212 obj.length = 1;
1213 observer.deliver();
1214 assert.isTrue(target.called);
1215 observer.close();
1216 });
1217
1218 it('Delivery Until No Changes', function() {
1219 var arr = [0, 1, 2, 3, 4];
1220 var callbackCount = 0;
1221 var observer = new ArrayObserver(arr);
1222 observer.open(function() {
1223 callbackCount++;
1224 arr.shift();
1225 });
1226
1227 arr.shift();
1228 observer.deliver();
1229
1230 assert.equal(5, callbackCount);
1231
1232 observer.close();
1233 });
1234
1235 it('Array disconnect', function() {
1236 var arr = [ 0 ];
1237
1238 observer = new ArrayObserver(arr);
1239 observer.open(callback);
1240
1241 arr[0] = 1;
1242
1243 assertArrayChanges([{
1244 index: 0,
1245 removed: [0],
1246 addedCount: 1
1247 }]);
1248
1249 observer.close();
1250 arr[1] = 2;
1251 assertNoChanges();
1252 });
1253
1254 it('Array discardChanges', function() {
1255 var arr = [];
1256
1257 arr.push(1);
1258 observer = new ArrayObserver(arr);
1259 observer.open(callback);
1260 arr.push(2);
1261
1262 assertArrayChanges([{
1263 index: 1,
1264 removed: [],
1265 addedCount: 1
1266 }]);
1267
1268 arr.push(3);
1269 observer.discardChanges();
1270 assertNoChanges();
1271
1272 arr.pop();
1273 assertArrayChanges([{
1274 index: 2,
1275 removed: [3],
1276 addedCount: 0
1277 }]);
1278 observer.close();
1279 });
1280
1281 it('Array', function() {
1282 var model = [0, 1];
1283
1284 observer = new ArrayObserver(model);
1285 observer.open(callback);
1286
1287 model[0] = 2;
1288
1289 assertArrayChanges([{
1290 index: 0,
1291 removed: [0],
1292 addedCount: 1
1293 }]);
1294
1295 model[1] = 3;
1296 assertArrayChanges([{
1297 index: 1,
1298 removed: [1],
1299 addedCount: 1
1300 }]);
1301
1302 observer.close();
1303 });
1304
1305 it('Array observe non-array throws', function() {
1306 assert.throws(function () {
1307 observer = new ArrayObserver({});
1308 });
1309 });
1310
1311 it('Array Set Same', function() {
1312 var model = [1];
1313
1314 observer = new ArrayObserver(model);
1315 observer.open(callback);
1316
1317 model[0] = 1;
1318 observer.deliver();
1319 assert.isFalse(callbackInvoked);
1320 observer.close();
1321 });
1322
1323 it('Array Splice', function() {
1324 var model = [0, 1]
1325
1326 observer = new ArrayObserver(model);
1327 observer.open(callback);
1328
1329 model.splice(1, 1, 2, 3); // [0, 2, 3]
1330 assertArrayChanges([{
1331 index: 1,
1332 removed: [1],
1333 addedCount: 2
1334 }]);
1335
1336 model.splice(0, 1); // [2, 3]
1337 assertArrayChanges([{
1338 index: 0,
1339 removed: [0],
1340 addedCount: 0
1341 }]);
1342
1343 model.splice();
1344 assertNoChanges();
1345
1346 model.splice(0, 0);
1347 assertNoChanges();
1348
1349 model.splice(0, -1);
1350 assertNoChanges();
1351
1352 model.splice(-1, 0, 1.5); // [2, 1.5, 3]
1353 assertArrayChanges([{
1354 index: 1,
1355 removed: [],
1356 addedCount: 1
1357 }]);
1358
1359 model.splice(3, 0, 0); // [2, 1.5, 3, 0]
1360 assertArrayChanges([{
1361 index: 3,
1362 removed: [],
1363 addedCount: 1
1364 }]);
1365
1366 model.splice(0); // []
1367 assertArrayChanges([{
1368 index: 0,
1369 removed: [2, 1.5, 3, 0],
1370 addedCount: 0
1371 }]);
1372
1373 observer.close();
1374 });
1375
1376 it('Array Splice Truncate And Expand With Length', function() {
1377 var model = ['a', 'b', 'c', 'd', 'e'];
1378
1379 observer = new ArrayObserver(model);
1380 observer.open(callback);
1381
1382 model.length = 2;
1383
1384 assertArrayChanges([{
1385 index: 2,
1386 removed: ['c', 'd', 'e'],
1387 addedCount: 0
1388 }]);
1389
1390 model.length = 5;
1391
1392 assertArrayChanges([{
1393 index: 2,
1394 removed: [],
1395 addedCount: 3
1396 }]);
1397
1398 observer.close();
1399 });
1400
1401 it('Array Splice Delete Too Many', function() {
1402 var model = ['a', 'b', 'c'];
1403
1404 observer = new ArrayObserver(model);
1405 observer.open(callback);
1406
1407 model.splice(2, 3); // ['a', 'b']
1408 assertArrayChanges([{
1409 index: 2,
1410 removed: ['c'],
1411 addedCount: 0
1412 }]);
1413
1414 observer.close();
1415 });
1416
1417 it('Array Length', function() {
1418 var model = [0, 1];
1419
1420 observer = new ArrayObserver(model);
1421 observer.open(callback);
1422
1423 model.length = 5; // [0, 1, , , ,];
1424 assertArrayChanges([{
1425 index: 2,
1426 removed: [],
1427 addedCount: 3
1428 }]);
1429
1430 model.length = 1;
1431 assertArrayChanges([{
1432 index: 1,
1433 removed: [1, , , ,],
1434 addedCount: 0
1435 }]);
1436
1437 model.length = 1;
1438 assertNoChanges();
1439
1440 observer.close();
1441 });
1442
1443 it('Array Push', function() {
1444 var model = [0, 1];
1445
1446 observer = new ArrayObserver(model);
1447 observer.open(callback);
1448
1449 model.push(2, 3); // [0, 1, 2, 3]
1450 assertArrayChanges([{
1451 index: 2,
1452 removed: [],
1453 addedCount: 2
1454 }]);
1455
1456 model.push();
1457 assertNoChanges();
1458
1459 observer.close();
1460 });
1461
1462 it('Array Pop', function() {
1463 var model = [0, 1];
1464
1465 observer = new ArrayObserver(model);
1466 observer.open(callback);
1467
1468 model.pop(); // [0]
1469 assertArrayChanges([{
1470 index: 1,
1471 removed: [1],
1472 addedCount: 0
1473 }]);
1474
1475 model.pop(); // []
1476 assertArrayChanges([{
1477 index: 0,
1478 removed: [0],
1479 addedCount: 0
1480 }]);
1481
1482 model.pop();
1483 assertNoChanges();
1484
1485 observer.close();
1486 });
1487
1488 it('Array Shift', function() {
1489 var model = [0, 1];
1490
1491 observer = new ArrayObserver(model);
1492 observer.open(callback);
1493
1494 model.shift(); // [1]
1495 assertArrayChanges([{
1496 index: 0,
1497 removed: [0],
1498 addedCount: 0
1499 }]);
1500
1501 model.shift(); // []
1502 assertArrayChanges([{
1503 index: 0,
1504 removed: [1],
1505 addedCount: 0
1506 }]);
1507
1508 model.shift();
1509 assertNoChanges();
1510
1511 observer.close();
1512 });
1513
1514 it('Array Unshift', function() {
1515 var model = [0, 1];
1516
1517 observer = new ArrayObserver(model);
1518 observer.open(callback);
1519
1520 model.unshift(-1); // [-1, 0, 1]
1521 assertArrayChanges([{
1522 index: 0,
1523 removed: [],
1524 addedCount: 1
1525 }]);
1526
1527 model.unshift(-3, -2); // []
1528 assertArrayChanges([{
1529 index: 0,
1530 removed: [],
1531 addedCount: 2
1532 }]);
1533
1534 model.unshift();
1535 assertNoChanges();
1536
1537 observer.close();
1538 });
1539
1540 it('Array Tracker Contained', function() {
1541 arrayMutationTest(
1542 ['a', 'b'],
1543 [
1544 { name: 'splice', args: [1, 1] },
1545 { name: 'unshift', args: ['c', 'd', 'e'] },
1546 { name: 'splice', args: [1, 2, 'f'] }
1547 ]
1548 );
1549 });
1550
1551 it('Array Tracker Delete Empty', function() {
1552 arrayMutationTest(
1553 [],
1554 [
1555 { name: 'delete', index: 0 },
1556 { name: 'splice', args: [0, 0, 'a', 'b', 'c'] }
1557 ]
1558 );
1559 });
1560
1561 it('Array Tracker Right Non Overlap', function() {
1562 arrayMutationTest(
1563 ['a', 'b', 'c', 'd'],
1564 [
1565 { name: 'splice', args: [0, 1, 'e'] },
1566 { name: 'splice', args: [2, 1, 'f', 'g'] }
1567 ]
1568 );
1569 });
1570
1571 it('Array Tracker Left Non Overlap', function() {
1572 arrayMutationTest(
1573 ['a', 'b', 'c', 'd'],
1574 [
1575 { name: 'splice', args: [3, 1, 'f', 'g'] },
1576 { name: 'splice', args: [0, 1, 'e'] }
1577 ]
1578 );
1579 });
1580
1581 it('Array Tracker Right Adjacent', function() {
1582 arrayMutationTest(
1583 ['a', 'b', 'c', 'd'],
1584 [
1585 { name: 'splice', args: [1, 1, 'e'] },
1586 { name: 'splice', args: [2, 1, 'f', 'g'] }
1587 ]
1588 );
1589 });
1590
1591 it('Array Tracker Left Adjacent', function() {
1592 arrayMutationTest(
1593 ['a', 'b', 'c', 'd'],
1594 [
1595 { name: 'splice', args: [2, 2, 'e'] },
1596 { name: 'splice', args: [1, 1, 'f', 'g'] }
1597 ]
1598 );
1599 });
1600
1601 it('Array Tracker Right Overlap', function() {
1602 arrayMutationTest(
1603 ['a', 'b', 'c', 'd'],
1604 [
1605 { name: 'splice', args: [1, 1, 'e'] },
1606 { name: 'splice', args: [1, 1, 'f', 'g'] }
1607 ]
1608 );
1609 });
1610
1611 it('Array Tracker Left Overlap', function() {
1612 arrayMutationTest(
1613 ['a', 'b', 'c', 'd'],
1614 [
1615 // a b [e f g] d
1616 { name: 'splice', args: [2, 1, 'e', 'f', 'g'] },
1617 // a [h i j] f g d
1618 { name: 'splice', args: [1, 2, 'h', 'i', 'j'] }
1619 ]
1620 );
1621 });
1622
1623 it('Array Tracker Prefix And Suffix One In', function() {
1624 arrayMutationTest(
1625 ['a', 'b', 'c', 'd'],
1626 [
1627 { name: 'unshift', args: ['z'] },
1628 { name: 'push', arg: ['z'] }
1629 ]
1630 );
1631 });
1632
1633 it('Array Tracker Shift One', function() {
1634 arrayMutationTest(
1635 [16, 15, 15],
1636 [
1637 { name: 'shift', args: ['z'] }
1638 ]
1639 );
1640 });
1641
1642 it('Array Tracker Update Delete', function() {
1643 arrayMutationTest(
1644 ['a', 'b', 'c', 'd'],
1645 [
1646 { name: 'splice', args: [2, 1, 'e', 'f', 'g'] },
1647 { name: 'update', index: 0, value: 'h' },
1648 { name: 'delete', index: 1 }
1649 ]
1650 );
1651 });
1652
1653 it('Array Tracker Update After Delete', function() {
1654 arrayMutationTest(
1655 ['a', 'b', undefined, 'd'],
1656 [
1657 { name: 'update', index: 2, value: 'e' }
1658 ]
1659 );
1660 });
1661
1662 it('Array Tracker Delete Mid Array', function() {
1663 arrayMutationTest(
1664 ['a', 'b', 'c', 'd'],
1665 [
1666 { name: 'delete', index: 2 }
1667 ]
1668 );
1669 });
1670
1671 it('Array Random Case 1', function() {
1672 var model = ['a','b'];
1673 var copy = model.slice();
1674
1675 observer = new ArrayObserver(model);
1676 observer.open(callback);
1677
1678 model.splice(0, 1, 'c', 'd', 'e');
1679 model.splice(4,0,'f');
1680 model.splice(3,2);
1681
1682 applySplicesAndAssertDeepEqual(model, copy);
1683 });
1684
1685 it('Array Random Case 2', function() {
1686 var model = [3,4];
1687 var copy = model.slice();
1688
1689 observer = new ArrayObserver(model);
1690 observer.open(callback);
1691
1692 model.splice(2,0,8);
1693 model.splice(0,1,0,5);
1694 model.splice(2,2);
1695
1696 applySplicesAndAssertDeepEqual(model, copy);
1697 });
1698
1699 it('Array Random Case 3', function() {
1700 var model = [1,3,6];
1701 var copy = model.slice();
1702
1703 observer = new ArrayObserver(model);
1704 observer.open(callback);
1705
1706 model.splice(1,1);
1707 model.splice(0,2,1,7);
1708 model.splice(1,0,3,7);
1709
1710 applySplicesAndAssertDeepEqual(model, copy);
1711 });
1712
1713 function ArrayFuzzer() {}
1714
1715 ArrayFuzzer.valMax = 16;
1716 ArrayFuzzer.arrayLengthMax = 128;
1717 ArrayFuzzer.operationCount = 64;
1718
1719 function randDouble(start, end) {
1720 return Math.random()*(end-start) + start;
1721 }
1722
1723 function randInt(start, end) {
1724 return Math.round(randDouble(start, end));
1725 }
1726
1727 function randASCIIChar() {
1728 return String.fromCharCode(randInt(32, 126));
1729 }
1730
1731 function randValue() {
1732 switch(randInt(0, 5)) {
1733 case 0:
1734 return {};
1735 case 1:
1736 return undefined;
1737 case 2:
1738 return null;
1739 case 3:
1740 return randInt(0, ArrayFuzzer.valMax);
1741 case 4:
1742 return randDouble(0, ArrayFuzzer.valMax);
1743 case 5:
1744 return randASCIIChar();
1745 }
1746 }
1747
1748 function randArray() {
1749 var args = [];
1750 var count = randInt(0, ArrayFuzzer.arrayLengthMax);
1751 while(count-- > 0)
1752 args.push(randValue());
1753
1754 return args;
1755 }
1756
1757 function randomArrayOperation(arr) {
1758 function empty() {
1759 return [];
1760 }
1761
1762 var operations = {
1763 push: randArray,
1764 unshift: randArray,
1765 pop: empty,
1766 shift: empty,
1767 splice: function() {
1768 var args = [];
1769 args.push(randInt(-arr.length*2, arr.length*2), randInt(0, arr.length*2) );
1770 args = args.concat(randArray());
1771 return args;
1772 }
1773 };
1774
1775 // Do a splice once for each of the other operations.
1776 var operationList = ['splice', 'update',
1777 'splice', 'delete',
1778 'splice', 'push',
1779 'splice', 'pop',
1780 'splice', 'shift',
1781 'splice', 'unshift'];
1782
1783 var op = {
1784 name: operationList[randInt(0, operationList.length - 1)]
1785 };
1786
1787 switch(op.name) {
1788 case 'delete':
1789 op.index = randInt(0, arr.length - 1);
1790 delete arr[op.index];
1791 break;
1792
1793 case 'update':
1794 op.index = randInt(0, arr.length);
1795 op.value = randValue();
1796 arr[op.index] = op.value;
1797 break;
1798
1799 default:
1800 op.args = operations[op.name]();
1801 arr[op.name].apply(arr, op.args);
1802 break;
1803 }
1804
1805 return op;
1806 }
1807
1808 function randomArrayOperations(arr, count) {
1809 var ops = []
1810 for (var i = 0; i < count; i++) {
1811 ops.push(randomArrayOperation(arr));
1812 }
1813
1814 return ops;
1815 }
1816
1817 ArrayFuzzer.prototype.go = function() {
1818 var orig = this.arr = randArray();
1819 randomArrayOperations(this.arr, ArrayFuzzer.operationCount);
1820 var copy = this.copy = this.arr.slice();
1821 this.origCopy = this.copy.slice();
1822
1823 var observer = new ArrayObserver(this.arr);
1824 observer.open(function(splices) {
1825 ArrayObserver.applySplices(copy, orig, splices);
1826 });
1827
1828 this.ops = randomArrayOperations(this.arr, ArrayFuzzer.operationCount);
1829 observer.deliver();
1830 observer.close();
1831 }
1832
1833
1834 it('Array Tracker Fuzzer', function() {
1835 var testCount = 64;
1836
1837 for (var i = 0; i < testCount; i++) {
1838 var fuzzer = new ArrayFuzzer();
1839 fuzzer.go();
1840 ensureNonSparse(fuzzer.arr);
1841 ensureNonSparse(fuzzer.copy);
1842 assert.deepEqual(fuzzer.arr, fuzzer.copy);
1843 }
1844 });
1845
1846 it('Array Tracker No Proxies Edits', function() {
1847 var model = [];
1848 observer = new ArrayObserver(model);
1849 observer.open(callback);
1850 model.length = 0;
1851 model.push(1, 2, 3);
1852 assertEditDistance(model, 3);
1853 observer.close();
1854
1855 model = ['x', 'x', 'x', 'x', '1', '2', '3'];
1856 observer = new ArrayObserver(model);
1857 observer.open(callback);
1858 model.length = 0;
1859 model.push('1', '2', '3', 'y', 'y', 'y', 'y');
1860 assertEditDistance(model, 8);
1861 observer.close();
1862
1863 model = ['1', '2', '3', '4', '5'];
1864 observer = new ArrayObserver(model);
1865 observer.open(callback);
1866 model.length = 0;
1867 model.push('a', '2', 'y', 'y', '4', '5', 'z', 'z');
1868 assertEditDistance(model, 7);
1869 observer.close();
1870 });
1871 });
1872 </script>
OLDNEW
« no previous file with comments | « no previous file | sky/tests/framework/observe-expected.txt » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698