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

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

Issue 1023213003: Removing Sky JS Framework tests (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 9 months 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
OLDNEW
(Empty)
1 <html>
2 <import src="../resources/chai.sky" />
3 <import src="../resources/mocha.sky" />
4 <import src="/sky/framework/elements/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 observer = new ObserverTransform(new PathObserver(obj, 'foo'),
338 valueFn);
339 observer.open(callback);
340
341 obj.foo = 2;
342
343 assert.strictEqual(4, observer.discardChanges());
344 assertNoChanges();
345
346 observer.setValue(2);
347 assert.strictEqual(obj.foo, 2);
348
349 obj.foo = 10;
350 assertPathChanges(20, 4);
351
352 observer.close();
353 });
354
355 it('valueFn - object literal', function() {
356 var model = {};
357
358 function valueFn(value) {
359 return [ value ];
360 }
361
362 observer = new ObserverTransform(new PathObserver(model, 'foo'), valueFn);
363 observer.open(callback);
364
365 model.foo = 1;
366 assertPathChanges([1], [undefined]);
367
368 model.foo = 3;
369 assertPathChanges([3], [1]);
370
371 observer.close();
372 });
373
374 it('CompoundObserver - valueFn reduction', function() {
375 var model = { a: 1, b: 2, c: 3 };
376
377 function valueFn(values) {
378 return values.reduce(function(last, cur) {
379 return typeof cur === 'number' ? last + cur : undefined;
380 }, 0);
381 }
382
383 var compound = new CompoundObserver();
384 compound.addPath(model, 'a');
385 compound.addPath(model, 'b');
386 compound.addPath(model, Path.get('c'));
387
388 observer = new ObserverTransform(compound, valueFn);
389 assert.strictEqual(6, observer.open(callback));
390
391 model.a = -10;
392 model.b = 20;
393 model.c = 30;
394 assertPathChanges(40, 6);
395
396 observer.close();
397 });
398 })
399
400 describe('PathObserver Tests', function() {
401
402 beforeEach(doSetup);
403
404 afterEach(doTeardown);
405
406 it('Callback args', function() {
407 var obj = {
408 foo: 'bar'
409 };
410
411 var path = Path.get('foo');
412 var observer = new PathObserver(obj, path);
413
414 var args;
415 observer.open(function() {
416 args = Array.prototype.slice.apply(arguments);
417 });
418
419 obj.foo = 'baz';
420 observer.deliver();
421 assert.strictEqual(args.length, 3);
422 assert.strictEqual(args[0], 'baz');
423 assert.strictEqual(args[1], 'bar');
424 assert.strictEqual(args[2], observer);
425 assert.strictEqual(args[2].path, path);
426 observer.close();
427 });
428
429 it('PathObserver.path', function() {
430 var obj = {
431 foo: 'bar'
432 };
433
434 var path = Path.get('foo');
435 var observer = new PathObserver(obj, 'foo');
436 assert.strictEqual(observer.path, Path.get('foo'));
437 });
438
439
440 it('invalid', function() {
441 var observer = new PathObserver({ a: { b: 1 }} , 'a b');
442 observer.open(callback);
443 assert.strictEqual(undefined, observer.value);
444 observer.deliver();
445 assert.isFalse(callbackInvoked);
446 });
447
448 it('Optional target for callback', function() {
449 var target = {
450 changed: function(value, oldValue) {
451 this.called = true;
452 }
453 };
454 var obj = { foo: 1 };
455 var observer = new PathObserver(obj, 'foo');
456 observer.open(target.changed, target);
457 obj.foo = 2;
458 observer.deliver();
459 assert.isTrue(target.called);
460
461 observer.close();
462 });
463
464 it('Delivery Until No Changes', function() {
465 var obj = { foo: { bar: 5 }};
466 var callbackCount = 0;
467 var observer = new PathObserver(obj, 'foo . bar');
468 observer.open(function() {
469 callbackCount++;
470 if (!obj.foo.bar)
471 return;
472
473 obj.foo.bar--;
474 });
475
476 obj.foo.bar--;
477 observer.deliver();
478
479 assert.equal(5, callbackCount);
480
481 observer.close();
482 });
483
484 it('Path disconnect', function() {
485 var arr = {};
486
487 arr.foo = 'bar';
488 observer = new PathObserver(arr, 'foo');
489 observer.open(callback);
490 arr.foo = 'baz';
491
492 assertPathChanges('baz', 'bar');
493 arr.foo = 'bar';
494
495 observer.close();
496
497 arr.foo = 'boo';
498 assertNoChanges();
499 });
500
501 it('Path discardChanges', function() {
502 var arr = {};
503
504 arr.foo = 'bar';
505 observer = new PathObserver(arr, 'foo');
506 observer.open(callback);
507 arr.foo = 'baz';
508
509 assertPathChanges('baz', 'bar');
510
511 arr.foo = 'bat';
512 observer.discardChanges();
513 assertNoChanges();
514
515 arr.foo = 'bag';
516 assertPathChanges('bag', 'bat');
517 observer.close();
518 });
519
520 it('Path setValue', function() {
521 var obj = {};
522
523 obj.foo = 'bar';
524 observer = new PathObserver(obj, 'foo');
525 observer.open(callback);
526 obj.foo = 'baz';
527
528 observer.setValue('bat');
529 assert.strictEqual(obj.foo, 'bat');
530 assertPathChanges('bat', 'bar');
531
532 observer.setValue('bot');
533 observer.discardChanges();
534 assertNoChanges();
535
536 observer.close();
537 });
538
539 it('Degenerate Values', function() {
540 var emptyPath = Path.get();
541 observer = new PathObserver(null, '');
542 observer.open(callback);
543 assert.equal(null, observer.value);
544 observer.close();
545
546 var foo = {};
547 observer = new PathObserver(foo, '');
548 assert.equal(foo, observer.open(callback));
549 observer.close();
550
551 observer = new PathObserver(3, '');
552 assert.equal(3, observer.open(callback));
553 observer.close();
554
555 observer = new PathObserver(undefined, 'a');
556 assert.equal(undefined, observer.open(callback));
557 observer.close();
558
559 var bar = { id: 23 };
560 observer = new PathObserver(undefined, 'a/3!');
561 assert.equal(undefined, observer.open(callback));
562 observer.close();
563 });
564
565 it('Path NaN', function() {
566 var foo = { val: 1 };
567 observer = new PathObserver(foo, 'val');
568 observer.open(callback);
569 foo.val = 0/0;
570
571 // Can't use assertSummary because deepEqual() will fail with NaN
572 observer.deliver();
573 assert.isTrue(callbackInvoked);
574 assert.isTrue(isNaN(callbackArgs[0]));
575 assert.strictEqual(1, callbackArgs[1]);
576 observer.close();
577 });
578
579 it('Path Set Value Back To Same', function() {
580 var obj = {};
581 var path = Path.get('foo');
582
583 path.setValueFrom(obj, 3);
584 assert.equal(3, obj.foo);
585
586 observer = new PathObserver(obj, 'foo');
587 assert.equal(3, observer.open(callback));
588
589 path.setValueFrom(obj, 2);
590 assert.equal(2, observer.discardChanges());
591
592 path.setValueFrom(obj, 3);
593 assert.equal(3, observer.discardChanges());
594
595 assertNoChanges();
596
597 observer.close();
598 });
599
600 it('Path Triple Equals', function() {
601 var model = { };
602
603 observer = new PathObserver(model, 'foo');
604 observer.open(callback);
605
606 model.foo = null;
607 assertPathChanges(null, undefined);
608
609 model.foo = undefined;
610 assertPathChanges(undefined, null);
611
612 observer.close();
613 });
614
615 it('Path Simple', function() {
616 var model = { };
617
618 observer = new PathObserver(model, 'foo');
619 observer.open(callback);
620
621 model.foo = 1;
622 assertPathChanges(1, undefined);
623
624 model.foo = 2;
625 assertPathChanges(2, 1);
626
627 delete model.foo;
628 assertPathChanges(undefined, 2);
629
630 observer.close();
631 });
632
633 it('Path Simple - path object', function() {
634 var model = { };
635
636 var path = Path.get('foo');
637 observer = new PathObserver(model, path);
638 observer.open(callback);
639
640 model.foo = 1;
641 assertPathChanges(1, undefined);
642
643 model.foo = 2;
644 assertPathChanges(2, 1);
645
646 delete model.foo;
647 assertPathChanges(undefined, 2);
648
649 observer.close();
650 });
651
652 it('Path - root is initially null', function(done) {
653 var model = { };
654
655 var path = Path.get('foo');
656 observer = new PathObserver(model, 'foo.bar');
657 observer.open(callback);
658
659 model.foo = { };
660 then(function() {
661 model.foo.bar = 1;
662
663 }).then(function() {
664 assertPathChanges(1, undefined, true);
665
666 observer.close();
667 done();
668 });
669 });
670
671 it('Path With Indices', function() {
672 var model = [];
673
674 observer = new PathObserver(model, '[0]');
675 observer.open(callback);
676
677 model.push(1);
678 assertPathChanges(1, undefined);
679
680 observer.close();
681 });
682
683 it('Path Observation', function() {
684 var model = {
685 a: {
686 b: {
687 c: 'hello, world'
688 }
689 }
690 };
691
692 observer = new PathObserver(model, 'a.b.c');
693 observer.open(callback);
694
695 model.a.b.c = 'hello, mom';
696 assertPathChanges('hello, mom', 'hello, world');
697
698 model.a.b = {
699 c: 'hello, dad'
700 };
701 assertPathChanges('hello, dad', 'hello, mom');
702
703 model.a = {
704 b: {
705 c: 'hello, you'
706 }
707 };
708 assertPathChanges('hello, you', 'hello, dad');
709
710 model.a.b = 1;
711 assertPathChanges(undefined, 'hello, you');
712
713 // Stop observing
714 observer.close();
715
716 model.a.b = {c: 'hello, back again -- but not observing'};
717 assertNoChanges();
718
719 // Resume observing
720 observer = new PathObserver(model, 'a.b.c');
721 observer.open(callback);
722
723 model.a.b.c = 'hello. Back for reals';
724 assertPathChanges('hello. Back for reals',
725 'hello, back again -- but not observing');
726
727 observer.close();
728 });
729
730 it('Path Set To Same As Prototype', function() {
731 var model = createObject({
732 __proto__: {
733 id: 1
734 }
735 });
736
737 observer = new PathObserver(model, 'id');
738 observer.open(callback);
739 model.id = 1;
740
741 assertNoChanges();
742 observer.close();
743 });
744
745 it('Path Set Read Only', function() {
746 var model = {};
747 Object.defineProperty(model, 'x', {
748 configurable: true,
749 writable: false,
750 value: 1
751 });
752 observer = new PathObserver(model, 'x');
753 observer.open(callback);
754
755 assert.throws(function() {
756 model.x = 2;
757 });
758
759 assertNoChanges();
760 observer.close();
761 });
762
763 it('Path Set Shadows', function() {
764 var model = createObject({
765 __proto__: {
766 x: 1
767 }
768 });
769
770 observer = new PathObserver(model, 'x');
771 observer.open(callback);
772 model.x = 2;
773 assertPathChanges(2, 1);
774 observer.close();
775 });
776
777 it('Delete With Same Value On Prototype', function() {
778 var model = createObject({
779 __proto__: {
780 x: 1,
781 },
782 x: 1
783 });
784
785 observer = new PathObserver(model, 'x');
786 observer.open(callback);
787 delete model.x;
788 assertNoChanges();
789 observer.close();
790 });
791
792 it('Delete With Different Value On Prototype', function() {
793 var model = createObject({
794 __proto__: {
795 x: 1,
796 },
797 x: 2
798 });
799
800 observer = new PathObserver(model, 'x');
801 observer.open(callback);
802 delete model.x;
803 assertPathChanges(1, 2);
804 observer.close();
805 });
806
807 it('Value Change On Prototype', function() {
808 var proto = {
809 x: 1
810 }
811 var model = createObject({
812 __proto__: proto
813 });
814
815 observer = new PathObserver(model, 'x');
816 observer.open(callback);
817 model.x = 2;
818 assertPathChanges(2, 1);
819
820 delete model.x;
821 assertPathChanges(1, 2);
822
823 proto.x = 3;
824 assertPathChanges(3, 1);
825 observer.close();
826 });
827
828 // FIXME: Need test of observing change on proto.
829
830 it('Delete Of Non Configurable', function() {
831 var model = {};
832 Object.defineProperty(model, 'x', {
833 configurable: false,
834 value: 1
835 });
836
837 observer = new PathObserver(model, 'x');
838 observer.open(callback);
839
840 assert.throws(function() {
841 delete model.x;
842 });
843
844 assertNoChanges();
845 observer.close();
846 });
847
848 it('Notify', function() {
849 if (typeof Object.getNotifier !== 'function')
850 return;
851
852 var model = {
853 a: {}
854 }
855
856 var _b = 2;
857
858 Object.defineProperty(model.a, 'b', {
859 get: function() { return _b; },
860 set: function(b) {
861 Object.getNotifier(this).notify({
862 type: 'update',
863 name: 'b',
864 oldValue: _b
865 });
866
867 _b = b;
868 }
869 });
870
871 observer = new PathObserver(model, 'a.b');
872 observer.open(callback);
873 _b = 3;
874 assertPathChanges(3, 2);
875
876 model.a.b = 4; // will be observed.
877 assertPathChanges(4, 3);
878
879 observer.close();
880 });
881
882 it('issue-161', function(done) {
883 var model = { model: 'model' };
884 var ob1 = new PathObserver(model, 'obj.bar');
885 var called = false
886 ob1.open(function() {
887 called = true;
888 });
889
890 var obj2 = new PathObserver(model, 'obj');
891 obj2.open(function() {
892 model.obj.bar = true;
893 });
894
895 model.obj = { 'obj': 'obj' };
896 model.obj.foo = true;
897
898 then(function() {
899 assert.strictEqual(called, true);
900 done();
901 });
902 });
903
904 it('object cycle', function(done) {
905 var model = { a: {}, c: 1 };
906 model.a.b = model;
907
908 var called = 0;
909 new PathObserver(model, 'a.b.c').open(function() {
910 called++;
911 });
912
913 // This change should be detected, even though it's a change to the root
914 // object and isn't a change to `a`.
915 model.c = 42;
916
917 then(function() {
918 assert.equal(called, 1);
919 done();
920 });
921 });
922
923 });
924
925
926 describe('CompoundObserver Tests', function() {
927
928 beforeEach(doSetup);
929
930 afterEach(doTeardown);
931
932 it('Simple', function() {
933 var model = { a: 1, b: 2, c: 3 };
934
935 observer = new CompoundObserver();
936 observer.addPath(model, 'a');
937 observer.addPath(model, 'b');
938 observer.addPath(model, Path.get('c'));
939 observer.open(callback);
940 assertNoChanges();
941
942 var observerCallbackArg = [model, Path.get('a'),
943 model, Path.get('b'),
944 model, Path.get('c')];
945 model.a = -10;
946 model.b = 20;
947 model.c = 30;
948 assertCompoundPathChanges([-10, 20, 30], [1, 2, 3],
949 observerCallbackArg);
950
951 model.a = 'a';
952 model.c = 'c';
953 assertCompoundPathChanges(['a', 20, 'c'], [-10,, 30],
954 observerCallbackArg);
955
956 model.a = 2;
957 model.b = 3;
958 model.c = 4;
959
960 assertCompoundPathChanges([2, 3, 4], ['a', 20, 'c'],
961 observerCallbackArg);
962
963 model.a = 'z';
964 model.b = 'y';
965 model.c = 'x';
966 assert.deepEqual(['z', 'y', 'x'], observer.discardChanges());
967 assertNoChanges();
968
969 assert.strictEqual('z', model.a);
970 assert.strictEqual('y', model.b);
971 assert.strictEqual('x', model.c);
972 assertNoChanges();
973
974 observer.close();
975 });
976
977 it('reportChangesOnOpen', function() {
978 var model = { a: 1, b: 2, c: 3 };
979
980 observer = new CompoundObserver(true);
981 observer.addPath(model, 'a');
982 observer.addPath(model, 'b');
983 observer.addPath(model, Path.get('c'));
984
985 model.a = -10;
986 model.b = 20;
987 observer.open(callback);
988 var observerCallbackArg = [model, Path.get('a'),
989 model, Path.get('b'),
990 model, Path.get('c')];
991 assertCompoundPathChanges([-10, 20, 3], [1, 2, ],
992 observerCallbackArg, true);
993 observer.close();
994 });
995
996 it('Degenerate Values', function() {
997 var model = {};
998 observer = new CompoundObserver();
999 observer.addPath({}, '.'); // invalid path
1000 observer.addPath('obj-value', ''); // empty path
1001 observer.addPath({}, 'foo'); // unreachable
1002 observer.addPath(3, 'bar'); // non-object with non-empty path
1003 var values = observer.open(callback);
1004 assert.strictEqual(4, values.length);
1005 assert.strictEqual(undefined, values[0]);
1006 assert.strictEqual('obj-value', values[1]);
1007 assert.strictEqual(undefined, values[2]);
1008 assert.strictEqual(undefined, values[3]);
1009 observer.close();
1010 });
1011
1012 it('valueFn - return object literal', function() {
1013 var model = { a: 1};
1014
1015 function valueFn(values) {
1016 return {};
1017 }
1018
1019 observer = new CompoundObserver(valueFn);
1020
1021 observer.addPath(model, 'a');
1022 observer.open(callback);
1023 model.a = 2;
1024
1025 observer.deliver();
1026 assert.isTrue(window.dirtyCheckCycleCount === undefined ||
1027 window.dirtyCheckCycleCount === 1);
1028 observer.close();
1029 });
1030
1031 it('reset', function() {
1032 var model = { a: 1, b: 2, c: 3 };
1033 var callCount = 0;
1034 function callback() {
1035 callCount++;
1036 }
1037
1038 observer = new CompoundObserver();
1039
1040 observer.addPath(model, 'a');
1041 observer.addPath(model, 'b');
1042 assert.deepEqual([1, 2], observer.open(callback));
1043
1044 model.a = 2;
1045 observer.deliver();
1046 assert.strictEqual(1, callCount);
1047
1048 model.b = 3;
1049 observer.deliver();
1050 assert.strictEqual(2, callCount);
1051
1052 model.c = 4;
1053 observer.deliver();
1054 assert.strictEqual(2, callCount);
1055
1056 observer.startReset();
1057 observer.addPath(model, 'b');
1058 observer.addPath(model, 'c');
1059 assert.deepEqual([3, 4], observer.finishReset())
1060
1061 model.a = 3;
1062 observer.deliver();
1063 assert.strictEqual(2, callCount);
1064
1065 model.b = 4;
1066 observer.deliver();
1067 assert.strictEqual(3, callCount);
1068
1069 model.c = 5;
1070 observer.deliver();
1071 assert.strictEqual(4, callCount);
1072
1073 observer.close();
1074 });
1075
1076 it('Heterogeneous', function() {
1077 var model = { a: 1, b: 2 };
1078 var otherModel = { c: 3 };
1079
1080 function valueFn(value) { return value * 2; }
1081 function setValueFn(value) { return value / 2; }
1082
1083 var compound = new CompoundObserver;
1084 compound.addPath(model, 'a');
1085 compound.addObserver(new ObserverTransform(new PathObserver(model, 'b'),
1086 valueFn, setValueFn));
1087 compound.addObserver(new PathObserver(otherModel, 'c'));
1088
1089 function combine(values) {
1090 return values[0] + values[1] + values[2];
1091 };
1092 observer = new ObserverTransform(compound, combine);
1093 assert.strictEqual(8, observer.open(callback));
1094
1095 model.a = 2;
1096 model.b = 4;
1097 assertPathChanges(13, 8);
1098
1099 model.b = 10;
1100 otherModel.c = 5;
1101 assertPathChanges(27, 13);
1102
1103 model.a = 20;
1104 model.b = 1;
1105 otherModel.c = 5;
1106 assertNoChanges();
1107
1108 observer.close();
1109 })
1110 });
1111
1112 describe('ArrayObserver Tests', function() {
1113
1114 beforeEach(doSetup);
1115
1116 afterEach(doTeardown);
1117
1118 function ensureNonSparse(arr) {
1119 for (var i = 0; i < arr.length; i++) {
1120 if (i in arr)
1121 continue;
1122 arr[i] = undefined;
1123 }
1124 }
1125
1126 function assertArrayChanges(expectSplices) {
1127 observer.deliver();
1128 var splices = callbackArgs[0];
1129
1130 assert.isTrue(callbackInvoked);
1131
1132 splices.forEach(function(splice) {
1133 ensureNonSparse(splice.removed);
1134 });
1135
1136 expectSplices.forEach(function(splice) {
1137 ensureNonSparse(splice.removed);
1138 });
1139
1140 assert.deepEqual(expectSplices, splices);
1141 callbackArgs = undefined;
1142 callbackInvoked = false;
1143 }
1144
1145 function applySplicesAndAssertDeepEqual(orig, copy) {
1146 observer.deliver();
1147 if (callbackInvoked) {
1148 var splices = callbackArgs[0];
1149 ArrayObserver.applySplices(copy, orig, splices);
1150 }
1151
1152 ensureNonSparse(orig);
1153 ensureNonSparse(copy);
1154 assert.deepEqual(orig, copy);
1155 callbackArgs = undefined;
1156 callbackInvoked = false;
1157 }
1158
1159 function assertEditDistance(orig, expectDistance) {
1160 observer.deliver();
1161 var splices = callbackArgs[0];
1162 var actualDistance = 0;
1163
1164 if (callbackInvoked) {
1165 splices.forEach(function(splice) {
1166 actualDistance += splice.addedCount + splice.removed.length;
1167 });
1168 }
1169
1170 assert.deepEqual(expectDistance, actualDistance);
1171 callbackArgs = undefined;
1172 callbackInvoked = false;
1173 }
1174
1175 function arrayMutationTest(arr, operations) {
1176 var copy = arr.slice();
1177 observer = new ArrayObserver(arr);
1178 observer.open(callback);
1179 operations.forEach(function(op) {
1180 switch(op.name) {
1181 case 'delete':
1182 delete arr[op.index];
1183 break;
1184
1185 case 'update':
1186 arr[op.index] = op.value;
1187 break;
1188
1189 default:
1190 arr[op.name].apply(arr, op.args);
1191 break;
1192 }
1193 });
1194
1195 applySplicesAndAssertDeepEqual(arr, copy);
1196 observer.close();
1197 }
1198
1199 it('Optional target for callback', function() {
1200 var target = {
1201 changed: function(splices) {
1202 this.called = true;
1203 }
1204 };
1205 var obj = [];
1206 var observer = new ArrayObserver(obj);
1207 observer.open(target.changed, target);
1208 obj.length = 1;
1209 observer.deliver();
1210 assert.isTrue(target.called);
1211 observer.close();
1212 });
1213
1214 it('Delivery Until No Changes', function() {
1215 var arr = [0, 1, 2, 3, 4];
1216 var callbackCount = 0;
1217 var observer = new ArrayObserver(arr);
1218 observer.open(function() {
1219 callbackCount++;
1220 arr.shift();
1221 });
1222
1223 arr.shift();
1224 observer.deliver();
1225
1226 assert.equal(5, callbackCount);
1227
1228 observer.close();
1229 });
1230
1231 it('Array disconnect', function() {
1232 var arr = [ 0 ];
1233
1234 observer = new ArrayObserver(arr);
1235 observer.open(callback);
1236
1237 arr[0] = 1;
1238
1239 assertArrayChanges([{
1240 index: 0,
1241 removed: [0],
1242 addedCount: 1
1243 }]);
1244
1245 observer.close();
1246 arr[1] = 2;
1247 assertNoChanges();
1248 });
1249
1250 it('Array discardChanges', function() {
1251 var arr = [];
1252
1253 arr.push(1);
1254 observer = new ArrayObserver(arr);
1255 observer.open(callback);
1256 arr.push(2);
1257
1258 assertArrayChanges([{
1259 index: 1,
1260 removed: [],
1261 addedCount: 1
1262 }]);
1263
1264 arr.push(3);
1265 observer.discardChanges();
1266 assertNoChanges();
1267
1268 arr.pop();
1269 assertArrayChanges([{
1270 index: 2,
1271 removed: [3],
1272 addedCount: 0
1273 }]);
1274 observer.close();
1275 });
1276
1277 it('Array', function() {
1278 var model = [0, 1];
1279
1280 observer = new ArrayObserver(model);
1281 observer.open(callback);
1282
1283 model[0] = 2;
1284
1285 assertArrayChanges([{
1286 index: 0,
1287 removed: [0],
1288 addedCount: 1
1289 }]);
1290
1291 model[1] = 3;
1292 assertArrayChanges([{
1293 index: 1,
1294 removed: [1],
1295 addedCount: 1
1296 }]);
1297
1298 observer.close();
1299 });
1300
1301 it('Array observe non-array throws', function() {
1302 assert.throws(function () {
1303 observer = new ArrayObserver({});
1304 });
1305 });
1306
1307 it('Array Set Same', function() {
1308 var model = [1];
1309
1310 observer = new ArrayObserver(model);
1311 observer.open(callback);
1312
1313 model[0] = 1;
1314 observer.deliver();
1315 assert.isFalse(callbackInvoked);
1316 observer.close();
1317 });
1318
1319 it('Array Splice', function() {
1320 var model = [0, 1]
1321
1322 observer = new ArrayObserver(model);
1323 observer.open(callback);
1324
1325 model.splice(1, 1, 2, 3); // [0, 2, 3]
1326 assertArrayChanges([{
1327 index: 1,
1328 removed: [1],
1329 addedCount: 2
1330 }]);
1331
1332 model.splice(0, 1); // [2, 3]
1333 assertArrayChanges([{
1334 index: 0,
1335 removed: [0],
1336 addedCount: 0
1337 }]);
1338
1339 model.splice();
1340 assertNoChanges();
1341
1342 model.splice(0, 0);
1343 assertNoChanges();
1344
1345 model.splice(0, -1);
1346 assertNoChanges();
1347
1348 model.splice(-1, 0, 1.5); // [2, 1.5, 3]
1349 assertArrayChanges([{
1350 index: 1,
1351 removed: [],
1352 addedCount: 1
1353 }]);
1354
1355 model.splice(3, 0, 0); // [2, 1.5, 3, 0]
1356 assertArrayChanges([{
1357 index: 3,
1358 removed: [],
1359 addedCount: 1
1360 }]);
1361
1362 model.splice(0); // []
1363 assertArrayChanges([{
1364 index: 0,
1365 removed: [2, 1.5, 3, 0],
1366 addedCount: 0
1367 }]);
1368
1369 observer.close();
1370 });
1371
1372 it('Array Splice Truncate And Expand With Length', function() {
1373 var model = ['a', 'b', 'c', 'd', 'e'];
1374
1375 observer = new ArrayObserver(model);
1376 observer.open(callback);
1377
1378 model.length = 2;
1379
1380 assertArrayChanges([{
1381 index: 2,
1382 removed: ['c', 'd', 'e'],
1383 addedCount: 0
1384 }]);
1385
1386 model.length = 5;
1387
1388 assertArrayChanges([{
1389 index: 2,
1390 removed: [],
1391 addedCount: 3
1392 }]);
1393
1394 observer.close();
1395 });
1396
1397 it('Array Splice Delete Too Many', function() {
1398 var model = ['a', 'b', 'c'];
1399
1400 observer = new ArrayObserver(model);
1401 observer.open(callback);
1402
1403 model.splice(2, 3); // ['a', 'b']
1404 assertArrayChanges([{
1405 index: 2,
1406 removed: ['c'],
1407 addedCount: 0
1408 }]);
1409
1410 observer.close();
1411 });
1412
1413 it('Array Length', function() {
1414 var model = [0, 1];
1415
1416 observer = new ArrayObserver(model);
1417 observer.open(callback);
1418
1419 model.length = 5; // [0, 1, , , ,];
1420 assertArrayChanges([{
1421 index: 2,
1422 removed: [],
1423 addedCount: 3
1424 }]);
1425
1426 model.length = 1;
1427 assertArrayChanges([{
1428 index: 1,
1429 removed: [1, , , ,],
1430 addedCount: 0
1431 }]);
1432
1433 model.length = 1;
1434 assertNoChanges();
1435
1436 observer.close();
1437 });
1438
1439 it('Array Push', function() {
1440 var model = [0, 1];
1441
1442 observer = new ArrayObserver(model);
1443 observer.open(callback);
1444
1445 model.push(2, 3); // [0, 1, 2, 3]
1446 assertArrayChanges([{
1447 index: 2,
1448 removed: [],
1449 addedCount: 2
1450 }]);
1451
1452 model.push();
1453 assertNoChanges();
1454
1455 observer.close();
1456 });
1457
1458 it('Array Pop', function() {
1459 var model = [0, 1];
1460
1461 observer = new ArrayObserver(model);
1462 observer.open(callback);
1463
1464 model.pop(); // [0]
1465 assertArrayChanges([{
1466 index: 1,
1467 removed: [1],
1468 addedCount: 0
1469 }]);
1470
1471 model.pop(); // []
1472 assertArrayChanges([{
1473 index: 0,
1474 removed: [0],
1475 addedCount: 0
1476 }]);
1477
1478 model.pop();
1479 assertNoChanges();
1480
1481 observer.close();
1482 });
1483
1484 it('Array Shift', function() {
1485 var model = [0, 1];
1486
1487 observer = new ArrayObserver(model);
1488 observer.open(callback);
1489
1490 model.shift(); // [1]
1491 assertArrayChanges([{
1492 index: 0,
1493 removed: [0],
1494 addedCount: 0
1495 }]);
1496
1497 model.shift(); // []
1498 assertArrayChanges([{
1499 index: 0,
1500 removed: [1],
1501 addedCount: 0
1502 }]);
1503
1504 model.shift();
1505 assertNoChanges();
1506
1507 observer.close();
1508 });
1509
1510 it('Array Unshift', function() {
1511 var model = [0, 1];
1512
1513 observer = new ArrayObserver(model);
1514 observer.open(callback);
1515
1516 model.unshift(-1); // [-1, 0, 1]
1517 assertArrayChanges([{
1518 index: 0,
1519 removed: [],
1520 addedCount: 1
1521 }]);
1522
1523 model.unshift(-3, -2); // []
1524 assertArrayChanges([{
1525 index: 0,
1526 removed: [],
1527 addedCount: 2
1528 }]);
1529
1530 model.unshift();
1531 assertNoChanges();
1532
1533 observer.close();
1534 });
1535
1536 it('Array Tracker Contained', function() {
1537 arrayMutationTest(
1538 ['a', 'b'],
1539 [
1540 { name: 'splice', args: [1, 1] },
1541 { name: 'unshift', args: ['c', 'd', 'e'] },
1542 { name: 'splice', args: [1, 2, 'f'] }
1543 ]
1544 );
1545 });
1546
1547 it('Array Tracker Delete Empty', function() {
1548 arrayMutationTest(
1549 [],
1550 [
1551 { name: 'delete', index: 0 },
1552 { name: 'splice', args: [0, 0, 'a', 'b', 'c'] }
1553 ]
1554 );
1555 });
1556
1557 it('Array Tracker Right Non Overlap', function() {
1558 arrayMutationTest(
1559 ['a', 'b', 'c', 'd'],
1560 [
1561 { name: 'splice', args: [0, 1, 'e'] },
1562 { name: 'splice', args: [2, 1, 'f', 'g'] }
1563 ]
1564 );
1565 });
1566
1567 it('Array Tracker Left Non Overlap', function() {
1568 arrayMutationTest(
1569 ['a', 'b', 'c', 'd'],
1570 [
1571 { name: 'splice', args: [3, 1, 'f', 'g'] },
1572 { name: 'splice', args: [0, 1, 'e'] }
1573 ]
1574 );
1575 });
1576
1577 it('Array Tracker Right Adjacent', function() {
1578 arrayMutationTest(
1579 ['a', 'b', 'c', 'd'],
1580 [
1581 { name: 'splice', args: [1, 1, 'e'] },
1582 { name: 'splice', args: [2, 1, 'f', 'g'] }
1583 ]
1584 );
1585 });
1586
1587 it('Array Tracker Left Adjacent', function() {
1588 arrayMutationTest(
1589 ['a', 'b', 'c', 'd'],
1590 [
1591 { name: 'splice', args: [2, 2, 'e'] },
1592 { name: 'splice', args: [1, 1, 'f', 'g'] }
1593 ]
1594 );
1595 });
1596
1597 it('Array Tracker Right Overlap', function() {
1598 arrayMutationTest(
1599 ['a', 'b', 'c', 'd'],
1600 [
1601 { name: 'splice', args: [1, 1, 'e'] },
1602 { name: 'splice', args: [1, 1, 'f', 'g'] }
1603 ]
1604 );
1605 });
1606
1607 it('Array Tracker Left Overlap', function() {
1608 arrayMutationTest(
1609 ['a', 'b', 'c', 'd'],
1610 [
1611 // a b [e f g] d
1612 { name: 'splice', args: [2, 1, 'e', 'f', 'g'] },
1613 // a [h i j] f g d
1614 { name: 'splice', args: [1, 2, 'h', 'i', 'j'] }
1615 ]
1616 );
1617 });
1618
1619 it('Array Tracker Prefix And Suffix One In', function() {
1620 arrayMutationTest(
1621 ['a', 'b', 'c', 'd'],
1622 [
1623 { name: 'unshift', args: ['z'] },
1624 { name: 'push', arg: ['z'] }
1625 ]
1626 );
1627 });
1628
1629 it('Array Tracker Shift One', function() {
1630 arrayMutationTest(
1631 [16, 15, 15],
1632 [
1633 { name: 'shift', args: ['z'] }
1634 ]
1635 );
1636 });
1637
1638 it('Array Tracker Update Delete', function() {
1639 arrayMutationTest(
1640 ['a', 'b', 'c', 'd'],
1641 [
1642 { name: 'splice', args: [2, 1, 'e', 'f', 'g'] },
1643 { name: 'update', index: 0, value: 'h' },
1644 { name: 'delete', index: 1 }
1645 ]
1646 );
1647 });
1648
1649 it('Array Tracker Update After Delete', function() {
1650 arrayMutationTest(
1651 ['a', 'b', undefined, 'd'],
1652 [
1653 { name: 'update', index: 2, value: 'e' }
1654 ]
1655 );
1656 });
1657
1658 it('Array Tracker Delete Mid Array', function() {
1659 arrayMutationTest(
1660 ['a', 'b', 'c', 'd'],
1661 [
1662 { name: 'delete', index: 2 }
1663 ]
1664 );
1665 });
1666
1667 it('Array Random Case 1', function() {
1668 var model = ['a','b'];
1669 var copy = model.slice();
1670
1671 observer = new ArrayObserver(model);
1672 observer.open(callback);
1673
1674 model.splice(0, 1, 'c', 'd', 'e');
1675 model.splice(4,0,'f');
1676 model.splice(3,2);
1677
1678 applySplicesAndAssertDeepEqual(model, copy);
1679 });
1680
1681 it('Array Random Case 2', function() {
1682 var model = [3,4];
1683 var copy = model.slice();
1684
1685 observer = new ArrayObserver(model);
1686 observer.open(callback);
1687
1688 model.splice(2,0,8);
1689 model.splice(0,1,0,5);
1690 model.splice(2,2);
1691
1692 applySplicesAndAssertDeepEqual(model, copy);
1693 });
1694
1695 it('Array Random Case 3', function() {
1696 var model = [1,3,6];
1697 var copy = model.slice();
1698
1699 observer = new ArrayObserver(model);
1700 observer.open(callback);
1701
1702 model.splice(1,1);
1703 model.splice(0,2,1,7);
1704 model.splice(1,0,3,7);
1705
1706 applySplicesAndAssertDeepEqual(model, copy);
1707 });
1708
1709 it('Array Tracker No Proxies Edits', function() {
1710 var model = [];
1711 observer = new ArrayObserver(model);
1712 observer.open(callback);
1713 model.length = 0;
1714 model.push(1, 2, 3);
1715 assertEditDistance(model, 3);
1716 observer.close();
1717
1718 model = ['x', 'x', 'x', 'x', '1', '2', '3'];
1719 observer = new ArrayObserver(model);
1720 observer.open(callback);
1721 model.length = 0;
1722 model.push('1', '2', '3', 'y', 'y', 'y', 'y');
1723 assertEditDistance(model, 8);
1724 observer.close();
1725
1726 model = ['1', '2', '3', '4', '5'];
1727 observer = new ArrayObserver(model);
1728 observer.open(callback);
1729 model.length = 0;
1730 model.push('a', '2', 'y', 'y', '4', '5', 'z', 'z');
1731 assertEditDistance(model, 7);
1732 observer.close();
1733 });
1734 });
1735 </script>
OLDNEW
« no previous file with comments | « sky/tests/framework/flights-app-pixels-expected.sky ('k') | sky/tests/framework/observe-expected.txt » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698