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

Side by Side Diff: pkg/template_binding/test/template_binding_test.dart

Issue 815843002: delete template binding from the repo (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library template_binding.test.template_binding_test;
6
7 import 'dart:async';
8 import 'dart:html';
9 import 'dart:js' show JsObject;
10 import 'dart:math' as math;
11 import 'package:observe/observe.dart';
12 import 'package:template_binding/template_binding.dart';
13 import 'package:unittest/html_config.dart';
14 import 'package:unittest/unittest.dart';
15
16 // TODO(jmesserly): merge this file?
17 import 'binding_syntax.dart' show syntaxTests;
18 import 'utils.dart';
19
20 // Note: this file ported from TemplateBinding's tests/tests.js
21
22 // TODO(jmesserly): submit a small cleanup patch to original. I fixed some
23 // cases where "div" and "t" were unintentionally using the JS global scope;
24 // look for "assertNodesAre".
25
26 main() => dirtyCheckZone().run(() {
27 useHtmlConfiguration();
28
29 setUp(() {
30 document.body.append(testDiv = new DivElement());
31 });
32
33 tearDown(() {
34 testDiv.remove();
35 clearAllTemplates(testDiv);
36 testDiv = null;
37 });
38
39 test('MutationObserver is supported', () {
40 expect(MutationObserver.supported, true, reason: 'polyfill was loaded.');
41 });
42
43 group('Template', templateInstantiationTests);
44
45 group('Binding Delegate API', () {
46 group('with Observable', () {
47 syntaxTests(([f, b]) => new FooBarModel(f, b));
48 });
49
50 group('with ChangeNotifier', () {
51 syntaxTests(([f, b]) => new FooBarNotifyModel(f, b));
52 });
53 });
54
55 group('Compat', compatTests);
56 });
57
58 var expando = new Expando('test');
59 void addExpandos(node) {
60 while (node != null) {
61 expando[node] = node.text;
62 node = node.nextNode;
63 }
64 }
65
66 void checkExpandos(node) {
67 expect(node, isNotNull);
68 while (node != null) {
69 expect(expando[node], node.text);
70 node = node.nextNode;
71 }
72 }
73
74 templateInstantiationTests() {
75 // Dart note: renamed some of these tests to have unique names
76
77 test('accessing bindingDelegate getter without Bind', () {
78 var div = createTestHtml('<template>');
79 var template = div.firstChild;
80 expect(templateBind(template).bindingDelegate, null);
81 });
82
83 test('Bind - simple', () {
84 var div = createTestHtml('<template bind={{}}>text</template>');
85 templateBind(div.firstChild).model = {};
86 return new Future(() {
87 expect(div.nodes.length, 2);
88 expect(div.nodes.last.text, 'text');
89
90 // Dart note: null is used instead of undefined to clear the template.
91 templateBind(div.firstChild).model = null;
92
93 }).then(endOfMicrotask).then((_) {
94 expect(div.nodes.length, 1);
95 templateBind(div.firstChild).model = 123;
96
97 }).then(endOfMicrotask).then((_) {
98 expect(div.nodes.length, 2);
99 expect(div.nodes.last.text, 'text');
100 });
101 });
102
103 test('oneTime-Bind', () {
104 var div = createTestHtml('<template bind="[[ bound ]]">text</template>');
105 var model = toObservable({'bound': 1});
106 templateBind(div.firstChild).model = model;
107 return new Future(() {
108 expect(div.nodes.length, 2);
109 expect(div.nodes.last.text, 'text');
110
111 model['bound'] = false;
112
113 }).then(endOfMicrotask).then((_) {
114 expect(div.nodes.length, 2);
115 expect(div.nodes.last.text, 'text');
116 });
117 });
118
119 test('Bind - no parent', () {
120 var div = createTestHtml('<template bind>text</template>');
121 var template = div.firstChild;
122 template.remove();
123
124 templateBind(template).model = {};
125 return new Future(() {
126 expect(template.nodes.length, 0);
127 expect(template.nextNode, null);
128 });
129 });
130
131 test('Bind - no defaultView', () {
132 var div = createTestHtml('<template bind>text</template>');
133 var template = div.firstChild;
134 var doc = document.implementation.createHtmlDocument('');
135 doc.adoptNode(div);
136 templateBind(template).model = {};
137 return new Future(() => expect(div.nodes.length, 2));
138 });
139
140 test('Empty Bind', () {
141 var div = createTestHtml('<template bind>text</template>');
142 var template = div.firstChild;
143 templateBind(template).model = {};
144 return new Future(() {
145 expect(div.nodes.length, 2);
146 expect(div.nodes.last.text, 'text');
147 });
148 });
149
150 test('Bind If', () {
151 var div = createTestHtml(
152 '<template bind="{{ bound }}" if="{{ predicate }}">'
153 'value:{{ value }}'
154 '</template>');
155 // Dart note: predicate changed from 0->null because 0 isn't falsey in Dart.
156 // See https://code.google.com/p/dart/issues/detail?id=11956
157 // Changed bound from null->1 since null is equivalent to JS undefined,
158 // and would cause the template to not be expanded.
159 var m = toObservable({ 'predicate': null, 'bound': 1 });
160 var template = div.firstChild;
161 bool errorSeen = false;
162 runZoned(() {
163 templateBind(template).model = m;
164 }, onError: (e, s) {
165 _expectNoSuchMethod(e);
166 errorSeen = true;
167 });
168 return new Future(() {
169 expect(div.nodes.length, 1);
170
171 m['predicate'] = 1;
172
173 expect(errorSeen, isFalse);
174 }).then(nextMicrotask).then((_) {
175 expect(errorSeen, isTrue);
176 expect(div.nodes.length, 1);
177
178 m['bound'] = toObservable({ 'value': 2 });
179
180 }).then(endOfMicrotask).then((_) {
181 expect(div.nodes.length, 2);
182 expect(div.lastChild.text, 'value:2');
183
184 m['bound']['value'] = 3;
185
186 }).then(endOfMicrotask).then((_) {
187 expect(div.nodes.length, 2);
188 expect(div.lastChild.text, 'value:3');
189
190 templateBind(template).model = null;
191
192 }).then(endOfMicrotask).then((_) {
193 expect(div.nodes.length, 1);
194 });
195 });
196
197 test('Bind oneTime-If - predicate false', () {
198 var div = createTestHtml(
199 '<template bind="{{ bound }}" if="[[ predicate ]]">'
200 'value:{{ value }}'
201 '</template>');
202 // Dart note: predicate changed from 0->null because 0 isn't falsey in Dart.
203 // See https://code.google.com/p/dart/issues/detail?id=11956
204 // Changed bound from null->1 since null is equivalent to JS undefined,
205 // and would cause the template to not be expanded.
206 var m = toObservable({ 'predicate': null, 'bound': 1 });
207 var template = div.firstChild;
208 templateBind(template).model = m;
209
210 return new Future(() {
211 expect(div.nodes.length, 1);
212
213 m['predicate'] = 1;
214
215 }).then(endOfMicrotask).then((_) {
216 expect(div.nodes.length, 1);
217
218 m['bound'] = toObservable({ 'value': 2 });
219
220 }).then(endOfMicrotask).then((_) {
221 expect(div.nodes.length, 1);
222
223 m['bound']['value'] = 3;
224
225 }).then(endOfMicrotask).then((_) {
226 expect(div.nodes.length, 1);
227
228 templateBind(template).model = null;
229
230 }).then(endOfMicrotask).then((_) {
231 expect(div.nodes.length, 1);
232 });
233 });
234
235 test('Bind oneTime-If - predicate true', () {
236 var div = createTestHtml(
237 '<template bind="{{ bound }}" if="[[ predicate ]]">'
238 'value:{{ value }}'
239 '</template>');
240
241 // Dart note: changed bound from null->1 since null is equivalent to JS
242 // undefined, and would cause the template to not be expanded.
243 var m = toObservable({ 'predicate': 1, 'bound': 1 });
244 var template = div.firstChild;
245 bool errorSeen = false;
246 runZoned(() {
247 templateBind(template).model = m;
248 }, onError: (e, s) {
249 _expectNoSuchMethod(e);
250 errorSeen = true;
251 });
252
253 return new Future(() {
254 expect(div.nodes.length, 1);
255 m['bound'] = toObservable({ 'value': 2 });
256 expect(errorSeen, isTrue);
257 }).then(endOfMicrotask).then((_) {
258 expect(div.nodes.length, 2);
259 expect(div.lastChild.text, 'value:2');
260
261 m['bound']['value'] = 3;
262
263 }).then(endOfMicrotask).then((_) {
264 expect(div.nodes.length, 2);
265 expect(div.lastChild.text, 'value:3');
266
267 m['predicate'] = null; // will have no effect
268
269 }).then(endOfMicrotask).then((_) {
270 expect(div.nodes.length, 2);
271 expect(div.lastChild.text, 'value:3');
272
273 templateBind(template).model = null;
274
275 }).then(endOfMicrotask).then((_) {
276 expect(div.nodes.length, 1);
277 });
278 });
279
280 test('oneTime-Bind If', () {
281 var div = createTestHtml(
282 '<template bind="[[ bound ]]" if="{{ predicate }}">'
283 'value:{{ value }}'
284 '</template>');
285
286 var m = toObservable({'predicate': null, 'bound': {'value': 2}});
287 var template = div.firstChild;
288 templateBind(template).model = m;
289
290 return new Future(() {
291 expect(div.nodes.length, 1);
292
293 m['predicate'] = 1;
294
295 }).then(endOfMicrotask).then((_) {
296 expect(div.nodes.length, 2);
297 expect(div.lastChild.text, 'value:2');
298
299 m['bound']['value'] = 3;
300
301 }).then(endOfMicrotask).then((_) {
302 expect(div.nodes.length, 2);
303 expect(div.lastChild.text, 'value:3');
304
305 m['bound'] = toObservable({'value': 4 });
306
307 }).then(endOfMicrotask).then((_) {
308 expect(div.nodes.length, 2);
309 expect(div.lastChild.text, 'value:3');
310
311 templateBind(template).model = null;
312
313 }).then(endOfMicrotask).then((_) {
314 expect(div.nodes.length, 1);
315 });
316 });
317
318 test('oneTime-Bind oneTime-If', () {
319 var div = createTestHtml(
320 '<template bind="[[ bound ]]" if="[[ predicate ]]">'
321 'value:{{ value }}'
322 '</template>');
323
324 var m = toObservable({'predicate': 1, 'bound': {'value': 2}});
325 var template = div.firstChild;
326 templateBind(template).model = m;
327
328 return new Future(() {
329 expect(div.nodes.length, 2);
330 expect(div.lastChild.text, 'value:2');
331
332 m['bound']['value'] = 3;
333
334 }).then(endOfMicrotask).then((_) {
335 expect(div.nodes.length, 2);
336 expect(div.lastChild.text, 'value:3');
337
338 m['bound'] = toObservable({'value': 4 });
339
340 }).then(endOfMicrotask).then((_) {
341 expect(div.nodes.length, 2);
342 expect(div.lastChild.text, 'value:3');
343
344 m['predicate'] = false;
345
346 }).then(endOfMicrotask).then((_) {
347 expect(div.nodes.length, 2);
348 expect(div.lastChild.text, 'value:3');
349
350 templateBind(template).model = null;
351
352 }).then(endOfMicrotask).then((_) {
353 expect(div.nodes.length, 1);
354 });
355 });
356
357 test('Bind If, 2', () {
358 var div = createTestHtml(
359 '<template bind="{{ foo }}" if="{{ bar }}">{{ bat }}</template>');
360 var template = div.firstChild;
361 var m = toObservable({ 'bar': null, 'foo': { 'bat': 'baz' } });
362 templateBind(template).model = m;
363 return new Future(() {
364 expect(div.nodes.length, 1);
365
366 m['bar'] = 1;
367 }).then(endOfMicrotask).then((_) {
368 expect(div.nodes.length, 2);
369 expect(div.lastChild.text, 'baz');
370 });
371 });
372
373 test('If', () {
374 var div = createTestHtml('<template if="{{ foo }}">{{ value }}</template>');
375 // Dart note: foo changed from 0->null because 0 isn't falsey in Dart.
376 // See https://code.google.com/p/dart/issues/detail?id=11956
377 var m = toObservable({ 'foo': null, 'value': 'foo' });
378 var template = div.firstChild;
379 templateBind(template).model = m;
380 return new Future(() {
381 expect(div.nodes.length, 1);
382
383 m['foo'] = 1;
384 }).then(endOfMicrotask).then((_) {
385 expect(div.nodes.length, 2);
386 expect(div.lastChild.text, 'foo');
387
388 templateBind(template).model = null;
389 }).then(endOfMicrotask).then((_) {
390 expect(div.nodes.length, 1);
391 });
392 });
393
394 test('Bind If minimal discardChanges', () {
395 var div = createTestHtml(
396 '<template bind="{{bound}}" if="{{predicate}}">value:{{ value }}'
397 '</template>');
398 // Dart Note: bound changed from null->{}.
399 var m = toObservable({ 'bound': {}, 'predicate': null });
400 var template = div.firstChild;
401
402 var discardChangesCalled = { 'bound': 0, 'predicate': 0 };
403 templateBind(template)
404 ..model = m
405 ..bindingDelegate =
406 new BindIfMinimalDiscardChanges(discardChangesCalled);
407
408 return new Future(() {
409 expect(discardChangesCalled['bound'], 0);
410 expect(discardChangesCalled['predicate'], 0);
411 expect(div.childNodes.length, 1);
412 m['predicate'] = 1;
413 }).then(endOfMicrotask).then((_) {
414 expect(discardChangesCalled['bound'], 1);
415 expect(discardChangesCalled['predicate'], 0);
416
417 expect(div.nodes.length, 2);
418 expect(div.lastChild.text, 'value:');
419
420 m['bound'] = toObservable({'value': 2});
421 }).then(endOfMicrotask).then((_) {
422 expect(discardChangesCalled['bound'], 1);
423 expect(discardChangesCalled['predicate'], 1);
424
425 expect(div.nodes.length, 2);
426 expect(div.lastChild.text, 'value:2');
427
428 m['bound']['value'] = 3;
429
430 }).then(endOfMicrotask).then((_) {
431 expect(discardChangesCalled['bound'], 1);
432 expect(discardChangesCalled['predicate'], 1);
433
434 expect(div.nodes.length, 2);
435 expect(div.lastChild.text, 'value:3');
436
437 templateBind(template).model = null;
438 }).then(endOfMicrotask).then((_) {
439 expect(discardChangesCalled['bound'], 1);
440 expect(discardChangesCalled['predicate'], 1);
441
442 expect(div.nodes.length, 1);
443 });
444 });
445
446
447 test('Empty-If', () {
448 var div = createTestHtml('<template if>{{ value }}</template>');
449 var template = div.firstChild;
450 var m = toObservable({ 'value': 'foo' });
451 templateBind(template).model = null;
452 return new Future(() {
453 expect(div.nodes.length, 1);
454
455 templateBind(template).model = m;
456 }).then(endOfMicrotask).then((_) {
457 expect(div.nodes.length, 2);
458 expect(div.lastChild.text, 'foo');
459 });
460 });
461
462 test('OneTime - simple text', () {
463 var div = createTestHtml('<template bind>[[ value ]]</template>');
464 var template = div.firstChild;
465 var m = toObservable({ 'value': 'foo' });
466 templateBind(template).model = m;
467 return new Future(() {
468 expect(div.nodes.length, 2);
469 expect(div.lastChild.text, 'foo');
470
471 m['value'] = 'bar';
472
473 }).then(endOfMicrotask).then((_) {
474 // unchanged.
475 expect(div.lastChild.text, 'foo');
476 });
477 });
478
479 test('OneTime - compound text', () {
480 var div = createTestHtml(
481 '<template bind>[[ foo ]] bar [[ baz ]]</template>');
482 var template = div.firstChild;
483 var m = toObservable({ 'foo': 'FOO', 'baz': 'BAZ' });
484 templateBind(template).model = m;
485 return new Future(() {
486 expect(div.nodes.length, 2);
487 expect(div.lastChild.text, 'FOO bar BAZ');
488
489 m['foo'] = 'FI';
490 m['baz'] = 'BA';
491
492 }).then(endOfMicrotask).then((_) {
493 // unchanged.
494 expect(div.nodes.length, 2);
495 expect(div.lastChild.text, 'FOO bar BAZ');
496 });
497 });
498
499 test('OneTime/Dynamic Mixed - compound text', () {
500 var div = createTestHtml(
501 '<template bind>[[ foo ]] bar {{ baz }}</template>');
502 var template = div.firstChild;
503 var m = toObservable({ 'foo': 'FOO', 'baz': 'BAZ' });
504 templateBind(template).model = m;
505 return new Future(() {
506 expect(div.nodes.length, 2);
507 expect(div.lastChild.text, 'FOO bar BAZ');
508
509 m['foo'] = 'FI';
510 m['baz'] = 'BA';
511
512 }).then(endOfMicrotask).then((_) {
513 // unchanged [[ foo ]].
514 expect(div.nodes.length, 2);
515 expect(div.lastChild.text, 'FOO bar BA');
516 });
517 });
518
519 test('OneTime - simple attribute', () {
520 var div = createTestHtml(
521 '<template bind><div foo="[[ value ]]"></div></template>');
522 var template = div.firstChild;
523 var m = toObservable({ 'value': 'foo' });
524 templateBind(template).model = m;
525 return new Future(() {
526 expect(div.nodes.length, 2);
527 expect(div.lastChild.attributes['foo'], 'foo');
528
529 m['value'] = 'bar';
530
531 }).then(endOfMicrotask).then((_) {
532 // unchanged.
533 expect(div.nodes.length, 2);
534 expect(div.lastChild.attributes['foo'], 'foo');
535 });
536 });
537
538 test('OneTime - compound attribute', () {
539 var div = createTestHtml(
540 '<template bind>'
541 '<div foo="[[ value ]]:[[ otherValue ]]"></div>'
542 '</template>');
543 var template = div.firstChild;
544 var m = toObservable({ 'value': 'foo', 'otherValue': 'bar' });
545 templateBind(template).model = m;
546 return new Future(() {
547 expect(div.nodes.length, 2);
548 expect(div.lastChild.attributes['foo'], 'foo:bar');
549
550 m['value'] = 'baz';
551 m['otherValue'] = 'bot';
552
553 }).then(endOfMicrotask).then((_) {
554 // unchanged.
555 expect(div.lastChild.attributes['foo'], 'foo:bar');
556 });
557 });
558
559 test('OneTime/Dynamic mixed - compound attribute', () {
560 var div = createTestHtml(
561 '<template bind>'
562 '<div foo="{{ value }}:[[ otherValue ]]"></div>'
563 '</template>');
564 var template = div.firstChild;
565 var m = toObservable({ 'value': 'foo', 'otherValue': 'bar' });
566 templateBind(template).model = m;
567 return new Future(() {
568 expect(div.nodes.length, 2);
569 expect(div.lastChild.attributes['foo'], 'foo:bar');
570
571 m['value'] = 'baz';
572 m['otherValue'] = 'bot';
573
574 }).then(endOfMicrotask).then((_) {
575 // unchanged [[ otherValue ]].
576 expect(div.lastChild.attributes['foo'], 'baz:bar');
577 });
578 });
579
580 test('Repeat If', () {
581 var div = createTestHtml(
582 '<template repeat="{{ items }}" if="{{ predicate }}">{{}}</template>');
583 // Dart note: predicate changed from 0->null because 0 isn't falsey in Dart.
584 // See https://code.google.com/p/dart/issues/detail?id=11956
585 var m = toObservable({ 'predicate': null, 'items': [1] });
586 var template = div.firstChild;
587 templateBind(template).model = m;
588 return new Future(() {
589 expect(div.nodes.length, 1);
590
591 m['predicate'] = 1;
592
593 }).then(endOfMicrotask).then((_) {
594 expect(div.nodes.length, 2);
595 expect(div.nodes[1].text, '1');
596
597 m['items']..add(2)..add(3);
598
599 }).then(endOfMicrotask).then((_) {
600 expect(div.nodes.length, 4);
601 expect(div.nodes[1].text, '1');
602 expect(div.nodes[2].text, '2');
603 expect(div.nodes[3].text, '3');
604
605 m['items'] = [4];
606
607 }).then(endOfMicrotask).then((_) {
608 expect(div.nodes.length, 2);
609 expect(div.nodes[1].text, '4');
610
611 templateBind(template).model = null;
612 }).then(endOfMicrotask).then((_) {
613 expect(div.nodes.length, 1);
614 });
615 });
616
617 test('Repeat oneTime-If (predicate false)', () {
618 var div = createTestHtml(
619 '<template repeat="{{ items }}" if="[[ predicate ]]">{{}}</template>');
620 // Dart note: predicate changed from 0->null because 0 isn't falsey in Dart.
621 // See https://code.google.com/p/dart/issues/detail?id=11956
622 var m = toObservable({ 'predicate': null, 'items': [1] });
623 var template = div.firstChild;
624 templateBind(template).model = m;
625 return new Future(() {
626 expect(div.nodes.length, 1);
627
628 m['predicate'] = 1;
629
630 }).then(endOfMicrotask).then((_) {
631 expect(div.nodes.length, 1, reason: 'unchanged');
632
633 m['items']..add(2)..add(3);
634
635 }).then(endOfMicrotask).then((_) {
636 expect(div.nodes.length, 1, reason: 'unchanged');
637
638 m['items'] = [4];
639
640 }).then(endOfMicrotask).then((_) {
641 expect(div.nodes.length, 1, reason: 'unchanged');
642
643 templateBind(template).model = null;
644 }).then(endOfMicrotask).then((_) {
645 expect(div.nodes.length, 1);
646 });
647 });
648
649 test('Repeat oneTime-If (predicate true)', () {
650 var div = createTestHtml(
651 '<template repeat="{{ items }}" if="[[ predicate ]]">{{}}</template>');
652
653 var m = toObservable({ 'predicate': true, 'items': [1] });
654 var template = div.firstChild;
655 templateBind(template).model = m;
656 return new Future(() {
657 expect(div.nodes.length, 2);
658 expect(div.nodes[1].text, '1');
659
660 m['items']..add(2)..add(3);
661
662 }).then(endOfMicrotask).then((_) {
663 expect(div.nodes.length, 4);
664 expect(div.nodes[1].text, '1');
665 expect(div.nodes[2].text, '2');
666 expect(div.nodes[3].text, '3');
667
668 m['items'] = [4];
669
670 }).then(endOfMicrotask).then((_) {
671 expect(div.nodes.length, 2);
672 expect(div.nodes[1].text, '4');
673
674 m['predicate'] = false;
675
676 }).then(endOfMicrotask).then((_) {
677 expect(div.nodes.length, 2, reason: 'unchanged');
678 expect(div.nodes[1].text, '4', reason: 'unchanged');
679
680 templateBind(template).model = null;
681 }).then(endOfMicrotask).then((_) {
682 expect(div.nodes.length, 1);
683 });
684 });
685
686 test('oneTime-Repeat If', () {
687 var div = createTestHtml(
688 '<template repeat="[[ items ]]" if="{{ predicate }}">{{}}</template>');
689
690 var m = toObservable({ 'predicate': false, 'items': [1] });
691 var template = div.firstChild;
692 templateBind(template).model = m;
693 return new Future(() {
694 expect(div.nodes.length, 1);
695
696 m['predicate'] = true;
697
698 }).then(endOfMicrotask).then((_) {
699 expect(div.nodes.length, 2);
700 expect(div.nodes[1].text, '1');
701
702 m['items']..add(2)..add(3);
703
704 }).then(endOfMicrotask).then((_) {
705 expect(div.nodes.length, 2);
706 expect(div.nodes[1].text, '1');
707
708 m['items'] = [4];
709
710 }).then(endOfMicrotask).then((_) {
711 expect(div.nodes.length, 2);
712 expect(div.nodes[1].text, '1');
713
714 templateBind(template).model = null;
715 }).then(endOfMicrotask).then((_) {
716 expect(div.nodes.length, 1);
717 });
718 });
719
720 test('oneTime-Repeat oneTime-If', () {
721 var div = createTestHtml(
722 '<template repeat="[[ items ]]" if="[[ predicate ]]">{{}}</template>');
723
724 var m = toObservable({ 'predicate': true, 'items': [1] });
725 var template = div.firstChild;
726 templateBind(template).model = m;
727 return new Future(() {
728 expect(div.nodes.length, 2);
729 expect(div.nodes[1].text, '1');
730
731 m['items']..add(2)..add(3);
732
733 }).then(endOfMicrotask).then((_) {
734 expect(div.nodes.length, 2);
735 expect(div.nodes[1].text, '1');
736
737 m['items'] = [4];
738
739 }).then(endOfMicrotask).then((_) {
740 expect(div.nodes.length, 2);
741 expect(div.nodes[1].text, '1');
742
743 m['predicate'] = false;
744
745 }).then(endOfMicrotask).then((_) {
746 expect(div.nodes.length, 2);
747 expect(div.nodes[1].text, '1');
748
749 templateBind(template).model = null;
750 }).then(endOfMicrotask).then((_) {
751 expect(div.nodes.length, 1);
752 });
753 });
754
755 test('TextTemplateWithNullStringBinding', () {
756 var div = createTestHtml('<template bind={{}}>a{{b}}c</template>');
757 var template = div.firstChild;
758 var model = toObservable({'b': 'B'});
759 templateBind(template).model = model;
760
761 return new Future(() {
762 expect(div.nodes.length, 2);
763 expect(div.nodes.last.text, 'aBc');
764
765 model['b'] = 'b';
766 }).then(endOfMicrotask).then((_) {
767 expect(div.nodes.last.text, 'abc');
768
769 model['b'] = null;
770 }).then(endOfMicrotask).then((_) {
771 expect(div.nodes.last.text, 'ac');
772
773 model = null;
774 }).then(endOfMicrotask).then((_) {
775 // setting model isn't bindable.
776 expect(div.nodes.last.text, 'ac');
777 });
778 });
779
780 test('TextTemplateWithBindingPath', () {
781 var div = createTestHtml(
782 '<template bind="{{ data }}">a{{b}}c</template>');
783 var model = toObservable({ 'data': {'b': 'B'} });
784 var template = div.firstChild;
785 templateBind(template).model = model;
786
787 return new Future(() {
788 expect(div.nodes.length, 2);
789 expect(div.nodes.last.text, 'aBc');
790
791 model['data']['b'] = 'b';
792 }).then(endOfMicrotask).then((_) {
793 expect(div.nodes.last.text, 'abc');
794
795 model['data'] = toObservable({'b': 'X'});
796 }).then(endOfMicrotask).then((_) {
797 expect(div.nodes.last.text, 'aXc');
798
799 // Dart note: changed from `null` since our null means don't render a mode l.
800 model['data'] = toObservable({});
801 }).then(endOfMicrotask).then((_) {
802 expect(div.nodes.last.text, 'ac');
803
804 model['data'] = null;
805 }).then(endOfMicrotask).then((_) {
806 expect(div.nodes.length, 1);
807 });
808 });
809
810 test('TextTemplateWithBindingAndConditional', () {
811 var div = createTestHtml(
812 '<template bind="{{}}" if="{{ d }}">a{{b}}c</template>');
813 var template = div.firstChild;
814 var model = toObservable({'b': 'B', 'd': 1});
815 templateBind(template).model = model;
816
817 return new Future(() {
818 expect(div.nodes.length, 2);
819 expect(div.nodes.last.text, 'aBc');
820
821 model['b'] = 'b';
822 }).then(endOfMicrotask).then((_) {
823 expect(div.nodes.last.text, 'abc');
824
825 // TODO(jmesserly): MDV set this to empty string and relies on JS conversi on
826 // rules. Is that intended?
827 // See https://github.com/Polymer/TemplateBinding/issues/59
828 model['d'] = null;
829 }).then(endOfMicrotask).then((_) {
830 expect(div.nodes.length, 1);
831
832 model['d'] = 'here';
833 model['b'] = 'd';
834
835 }).then(endOfMicrotask).then((_) {
836 expect(div.nodes.length, 2);
837 expect(div.nodes.last.text, 'adc');
838 });
839 });
840
841 test('TemplateWithTextBinding2', () {
842 var div = createTestHtml(
843 '<template bind="{{ b }}">a{{value}}c</template>');
844 expect(div.nodes.length, 1);
845 var template = div.firstChild;
846 var model = toObservable({'b': {'value': 'B'}});
847 templateBind(template).model = model;
848
849 return new Future(() {
850 expect(div.nodes.length, 2);
851 expect(div.nodes.last.text, 'aBc');
852
853 model['b'] = toObservable({'value': 'b'});
854 }).then(endOfMicrotask).then((_) {
855 expect(div.nodes.last.text, 'abc');
856 });
857 });
858
859 test('TemplateWithAttributeBinding', () {
860 var div = createTestHtml(
861 '<template bind="{{}}">'
862 '<div foo="a{{b}}c"></div>'
863 '</template>');
864 var template = div.firstChild;
865 var model = toObservable({'b': 'B'});
866 templateBind(template).model = model;
867
868 return new Future(() {
869 expect(div.nodes.length, 2);
870 expect(div.nodes.last.attributes['foo'], 'aBc');
871
872 model['b'] = 'b';
873 }).then(endOfMicrotask).then((_) {
874 expect(div.nodes.last.attributes['foo'], 'abc');
875
876 model['b'] = 'X';
877 }).then(endOfMicrotask).then((_) {
878 expect(div.nodes.last.attributes['foo'], 'aXc');
879 });
880 });
881
882 test('TemplateWithConditionalBinding', () {
883 var div = createTestHtml(
884 '<template bind="{{}}">'
885 '<div foo?="{{b}}"></div>'
886 '</template>');
887 var template = div.firstChild;
888 var model = toObservable({'b': 'b'});
889 templateBind(template).model = model;
890
891 return new Future(() {
892 expect(div.nodes.length, 2);
893 expect(div.nodes.last.attributes['foo'], '');
894 expect(div.nodes.last.attributes, isNot(contains('foo?')));
895
896 model['b'] = null;
897 }).then(endOfMicrotask).then((_) {
898 expect(div.nodes.last.attributes, isNot(contains('foo')));
899 });
900 });
901
902 test('Repeat', () {
903 var div = createTestHtml(
904 '<template repeat="{{ array }}">{{}},</template>');
905
906 var model = toObservable({'array': [0, 1, 2]});
907 var template = templateBind(div.firstChild);
908 template.model = model;
909
910 return new Future(() {
911 expect(div.nodes.length, 4);
912 expect(div.text, '0,1,2,');
913
914 model['array'].length = 1;
915
916 }).then(endOfMicrotask).then((_) {
917 expect(div.nodes.length, 2);
918 expect(div.text, '0,');
919
920 model['array'].addAll([3, 4]);
921
922 }).then(endOfMicrotask).then((_) {
923 expect(div.nodes.length, 4);
924 expect(div.text, '0,3,4,');
925
926 model['array'].removeRange(1, 2);
927
928 }).then(endOfMicrotask).then((_) {
929 expect(div.nodes.length, 3);
930 expect(div.text, '0,4,');
931
932 model['array'].addAll([5, 6]);
933 model['array'] = toObservable(['x', 'y']);
934
935 }).then(endOfMicrotask).then((_) {
936 expect(div.nodes.length, 3);
937 expect(div.text, 'x,y,');
938
939 template.model = null;
940
941 }).then(endOfMicrotask).then((_) {
942 expect(div.nodes.length, 1);
943 expect(div.text, '');
944 });
945 });
946
947 test('Repeat - oneTime', () {
948 var div = createTestHtml('<template repeat="[[]]">text</template>');
949
950 var model = toObservable([0, 1, 2]);
951 var template = templateBind(div.firstChild);
952 template.model = model;
953
954 return new Future(() {
955 expect(div.nodes.length, 4);
956
957 model.length = 1;
958 }).then(endOfMicrotask).then((_) {
959 expect(div.nodes.length, 4);
960
961 model.addAll([3, 4]);
962 }).then(endOfMicrotask).then((_) {
963 expect(div.nodes.length, 4);
964
965 model.removeRange(1, 2);
966 }).then(endOfMicrotask).then((_) {
967 expect(div.nodes.length, 4);
968
969 template.model = null;
970 }).then(endOfMicrotask).then((_) {
971 expect(div.nodes.length, 1);
972 });
973 });
974
975 test('Repeat - Reuse Instances', () {
976 var div = createTestHtml('<template repeat>{{ val }}</template>');
977
978 var model = toObservable([
979 {'val': 10},
980 {'val': 5},
981 {'val': 2},
982 {'val': 8},
983 {'val': 1}
984 ]);
985 var template = div.firstChild;
986 templateBind(template).model = model;
987
988 return new Future(() {
989 expect(div.nodes.length, 6);
990
991 addExpandos(template.nextNode);
992 checkExpandos(template.nextNode);
993
994 model.sort((a, b) => a['val'] - b['val']);
995 }).then(endOfMicrotask).then((_) {
996 checkExpandos(template.nextNode);
997
998 model = toObservable(model.reversed);
999 templateBind(template).model = model;
1000 }).then(endOfMicrotask).then((_) {
1001 checkExpandos(template.nextNode);
1002
1003 for (var item in model) {
1004 item['val'] += 1;
1005 }
1006
1007 }).then(endOfMicrotask).then((_) {
1008 expect(div.nodes[1].text, "11");
1009 expect(div.nodes[2].text, "9");
1010 expect(div.nodes[3].text, "6");
1011 expect(div.nodes[4].text, "3");
1012 expect(div.nodes[5].text, "2");
1013 });
1014 });
1015
1016 test('Bind - Reuse Instance', () {
1017 var div = createTestHtml(
1018 '<template bind="{{ foo }}">{{ bar }}</template>');
1019
1020 var template = div.firstChild;
1021 var model = toObservable({ 'foo': { 'bar': 5 }});
1022 templateBind(template).model = model;
1023
1024 return new Future(() {
1025 expect(div.nodes.length, 2);
1026
1027 addExpandos(template.nextNode);
1028 checkExpandos(template.nextNode);
1029
1030 model = toObservable({'foo': model['foo']});
1031 templateBind(template).model = model;
1032 }).then(endOfMicrotask).then((_) {
1033 checkExpandos(template.nextNode);
1034 });
1035 });
1036
1037 test('Repeat-Empty', () {
1038 var div = createTestHtml(
1039 '<template repeat>text</template>');
1040
1041 var template = div.firstChild;
1042 var model = toObservable([0, 1, 2]);
1043 templateBind(template).model = model;
1044
1045 return new Future(() {
1046 expect(div.nodes.length, 4);
1047
1048 model.length = 1;
1049 }).then(endOfMicrotask).then((_) {
1050 expect(div.nodes.length, 2);
1051
1052 model.addAll(toObservable([3, 4]));
1053 }).then(endOfMicrotask).then((_) {
1054 expect(div.nodes.length, 4);
1055
1056 model.removeRange(1, 2);
1057 }).then(endOfMicrotask).then((_) {
1058 expect(div.nodes.length, 3);
1059 });
1060 });
1061
1062 test('Removal from iteration needs to unbind', () {
1063 var div = createTestHtml(
1064 '<template repeat="{{}}"><a>{{v}}</a></template>');
1065 var template = div.firstChild;
1066 var model = toObservable([{'v': 0}, {'v': 1}, {'v': 2}, {'v': 3},
1067 {'v': 4}]);
1068 templateBind(template).model = model;
1069
1070 var nodes, vs;
1071 return new Future(() {
1072
1073 nodes = div.nodes.skip(1).toList();
1074 vs = model.toList();
1075
1076 for (var i = 0; i < 5; i++) {
1077 expect(nodes[i].text, '$i');
1078 }
1079
1080 model.length = 3;
1081 }).then(endOfMicrotask).then((_) {
1082 for (var i = 0; i < 5; i++) {
1083 expect(nodes[i].text, '$i');
1084 }
1085
1086 vs[3]['v'] = 33;
1087 vs[4]['v'] = 44;
1088 }).then(endOfMicrotask).then((_) {
1089 for (var i = 0; i < 5; i++) {
1090 expect(nodes[i].text, '$i');
1091 }
1092 });
1093 });
1094
1095 test('Template.clear', () {
1096 var div = createTestHtml(
1097 '<template repeat>{{}}</template>');
1098 var template = div.firstChild;
1099 templateBind(template).model = [0, 1, 2];
1100
1101 return new Future(() {
1102 expect(div.nodes.length, 4);
1103 expect(div.nodes[1].text, '0');
1104 expect(div.nodes[2].text, '1');
1105 expect(div.nodes[3].text, '2');
1106
1107 // clear() synchronously removes instances and clears the model.
1108 templateBind(div.firstChild).clear();
1109 expect(div.nodes.length, 1);
1110 expect(templateBind(template).model, null);
1111
1112 // test that template still works if new model assigned
1113 templateBind(template).model = [3, 4];
1114
1115 }).then(endOfMicrotask).then((_) {
1116 expect(div.nodes.length, 3);
1117 expect(div.nodes[1].text, '3');
1118 expect(div.nodes[2].text, '4');
1119 });
1120 });
1121
1122 test('DOM Stability on Iteration', () {
1123 var div = createTestHtml(
1124 '<template repeat="{{}}">{{}}</template>');
1125 var template = div.firstChild;
1126 var model = toObservable([1, 2, 3, 4, 5]);
1127 templateBind(template).model = model;
1128
1129 var nodes;
1130 return new Future(() {
1131 // Note: the node at index 0 is the <template>.
1132 nodes = div.nodes.toList();
1133 expect(nodes.length, 6, reason: 'list has 5 items');
1134
1135 model.removeAt(0);
1136 model.removeLast();
1137
1138 }).then(endOfMicrotask).then((_) {
1139 expect(div.nodes.length, 4, reason: 'list has 3 items');
1140 expect(identical(div.nodes[1], nodes[2]), true, reason: '2 not removed');
1141 expect(identical(div.nodes[2], nodes[3]), true, reason: '3 not removed');
1142 expect(identical(div.nodes[3], nodes[4]), true, reason: '4 not removed');
1143
1144 model.insert(0, 5);
1145 model[2] = 6;
1146 model.add(7);
1147
1148 }).then(endOfMicrotask).then((_) {
1149
1150 expect(div.nodes.length, 6, reason: 'list has 5 items');
1151 expect(nodes.contains(div.nodes[1]), false, reason: '5 is a new node');
1152 expect(identical(div.nodes[2], nodes[2]), true);
1153 expect(nodes.contains(div.nodes[3]), false, reason: '6 is a new node');
1154 expect(identical(div.nodes[4], nodes[4]), true);
1155 expect(nodes.contains(div.nodes[5]), false, reason: '7 is a new node');
1156
1157 nodes = div.nodes.toList();
1158
1159 model.insert(2, 8);
1160
1161 }).then(endOfMicrotask).then((_) {
1162
1163 expect(div.nodes.length, 7, reason: 'list has 6 items');
1164 expect(identical(div.nodes[1], nodes[1]), true);
1165 expect(identical(div.nodes[2], nodes[2]), true);
1166 expect(nodes.contains(div.nodes[3]), false, reason: '8 is a new node');
1167 expect(identical(div.nodes[4], nodes[3]), true);
1168 expect(identical(div.nodes[5], nodes[4]), true);
1169 expect(identical(div.nodes[6], nodes[5]), true);
1170 });
1171 });
1172
1173 test('Repeat2', () {
1174 var div = createTestHtml(
1175 '<template repeat="{{}}">{{value}}</template>');
1176 expect(div.nodes.length, 1);
1177
1178 var template = div.firstChild;
1179 var model = toObservable([
1180 {'value': 0},
1181 {'value': 1},
1182 {'value': 2}
1183 ]);
1184 templateBind(template).model = model;
1185
1186 return new Future(() {
1187 expect(div.nodes.length, 4);
1188 expect(div.nodes[1].text, '0');
1189 expect(div.nodes[2].text, '1');
1190 expect(div.nodes[3].text, '2');
1191
1192 model[1]['value'] = 'One';
1193 }).then(endOfMicrotask).then((_) {
1194 expect(div.nodes.length, 4);
1195 expect(div.nodes[1].text, '0');
1196 expect(div.nodes[2].text, 'One');
1197 expect(div.nodes[3].text, '2');
1198
1199 model.replaceRange(0, 1, toObservable([{'value': 'Zero'}]));
1200 }).then(endOfMicrotask).then((_) {
1201 expect(div.nodes.length, 4);
1202 expect(div.nodes[1].text, 'Zero');
1203 expect(div.nodes[2].text, 'One');
1204 expect(div.nodes[3].text, '2');
1205 });
1206 });
1207
1208 test('TemplateWithInputValue', () {
1209 var div = createTestHtml(
1210 '<template bind="{{}}">'
1211 '<input value="{{x}}">'
1212 '</template>');
1213 var template = div.firstChild;
1214 var model = toObservable({'x': 'hi'});
1215 templateBind(template).model = model;
1216
1217 return new Future(() {
1218 expect(div.nodes.length, 2);
1219 expect(div.nodes.last.value, 'hi');
1220
1221 model['x'] = 'bye';
1222 expect(div.nodes.last.value, 'hi');
1223 }).then(endOfMicrotask).then((_) {
1224 expect(div.nodes.last.value, 'bye');
1225
1226 div.nodes.last.value = 'hello';
1227 dispatchEvent('input', div.nodes.last);
1228 expect(model['x'], 'hello');
1229 }).then(endOfMicrotask).then((_) {
1230 expect(div.nodes.last.value, 'hello');
1231 });
1232 });
1233
1234 //////////////////////////////////////////////////////////////////////////////
1235
1236 test('Decorated', () {
1237 var div = createTestHtml(
1238 '<template bind="{{ XX }}" id="t1">'
1239 '<p>Crew member: {{name}}, Job title: {{title}}</p>'
1240 '</template>'
1241 '<template bind="{{ XY }}" id="t2" ref="t1"></template>');
1242
1243 var t1 = document.getElementById('t1');
1244 var t2 = document.getElementById('t2');
1245 var model = toObservable({
1246 'XX': {'name': 'Leela', 'title': 'Captain'},
1247 'XY': {'name': 'Fry', 'title': 'Delivery boy'},
1248 'XZ': {'name': 'Zoidberg', 'title': 'Doctor'}
1249 });
1250 templateBind(t1).model = model;
1251 templateBind(t2).model = model;
1252
1253 return new Future(() {
1254 var instance = t1.nextElementSibling;
1255 expect(instance.text, 'Crew member: Leela, Job title: Captain');
1256
1257 instance = t2.nextElementSibling;
1258 expect(instance.text, 'Crew member: Fry, Job title: Delivery boy');
1259
1260 expect(div.children.length, 4);
1261 expect(div.nodes.length, 4);
1262
1263 expect(div.nodes[1].tagName, 'P');
1264 expect(div.nodes[3].tagName, 'P');
1265 });
1266 });
1267
1268 test('DefaultStyles', () {
1269 var t = new Element.tag('template');
1270 TemplateBindExtension.decorate(t);
1271
1272 document.body.append(t);
1273 expect(t.getComputedStyle().display, 'none');
1274
1275 t.remove();
1276 });
1277
1278
1279 test('Bind', () {
1280 var div = createTestHtml('<template bind="{{}}">Hi {{ name }}</template>');
1281 var template = div.firstChild;
1282 var model = toObservable({'name': 'Leela'});
1283 templateBind(template).model = model;
1284
1285 return new Future(() => expect(div.nodes[1].text, 'Hi Leela'));
1286 });
1287
1288 test('BindPlaceHolderHasNewLine', () {
1289 var div = createTestHtml(
1290 '<template bind="{{}}">Hi {{\nname\n}}</template>');
1291 var template = div.firstChild;
1292 var model = toObservable({'name': 'Leela'});
1293 templateBind(template).model = model;
1294
1295 return new Future(() => expect(div.nodes[1].text, 'Hi Leela'));
1296 });
1297
1298 test('BindWithRef', () {
1299 var id = 't${new math.Random().nextInt(100)}';
1300 var div = createTestHtml(
1301 '<template id="$id">'
1302 'Hi {{ name }}'
1303 '</template>'
1304 '<template ref="$id" bind="{{}}"></template>');
1305
1306 var t1 = div.nodes.first;
1307 var t2 = div.nodes[1];
1308
1309 var model = toObservable({'name': 'Fry'});
1310 templateBind(t1).model = model;
1311 templateBind(t2).model = model;
1312
1313 return new Future(() => expect(t2.nextNode.text, 'Hi Fry'));
1314 });
1315
1316 test('Ref at multiple', () {
1317 // Note: this test is asserting that template "ref"erences can be located
1318 // at various points. In particular:
1319 // -in the document (at large) (e.g. ref=doc)
1320 // -within template content referenced from sub-content
1321 // -both before and after the reference
1322 // The following asserts ensure that all referenced templates content is
1323 // found.
1324 var div = createTestHtml(
1325 '<template bind>'
1326 '<template bind ref=doc></template>'
1327 '<template id=elRoot>EL_ROOT</template>'
1328 '<template bind>'
1329 '<template bind ref=elRoot></template>'
1330 '<template bind>'
1331 '<template bind ref=subA></template>'
1332 '<template id=subB>SUB_B</template>'
1333 '<template bind>'
1334 '<template bind ref=subB></template>'
1335 '</template>'
1336 '</template>'
1337 '<template id=subA>SUB_A</template>'
1338 '</template>'
1339 '</template>'
1340 '<template id=doc>DOC</template>');
1341 var t = div.firstChild;
1342 var fragment = templateBind(t).createInstance({});
1343 expect(fragment.nodes.length, 14);
1344 expect(fragment.nodes[1].text, 'DOC');
1345 expect(fragment.nodes[5].text, 'EL_ROOT');
1346 expect(fragment.nodes[8].text, 'SUB_A');
1347 expect(fragment.nodes[12].text, 'SUB_B');
1348 div.append(fragment);
1349 });
1350
1351 test('Update Ref', () {
1352 // Updating ref by observing the attribute is dependent on MutationObserver
1353 var div = createTestHtml(
1354 '<template id=A>Hi, {{}}</template>'
1355 '<template id=B>Hola, {{}}</template>'
1356 '<template ref=A repeat></template>');
1357
1358 var template = div.nodes[2];
1359 var model = new ObservableList.from(['Fry']);
1360 templateBind(template).model = model;
1361
1362 return new Future(() {
1363 expect(div.nodes.length, 4);
1364 expect('Hi, Fry', div.nodes[3].text);
1365
1366 // In IE 11, MutationObservers do not fire before setTimeout.
1367 // So rather than using "then" to queue up the next test, we use a
1368 // MutationObserver here to detect the change to "ref".
1369 var done = new Completer();
1370 new MutationObserver((mutations, observer) {
1371 expect(div.nodes.length, 5);
1372
1373 expect('Hola, Fry', div.nodes[3].text);
1374 expect('Hola, Leela', div.nodes[4].text);
1375 done.complete();
1376 }).observe(template, attributes: true, attributeFilter: ['ref']);
1377
1378 template.setAttribute('ref', 'B');
1379 model.add('Leela');
1380
1381 return done.future;
1382 });
1383 });
1384
1385 test('Bound Ref', () {
1386 var div = createTestHtml(
1387 '<template id=A>Hi, {{}}</template>'
1388 '<template id=B>Hola, {{}}</template>'
1389 '<template ref="{{ ref }}" repeat="{{ people }}"></template>');
1390
1391 var template = div.nodes[2];
1392 var model = toObservable({'ref': 'A', 'people': ['Fry']});
1393 templateBind(template).model = model;
1394
1395 return new Future(() {
1396 expect(div.nodes.length, 4);
1397 expect('Hi, Fry', div.nodes[3].text);
1398
1399 model['ref'] = 'B';
1400 model['people'].add('Leela');
1401
1402 }).then(endOfMicrotask).then((x) {
1403 expect(div.nodes.length, 5);
1404
1405 expect('Hola, Fry', div.nodes[3].text);
1406 expect('Hola, Leela', div.nodes[4].text);
1407 });
1408 });
1409
1410 test('BindWithDynamicRef', () {
1411 var id = 't${new math.Random().nextInt(100)}';
1412 var div = createTestHtml(
1413 '<template id="$id">'
1414 'Hi {{ name }}'
1415 '</template>'
1416 '<template ref="{{ id }}" bind="{{}}"></template>');
1417
1418 var t1 = div.firstChild;
1419 var t2 = div.nodes[1];
1420 var model = toObservable({'name': 'Fry', 'id': id });
1421 templateBind(t1).model = model;
1422 templateBind(t2).model = model;
1423
1424 return new Future(() => expect(t2.nextNode.text, 'Hi Fry'));
1425 });
1426
1427 assertNodesAre(div, [arguments]) {
1428 var expectedLength = arguments.length;
1429 expect(div.nodes.length, expectedLength + 1);
1430
1431 for (var i = 0; i < arguments.length; i++) {
1432 var targetNode = div.nodes[i + 1];
1433 expect(targetNode.text, arguments[i]);
1434 }
1435 }
1436
1437 test('Repeat3', () {
1438 var div = createTestHtml(
1439 '<template repeat="{{ contacts }}">Hi {{ name }}</template>');
1440 var t = div.nodes.first;
1441
1442 var m = toObservable({
1443 'contacts': [
1444 {'name': 'Raf'},
1445 {'name': 'Arv'},
1446 {'name': 'Neal'}
1447 ]
1448 });
1449
1450 templateBind(t).model = m;
1451 return new Future(() {
1452
1453 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
1454
1455 m['contacts'].add(toObservable({'name': 'Alex'}));
1456 }).then(endOfMicrotask).then((_) {
1457 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal', 'Hi Alex']);
1458
1459 m['contacts'].replaceRange(0, 2,
1460 toObservable([{'name': 'Rafael'}, {'name': 'Erik'}]));
1461 }).then(endOfMicrotask).then((_) {
1462 assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Neal', 'Hi Alex']);
1463
1464 m['contacts'].removeRange(1, 3);
1465 }).then(endOfMicrotask).then((_) {
1466 assertNodesAre(div, ['Hi Rafael', 'Hi Alex']);
1467
1468 m['contacts'].insertAll(1,
1469 toObservable([{'name': 'Erik'}, {'name': 'Dimitri'}]));
1470 }).then(endOfMicrotask).then((_) {
1471 assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Dimitri', 'Hi Alex']);
1472
1473 m['contacts'].replaceRange(0, 1,
1474 toObservable([{'name': 'Tab'}, {'name': 'Neal'}]));
1475 }).then(endOfMicrotask).then((_) {
1476 assertNodesAre(div, ['Hi Tab', 'Hi Neal', 'Hi Erik', 'Hi Dimitri',
1477 'Hi Alex']);
1478
1479 m['contacts'] = toObservable([{'name': 'Alex'}]);
1480 }).then(endOfMicrotask).then((_) {
1481 assertNodesAre(div, ['Hi Alex']);
1482
1483 m['contacts'].length = 0;
1484 }).then(endOfMicrotask).then((_) {
1485 assertNodesAre(div, []);
1486 });
1487 });
1488
1489 test('RepeatModelSet', () {
1490 var div = createTestHtml(
1491 '<template repeat="{{ contacts }}">'
1492 'Hi {{ name }}'
1493 '</template>');
1494 var template = div.firstChild;
1495 var m = toObservable({
1496 'contacts': [
1497 {'name': 'Raf'},
1498 {'name': 'Arv'},
1499 {'name': 'Neal'}
1500 ]
1501 });
1502 templateBind(template).model = m;
1503 return new Future(() {
1504 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
1505 });
1506 });
1507
1508 test('RepeatEmptyPath', () {
1509 var div = createTestHtml(
1510 '<template repeat="{{}}">Hi {{ name }}</template>');
1511 var t = div.nodes.first;
1512
1513 var m = toObservable([
1514 {'name': 'Raf'},
1515 {'name': 'Arv'},
1516 {'name': 'Neal'}
1517 ]);
1518 templateBind(t).model = m;
1519 return new Future(() {
1520
1521 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
1522
1523 m.add(toObservable({'name': 'Alex'}));
1524 }).then(endOfMicrotask).then((_) {
1525 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal', 'Hi Alex']);
1526
1527 m.replaceRange(0, 2, toObservable([{'name': 'Rafael'}, {'name': 'Erik'}])) ;
1528 }).then(endOfMicrotask).then((_) {
1529 assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Neal', 'Hi Alex']);
1530
1531 m.removeRange(1, 3);
1532 }).then(endOfMicrotask).then((_) {
1533 assertNodesAre(div, ['Hi Rafael', 'Hi Alex']);
1534
1535 m.insertAll(1, toObservable([{'name': 'Erik'}, {'name': 'Dimitri'}]));
1536 }).then(endOfMicrotask).then((_) {
1537 assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Dimitri', 'Hi Alex']);
1538
1539 m.replaceRange(0, 1, toObservable([{'name': 'Tab'}, {'name': 'Neal'}]));
1540 }).then(endOfMicrotask).then((_) {
1541 assertNodesAre(div, ['Hi Tab', 'Hi Neal', 'Hi Erik', 'Hi Dimitri',
1542 'Hi Alex']);
1543
1544 m.length = 0;
1545 m.add(toObservable({'name': 'Alex'}));
1546 }).then(endOfMicrotask).then((_) {
1547 assertNodesAre(div, ['Hi Alex']);
1548 });
1549 });
1550
1551 test('RepeatNullModel', () {
1552 var div = createTestHtml(
1553 '<template repeat="{{}}">Hi {{ name }}</template>');
1554 var t = div.nodes.first;
1555
1556 var m = null;
1557 templateBind(t).model = m;
1558
1559 expect(div.nodes.length, 1);
1560
1561 t.attributes['iterate'] = '';
1562 m = toObservable({});
1563 templateBind(t).model = m;
1564 return new Future(() => expect(div.nodes.length, 1));
1565 });
1566
1567 test('RepeatReuse', () {
1568 var div = createTestHtml(
1569 '<template repeat="{{}}">Hi {{ name }}</template>');
1570 var t = div.nodes.first;
1571
1572 var m = toObservable([
1573 {'name': 'Raf'},
1574 {'name': 'Arv'},
1575 {'name': 'Neal'}
1576 ]);
1577 templateBind(t).model = m;
1578
1579 var node1, node2, node3;
1580 return new Future(() {
1581 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
1582 node1 = div.nodes[1];
1583 node2 = div.nodes[2];
1584 node3 = div.nodes[3];
1585
1586 m.replaceRange(1, 2, toObservable([{'name': 'Erik'}]));
1587 }).then(endOfMicrotask).then((_) {
1588 assertNodesAre(div, ['Hi Raf', 'Hi Erik', 'Hi Neal']);
1589 expect(div.nodes[1], node1,
1590 reason: 'model[0] did not change so the node should not have changed') ;
1591 expect(div.nodes[2], isNot(equals(node2)),
1592 reason: 'Should not reuse when replacing');
1593 expect(div.nodes[3], node3,
1594 reason: 'model[2] did not change so the node should not have changed') ;
1595
1596 node2 = div.nodes[2];
1597 m.insert(0, toObservable({'name': 'Alex'}));
1598 }).then(endOfMicrotask).then((_) {
1599 assertNodesAre(div, ['Hi Alex', 'Hi Raf', 'Hi Erik', 'Hi Neal']);
1600 });
1601 });
1602
1603 test('TwoLevelsDeepBug', () {
1604 var div = createTestHtml(
1605 '<template bind="{{}}"><span><span>{{ foo }}</span></span></template>');
1606 var template = div.firstChild;
1607 var model = toObservable({'foo': 'bar'});
1608 templateBind(template).model = model;
1609 return new Future(() {
1610 expect(div.nodes[1].nodes[0].nodes[0].text, 'bar');
1611 });
1612 });
1613
1614 test('Checked', () {
1615 var div = createTestHtml(
1616 '<template bind>'
1617 '<input type="checkbox" checked="{{a}}">'
1618 '</template>');
1619 var t = div.nodes.first;
1620 templateBind(t).model = toObservable({'a': true });
1621
1622 return new Future(() {
1623
1624 var instanceInput = t.nextNode;
1625 expect(instanceInput.checked, true);
1626
1627 instanceInput.click();
1628 expect(instanceInput.checked, false);
1629
1630 instanceInput.click();
1631 expect(instanceInput.checked, true);
1632 });
1633 });
1634
1635 nestedHelper(s, start) {
1636 var div = createTestHtml(s);
1637
1638 var m = toObservable({
1639 'a': {
1640 'b': 1,
1641 'c': {'d': 2}
1642 },
1643 });
1644
1645 recursivelySetTemplateModel(div, m);
1646 return new Future(() {
1647
1648 var i = start;
1649 expect(div.nodes[i++].text, '1');
1650 expect(div.nodes[i++].tagName, 'TEMPLATE');
1651 expect(div.nodes[i++].text, '2');
1652
1653 m['a']['b'] = 11;
1654 }).then(endOfMicrotask).then((_) {
1655 expect(div.nodes[start].text, '11');
1656
1657 m['a']['c'] = toObservable({'d': 22});
1658 }).then(endOfMicrotask).then((_) {
1659 expect(div.nodes[start + 2].text, '22');
1660
1661 //clearAllTemplates(div);
1662 });
1663 }
1664
1665 test('Nested', () => nestedHelper(
1666 '<template bind="{{a}}">'
1667 '{{b}}'
1668 '<template bind="{{c}}">'
1669 '{{d}}'
1670 '</template>'
1671 '</template>', 1));
1672
1673 test('NestedWithRef', () => nestedHelper(
1674 '<template id="inner">{{d}}</template>'
1675 '<template id="outer" bind="{{a}}">'
1676 '{{b}}'
1677 '<template ref="inner" bind="{{c}}"></template>'
1678 '</template>', 2));
1679
1680 nestedIterateInstantiateHelper(s, start) {
1681 var div = createTestHtml(s);
1682
1683 var m = toObservable({
1684 'a': [
1685 {
1686 'b': 1,
1687 'c': {'d': 11}
1688 },
1689 {
1690 'b': 2,
1691 'c': {'d': 22}
1692 }
1693 ]
1694 });
1695
1696 recursivelySetTemplateModel(div, m);
1697 return new Future(() {
1698
1699 var i = start;
1700 expect(div.nodes[i++].text, '1');
1701 expect(div.nodes[i++].tagName, 'TEMPLATE');
1702 expect(div.nodes[i++].text, '11');
1703 expect(div.nodes[i++].text, '2');
1704 expect(div.nodes[i++].tagName, 'TEMPLATE');
1705 expect(div.nodes[i++].text, '22');
1706
1707 m['a'][1] = toObservable({
1708 'b': 3,
1709 'c': {'d': 33}
1710 });
1711
1712 }).then(endOfMicrotask).then((_) {
1713 expect(div.nodes[start + 3].text, '3');
1714 expect(div.nodes[start + 5].text, '33');
1715 });
1716 }
1717
1718 test('NestedRepeatBind', () => nestedIterateInstantiateHelper(
1719 '<template repeat="{{a}}">'
1720 '{{b}}'
1721 '<template bind="{{c}}">'
1722 '{{d}}'
1723 '</template>'
1724 '</template>', 1));
1725
1726 test('NestedRepeatBindWithRef', () => nestedIterateInstantiateHelper(
1727 '<template id="inner">'
1728 '{{d}}'
1729 '</template>'
1730 '<template repeat="{{a}}">'
1731 '{{b}}'
1732 '<template ref="inner" bind="{{c}}"></template>'
1733 '</template>', 2));
1734
1735 nestedIterateIterateHelper(s, start) {
1736 var div = createTestHtml(s);
1737
1738 var m = toObservable({
1739 'a': [
1740 {
1741 'b': 1,
1742 'c': [{'d': 11}, {'d': 12}]
1743 },
1744 {
1745 'b': 2,
1746 'c': [{'d': 21}, {'d': 22}]
1747 }
1748 ]
1749 });
1750
1751 recursivelySetTemplateModel(div, m);
1752 return new Future(() {
1753
1754 var i = start;
1755 expect(div.nodes[i++].text, '1');
1756 expect(div.nodes[i++].tagName, 'TEMPLATE');
1757 expect(div.nodes[i++].text, '11');
1758 expect(div.nodes[i++].text, '12');
1759 expect(div.nodes[i++].text, '2');
1760 expect(div.nodes[i++].tagName, 'TEMPLATE');
1761 expect(div.nodes[i++].text, '21');
1762 expect(div.nodes[i++].text, '22');
1763
1764 m['a'][1] = toObservable({
1765 'b': 3,
1766 'c': [{'d': 31}, {'d': 32}, {'d': 33}]
1767 });
1768
1769 i = start + 4;
1770 }).then(endOfMicrotask).then((_) {
1771 expect(div.nodes[start + 4].text, '3');
1772 expect(div.nodes[start + 6].text, '31');
1773 expect(div.nodes[start + 7].text, '32');
1774 expect(div.nodes[start + 8].text, '33');
1775 });
1776 }
1777
1778 test('NestedRepeatBind', () => nestedIterateIterateHelper(
1779 '<template repeat="{{a}}">'
1780 '{{b}}'
1781 '<template repeat="{{c}}">'
1782 '{{d}}'
1783 '</template>'
1784 '</template>', 1));
1785
1786 test('NestedRepeatRepeatWithRef', () => nestedIterateIterateHelper(
1787 '<template id="inner">'
1788 '{{d}}'
1789 '</template>'
1790 '<template repeat="{{a}}">'
1791 '{{b}}'
1792 '<template ref="inner" repeat="{{c}}"></template>'
1793 '</template>', 2));
1794
1795 test('NestedRepeatSelfRef', () {
1796 var div = createTestHtml(
1797 '<template id="t" repeat="{{}}">'
1798 '{{name}}'
1799 '<template ref="t" repeat="{{items}}"></template>'
1800 '</template>');
1801
1802 var template = div.firstChild;
1803
1804 var m = toObservable([
1805 {
1806 'name': 'Item 1',
1807 'items': [
1808 {
1809 'name': 'Item 1.1',
1810 'items': [
1811 {
1812 'name': 'Item 1.1.1',
1813 'items': []
1814 }
1815 ]
1816 },
1817 {
1818 'name': 'Item 1.2'
1819 }
1820 ]
1821 },
1822 {
1823 'name': 'Item 2',
1824 'items': []
1825 },
1826 ]);
1827
1828 templateBind(template).model = m;
1829
1830 int i = 1;
1831 return new Future(() {
1832 expect(div.nodes[i++].text, 'Item 1');
1833 expect(div.nodes[i++].tagName, 'TEMPLATE');
1834 expect(div.nodes[i++].text, 'Item 1.1');
1835 expect(div.nodes[i++].tagName, 'TEMPLATE');
1836 expect(div.nodes[i++].text, 'Item 1.1.1');
1837 expect(div.nodes[i++].tagName, 'TEMPLATE');
1838 expect(div.nodes[i++].text, 'Item 1.2');
1839 expect(div.nodes[i++].tagName, 'TEMPLATE');
1840 expect(div.nodes[i++].text, 'Item 2');
1841
1842 m[0] = toObservable({'name': 'Item 1 changed'});
1843
1844 i = 1;
1845 }).then(endOfMicrotask).then((_) {
1846 expect(div.nodes[i++].text, 'Item 1 changed');
1847 expect(div.nodes[i++].tagName, 'TEMPLATE');
1848 expect(div.nodes[i++].text, 'Item 2');
1849 });
1850 });
1851
1852 // Note: we don't need a zone for this test, and we don't want to alter timing
1853 // since we're testing a rather subtle relationship between select and option.
1854 test('Attribute Template Option/Optgroup', () {
1855 var div = createTestHtml(
1856 '<template bind>'
1857 '<select selectedIndex="{{ selected }}">'
1858 '<optgroup template repeat="{{ groups }}" label="{{ name }}">'
1859 '<option template repeat="{{ items }}">{{ val }}</option>'
1860 '</optgroup>'
1861 '</select>'
1862 '</template>');
1863
1864 var template = div.firstChild;
1865 var m = toObservable({
1866 'selected': 1,
1867 'groups': [{
1868 'name': 'one', 'items': [{ 'val': 0 }, { 'val': 1 }]
1869 }],
1870 });
1871
1872 templateBind(template).model = m;
1873
1874 var completer = new Completer();
1875
1876 new MutationObserver((records, observer) {
1877 var select = div.nodes[0].nextNode;
1878 if (select == null || select.querySelector('option') == null) return;
1879
1880 observer.disconnect();
1881 new Future(() {
1882 expect(select.nodes.length, 2);
1883
1884 expect(select.selectedIndex, 1, reason: 'selected index should update '
1885 'after template expands.');
1886
1887 expect(select.nodes[0].tagName, 'TEMPLATE');
1888 var optgroup = select.nodes[1];
1889 expect(optgroup.nodes[0].tagName, 'TEMPLATE');
1890 expect(optgroup.nodes[1].tagName, 'OPTION');
1891 expect(optgroup.nodes[1].text, '0');
1892 expect(optgroup.nodes[2].tagName, 'OPTION');
1893 expect(optgroup.nodes[2].text, '1');
1894
1895 completer.complete();
1896 });
1897 })..observe(div, childList: true, subtree: true);
1898
1899 Observable.dirtyCheck();
1900
1901 return completer.future;
1902 });
1903
1904 test('NestedIterateTableMixedSemanticNative', () {
1905 if (!parserHasNativeTemplate) return null;
1906
1907 var div = createTestHtml(
1908 '<table><tbody>'
1909 '<template repeat="{{}}">'
1910 '<tr>'
1911 '<td template repeat="{{}}" class="{{ val }}">{{ val }}</td>'
1912 '</tr>'
1913 '</template>'
1914 '</tbody></table>');
1915 var template = div.firstChild.firstChild.firstChild;
1916
1917 var m = toObservable([
1918 [{ 'val': 0 }, { 'val': 1 }],
1919 [{ 'val': 2 }, { 'val': 3 }]
1920 ]);
1921
1922 templateBind(template).model = m;
1923 return new Future(() {
1924 var tbody = div.nodes[0].nodes[0];
1925
1926 // 1 for the <tr template>, 2 * (1 tr)
1927 expect(tbody.nodes.length, 3);
1928
1929 // 1 for the <td template>, 2 * (1 td)
1930 expect(tbody.nodes[1].nodes.length, 3);
1931
1932 expect(tbody.nodes[1].nodes[1].text, '0');
1933 expect(tbody.nodes[1].nodes[2].text, '1');
1934
1935 // 1 for the <td template>, 2 * (1 td)
1936 expect(tbody.nodes[2].nodes.length, 3);
1937 expect(tbody.nodes[2].nodes[1].text, '2');
1938 expect(tbody.nodes[2].nodes[2].text, '3');
1939
1940 // Asset the 'class' binding is retained on the semantic template (just
1941 // check the last one).
1942 expect(tbody.nodes[2].nodes[2].attributes["class"], '3');
1943 });
1944 });
1945
1946 test('NestedIterateTable', () {
1947 var div = createTestHtml(
1948 '<table><tbody>'
1949 '<tr template repeat="{{}}">'
1950 '<td template repeat="{{}}" class="{{ val }}">{{ val }}</td>'
1951 '</tr>'
1952 '</tbody></table>');
1953 var template = div.firstChild.firstChild.firstChild;
1954
1955 var m = toObservable([
1956 [{ 'val': 0 }, { 'val': 1 }],
1957 [{ 'val': 2 }, { 'val': 3 }]
1958 ]);
1959
1960 templateBind(template).model = m;
1961 return new Future(() {
1962
1963 var i = 1;
1964 var tbody = div.nodes[0].nodes[0];
1965
1966 // 1 for the <tr template>, 2 * (1 tr)
1967 expect(tbody.nodes.length, 3);
1968
1969 // 1 for the <td template>, 2 * (1 td)
1970 expect(tbody.nodes[1].nodes.length, 3);
1971 expect(tbody.nodes[1].nodes[1].text, '0');
1972 expect(tbody.nodes[1].nodes[2].text, '1');
1973
1974 // 1 for the <td template>, 2 * (1 td)
1975 expect(tbody.nodes[2].nodes.length, 3);
1976 expect(tbody.nodes[2].nodes[1].text, '2');
1977 expect(tbody.nodes[2].nodes[2].text, '3');
1978
1979 // Asset the 'class' binding is retained on the semantic template (just
1980 // check the last one).
1981 expect(tbody.nodes[2].nodes[2].attributes['class'], '3');
1982 });
1983 });
1984
1985 test('NestedRepeatDeletionOfMultipleSubTemplates', () {
1986 var div = createTestHtml(
1987 '<ul>'
1988 '<template repeat="{{}}" id=t1>'
1989 '<li>{{name}}'
1990 '<ul>'
1991 '<template ref=t1 repeat="{{items}}"></template>'
1992 '</ul>'
1993 '</li>'
1994 '</template>'
1995 '</ul>');
1996
1997 var m = toObservable([
1998 {
1999 'name': 'Item 1',
2000 'items': [
2001 {
2002 'name': 'Item 1.1'
2003 }
2004 ]
2005 }
2006 ]);
2007 var ul = div.firstChild;
2008 var t = ul.firstChild;
2009
2010 templateBind(t).model = m;
2011 return new Future(() {
2012 expect(ul.nodes.length, 2);
2013 var ul2 = ul.nodes[1].nodes[1];
2014 expect(ul2.nodes.length, 2);
2015 var ul3 = ul2.nodes[1].nodes[1];
2016 expect(ul3.nodes.length, 1);
2017
2018 m.removeAt(0);
2019 }).then(endOfMicrotask).then((_) {
2020 expect(ul.nodes.length, 1);
2021 });
2022 });
2023
2024 test('DeepNested', () {
2025 var div = createTestHtml(
2026 '<template bind="{{a}}">'
2027 '<p>'
2028 '<template bind="{{b}}">'
2029 '{{ c }}'
2030 '</template>'
2031 '</p>'
2032 '</template>');
2033 var template = div.firstChild;
2034 var m = toObservable({
2035 'a': {
2036 'b': {
2037 'c': 42
2038 }
2039 }
2040 });
2041 templateBind(template).model = m;
2042 return new Future(() {
2043 expect(div.nodes[1].tagName, 'P');
2044 expect(div.nodes[1].nodes.first.tagName, 'TEMPLATE');
2045 expect(div.nodes[1].nodes[1].text, '42');
2046 });
2047 });
2048
2049 test('TemplateContentRemoved', () {
2050 var div = createTestHtml('<template bind="{{}}">{{ }}</template>');
2051 var template = div.firstChild;
2052 var model = 42;
2053
2054 templateBind(template).model = model;
2055 return new Future(() {
2056 expect(div.nodes[1].text, '42');
2057 expect(div.nodes[0].text, '');
2058 });
2059 });
2060
2061 test('TemplateContentRemovedEmptyArray', () {
2062 var div = createTestHtml('<template iterate>Remove me</template>');
2063 var template = div.firstChild;
2064 templateBind(template).model = [];
2065 return new Future(() {
2066 expect(div.nodes.length, 1);
2067 expect(div.nodes[0].text, '');
2068 });
2069 });
2070
2071 test('TemplateContentRemovedNested', () {
2072 var div = createTestHtml(
2073 '<template bind="{{}}">'
2074 '{{ a }}'
2075 '<template bind="{{}}">'
2076 '{{ b }}'
2077 '</template>'
2078 '</template>');
2079 var template = div.firstChild;
2080 var model = toObservable({
2081 'a': 1,
2082 'b': 2
2083 });
2084 templateBind(template).model = model;
2085 return new Future(() {
2086 expect(div.nodes[0].text, '');
2087 expect(div.nodes[1].text, '1');
2088 expect(div.nodes[2].text, '');
2089 expect(div.nodes[3].text, '2');
2090 });
2091 });
2092
2093 test('BindWithUndefinedModel', () {
2094 var div = createTestHtml(
2095 '<template bind="{{}}" if="{{}}">{{ a }}</template>');
2096 var template = div.firstChild;
2097
2098 var model = toObservable({'a': 42});
2099 templateBind(template).model = model;
2100 return new Future(() {
2101 expect(div.nodes[1].text, '42');
2102
2103 model = null;
2104 templateBind(template).model = model;
2105 }).then(endOfMicrotask).then((_) {
2106 expect(div.nodes.length, 1);
2107
2108 model = toObservable({'a': 42});
2109 templateBind(template).model = model;
2110 }).then(endOfMicrotask).then((_) {
2111 expect(div.nodes[1].text, '42');
2112 });
2113 });
2114
2115 test('BindNested', () {
2116 var div = createTestHtml(
2117 '<template bind="{{}}">'
2118 'Name: {{ name }}'
2119 '<template bind="{{wife}}" if="{{wife}}">'
2120 'Wife: {{ name }}'
2121 '</template>'
2122 '<template bind="{{child}}" if="{{child}}">'
2123 'Child: {{ name }}'
2124 '</template>'
2125 '</template>');
2126 var template = div.firstChild;
2127 var m = toObservable({
2128 'name': 'Hermes',
2129 'wife': {
2130 'name': 'LaBarbara'
2131 }
2132 });
2133 templateBind(template).model = m;
2134
2135 return new Future(() {
2136 expect(div.nodes.length, 5);
2137 expect(div.nodes[1].text, 'Name: Hermes');
2138 expect(div.nodes[3].text, 'Wife: LaBarbara');
2139
2140 m['child'] = toObservable({'name': 'Dwight'});
2141
2142 }).then(endOfMicrotask).then((_) {
2143 expect(div.nodes.length, 6);
2144 expect(div.nodes[5].text, 'Child: Dwight');
2145
2146 m.remove('wife');
2147
2148 }).then(endOfMicrotask).then((_) {
2149 expect(div.nodes.length, 5);
2150 expect(div.nodes[4].text, 'Child: Dwight');
2151 });
2152 });
2153
2154 test('BindRecursive', () {
2155 var div = createTestHtml(
2156 '<template bind="{{}}" if="{{}}" id="t">'
2157 'Name: {{ name }}'
2158 '<template bind="{{friend}}" if="{{friend}}" ref="t"></template>'
2159 '</template>');
2160 var template = div.firstChild;
2161 var m = toObservable({
2162 'name': 'Fry',
2163 'friend': {
2164 'name': 'Bender'
2165 }
2166 });
2167 templateBind(template).model = m;
2168 return new Future(() {
2169 expect(div.nodes.length, 5);
2170 expect(div.nodes[1].text, 'Name: Fry');
2171 expect(div.nodes[3].text, 'Name: Bender');
2172
2173 m['friend']['friend'] = toObservable({'name': 'Leela'});
2174 }).then(endOfMicrotask).then((_) {
2175 expect(div.nodes.length, 7);
2176 expect(div.nodes[5].text, 'Name: Leela');
2177
2178 m['friend'] = toObservable({'name': 'Leela'});
2179 }).then(endOfMicrotask).then((_) {
2180 expect(div.nodes.length, 5);
2181 expect(div.nodes[3].text, 'Name: Leela');
2182 });
2183 });
2184
2185 test('Template - Self is terminator', () {
2186 var div = createTestHtml(
2187 '<template repeat>{{ foo }}'
2188 '<template bind></template>'
2189 '</template>');
2190 var template = div.firstChild;
2191
2192 var m = toObservable([{ 'foo': 'bar' }]);
2193 templateBind(template).model = m;
2194 return new Future(() {
2195
2196 m.add(toObservable({ 'foo': 'baz' }));
2197 templateBind(template).model = m;
2198 }).then(endOfMicrotask).then((_) {
2199
2200 expect(div.nodes.length, 5);
2201 expect(div.nodes[1].text, 'bar');
2202 expect(div.nodes[3].text, 'baz');
2203 });
2204 });
2205
2206 test('Template - Same Contents, Different Array has no effect', () {
2207 if (!MutationObserver.supported) return null;
2208
2209 var div = createTestHtml('<template repeat>{{ foo }}</template>');
2210 var template = div.firstChild;
2211
2212 var m = toObservable([{ 'foo': 'bar' }, { 'foo': 'bat'}]);
2213 templateBind(template).model = m;
2214 var observer = new MutationObserver((x, y) {});
2215 return new Future(() {
2216 observer.observe(div, childList: true);
2217
2218 var template = div.firstChild;
2219 templateBind(template).model = new ObservableList.from(m);
2220 }).then(endOfMicrotask).then((_) {
2221 var records = observer.takeRecords();
2222 expect(records.length, 0);
2223 });
2224 });
2225
2226 test('RecursiveRef', () {
2227 var div = createTestHtml(
2228 '<template bind>'
2229 '<template id=src>{{ foo }}</template>'
2230 '<template bind ref=src></template>'
2231 '</template>');
2232
2233 var m = toObservable({'foo': 'bar'});
2234 templateBind(div.firstChild).model = m;
2235 return new Future(() {
2236 expect(div.nodes.length, 4);
2237 expect(div.nodes[3].text, 'bar');
2238 });
2239 });
2240
2241 test('baseURI', () {
2242 // TODO(jmesserly): Dart's setInnerHtml breaks this test -- the template
2243 // URL is created as blank despite the NullTreeSanitizer.
2244 // Use JS interop as a workaround.
2245 //var div = createTestHtml('<template bind>'
2246 // '<div style="background: url(foo.jpg)"></div></template>');
2247 var div = new DivElement();
2248 new JsObject.fromBrowserObject(div)['innerHTML'] = '<template bind>'
2249 '<div style="background: url(foo.jpg)"></div></template>';
2250 testDiv.append(div);
2251 TemplateBindExtension.decorate(div.firstChild);
2252
2253 var local = document.createElement('div');
2254 local.attributes['style'] = 'background: url(foo.jpg)';
2255 div.append(local);
2256 var template = div.firstChild;
2257 templateBind(template).model = {};
2258 return new Future(() {
2259 expect(div.nodes[1].style.backgroundImage, local.style.backgroundImage);
2260 });
2261 });
2262
2263 test('ChangeRefId', () {
2264 var div = createTestHtml(
2265 '<template id="a">a:{{ }}</template>'
2266 '<template id="b">b:{{ }}</template>'
2267 '<template repeat="{{}}">'
2268 '<template ref="a" bind="{{}}"></template>'
2269 '</template>');
2270 var template = div.nodes[2];
2271 var model = toObservable([]);
2272 templateBind(template).model = model;
2273 return new Future(() {
2274 expect(div.nodes.length, 3);
2275
2276 document.getElementById('a').id = 'old-a';
2277 document.getElementById('b').id = 'a';
2278
2279 model..add(1)..add(2);
2280 }).then(endOfMicrotask).then((_) {
2281
2282 expect(div.nodes.length, 7);
2283 expect(div.nodes[4].text, 'b:1');
2284 expect(div.nodes[6].text, 'b:2');
2285 });
2286 });
2287
2288 test('Content', () {
2289 var div = createTestHtml(
2290 '<template><a></a></template>'
2291 '<template><b></b></template>');
2292 var templateA = div.nodes.first;
2293 var templateB = div.nodes.last;
2294 var contentA = templateBind(templateA).content;
2295 var contentB = templateBind(templateB).content;
2296 expect(contentA, isNotNull);
2297
2298 expect(templateA.ownerDocument, isNot(equals(contentA.ownerDocument)));
2299 expect(templateB.ownerDocument, isNot(equals(contentB.ownerDocument)));
2300
2301 expect(templateB.ownerDocument, templateA.ownerDocument);
2302 expect(contentB.ownerDocument, contentA.ownerDocument);
2303
2304 // NOTE: these tests don't work under ShadowDOM polyfill.
2305 // Disabled for now.
2306 //expect(templateA.ownerDocument.window, window);
2307 //expect(templateB.ownerDocument.window, window);
2308
2309 expect(contentA.ownerDocument.window, null);
2310 expect(contentB.ownerDocument.window, null);
2311
2312 expect(contentA.nodes.last, contentA.nodes.first);
2313 expect(contentA.nodes.first.tagName, 'A');
2314
2315 expect(contentB.nodes.last, contentB.nodes.first);
2316 expect(contentB.nodes.first.tagName, 'B');
2317 });
2318
2319 test('NestedContent', () {
2320 var div = createTestHtml(
2321 '<template>'
2322 '<template></template>'
2323 '</template>');
2324 var templateA = div.nodes.first;
2325 var templateB = templateBind(templateA).content.nodes.first;
2326
2327 expect(templateB.ownerDocument, templateBind(templateA)
2328 .content.ownerDocument);
2329 expect(templateBind(templateB).content.ownerDocument,
2330 templateBind(templateA).content.ownerDocument);
2331 });
2332
2333 test('BindShadowDOM', () {
2334 if (!ShadowRoot.supported) return null;
2335
2336 var root = createShadowTestHtml(
2337 '<template bind="{{}}">Hi {{ name }}</template>');
2338 var model = toObservable({'name': 'Leela'});
2339 templateBind(root.firstChild).model = model;
2340 return new Future(() => expect(root.nodes[1].text, 'Hi Leela'));
2341 });
2342
2343 // Dart note: this test seems gone from JS. Keeping for posterity sake.
2344 test('BindShadowDOM createInstance', () {
2345 if (!ShadowRoot.supported) return null;
2346
2347 var model = toObservable({'name': 'Leela'});
2348 var template = new Element.html('<template>Hi {{ name }}</template>');
2349 var root = createShadowTestHtml('');
2350 root.nodes.add(templateBind(template).createInstance(model));
2351
2352 return new Future(() {
2353 expect(root.text, 'Hi Leela');
2354
2355 model['name'] = 'Fry';
2356 }).then(endOfMicrotask).then((_) {
2357 expect(root.text, 'Hi Fry');
2358 });
2359 });
2360
2361 test('BindShadowDOM Template Ref', () {
2362 if (!ShadowRoot.supported) return null;
2363 var root = createShadowTestHtml(
2364 '<template id=foo>Hi</template><template bind ref=foo></template>');
2365 var template = root.nodes[1];
2366 templateBind(template).model = toObservable({});
2367 return new Future(() {
2368 expect(root.nodes.length, 3);
2369 clearAllTemplates(root);
2370 });
2371 });
2372
2373 // https://github.com/Polymer/TemplateBinding/issues/8
2374 test('UnbindingInNestedBind', () {
2375 var div = createTestHtml(
2376 '<template bind="{{outer}}" if="{{outer}}" syntax="testHelper">'
2377 '<template bind="{{inner}}" if="{{inner}}">'
2378 '{{ age }}'
2379 '</template>'
2380 '</template>');
2381 var template = div.firstChild;
2382 var syntax = new UnbindingInNestedBindSyntax();
2383 var model = toObservable({'outer': {'inner': {'age': 42}}});
2384
2385 templateBind(template)..model = model..bindingDelegate = syntax;
2386
2387 return new Future(() {
2388 expect(syntax.count, 1);
2389
2390 var inner = model['outer']['inner'];
2391 model['outer'] = null;
2392
2393 }).then(endOfMicrotask).then((_) {
2394 expect(syntax.count, 1);
2395
2396 model['outer'] = toObservable({'inner': {'age': 2}});
2397 syntax.expectedAge = 2;
2398
2399 }).then(endOfMicrotask).then((_) {
2400 expect(syntax.count, 2);
2401 });
2402 });
2403
2404 // https://github.com/toolkitchen/mdv/issues/8
2405 test('DontCreateInstancesForAbandonedIterators', () {
2406 var div = createTestHtml(
2407 '<template bind="{{}} {{}}">'
2408 '<template bind="{{}}">Foo</template>'
2409 '</template>');
2410 var template = div.firstChild;
2411 templateBind(template).model = null;
2412 return nextMicrotask;
2413 });
2414
2415 test('CreateInstance', () {
2416 var div = createTestHtml(
2417 '<template bind="{{a}}">'
2418 '<template bind="{{b}}">'
2419 '{{ foo }}:{{ replaceme }}'
2420 '</template>'
2421 '</template>');
2422 var outer = templateBind(div.nodes.first);
2423 var model = toObservable({'b': {'foo': 'bar'}});
2424
2425 var instance = outer.createInstance(model, new TestBindingSyntax());
2426 expect(instance.firstChild.nextNode.text, 'bar:replaced');
2427
2428 clearAllTemplates(instance);
2429 });
2430
2431 test('CreateInstance - sync error', () {
2432 var div = createTestHtml('<template>{{foo}}</template>');
2433 var outer = templateBind(div.nodes.first);
2434 var model = 1; // model is missing 'foo' should throw.
2435 expect(() => outer.createInstance(model, new TestBindingSyntax()),
2436 throwsA(_isNoSuchMethodError));
2437 });
2438
2439 test('CreateInstance - async error', () {
2440 var div = createTestHtml(
2441 '<template>'
2442 '<template bind="{{b}}">'
2443 '{{ foo }}:{{ replaceme }}'
2444 '</template>'
2445 '</template>');
2446 var outer = templateBind(div.nodes.first);
2447 var model = toObservable({'b': 1}); // missing 'foo' should throw.
2448
2449 bool seen = false;
2450 runZoned(() => outer.createInstance(model, new TestBindingSyntax()),
2451 onError: (e) {
2452 _expectNoSuchMethod(e);
2453 seen = true;
2454 });
2455 return new Future(() { expect(seen, isTrue); });
2456 });
2457
2458 test('Repeat - svg', () {
2459 var div = createTestHtml(
2460 '<svg width="400" height="110">'
2461 '<template repeat>'
2462 '<rect width="{{ width }}" height="{{ height }}" />'
2463 '</template>'
2464 '</svg>');
2465
2466 var model = toObservable([{ 'width': 10, 'height': 11 },
2467 { 'width': 20, 'height': 21 }]);
2468 var svg = div.firstChild;
2469 var template = svg.firstChild;
2470 templateBind(template).model = model;
2471
2472 return new Future(() {
2473 expect(svg.nodes.length, 3);
2474 expect(svg.nodes[1].attributes['width'], '10');
2475 expect(svg.nodes[1].attributes['height'], '11');
2476 expect(svg.nodes[2].attributes['width'], '20');
2477 expect(svg.nodes[2].attributes['height'], '21');
2478 });
2479 });
2480
2481 test('Bootstrap', () {
2482 var div = new DivElement();
2483 div.innerHtml =
2484 '<template>'
2485 '<div></div>'
2486 '<template>'
2487 'Hello'
2488 '</template>'
2489 '</template>';
2490
2491 TemplateBindExtension.bootstrap(div);
2492 var template = templateBind(div.nodes.first);
2493 expect(template.content.nodes.length, 2);
2494 var template2 = templateBind(template.content.nodes.first.nextNode);
2495 expect(template2.content.nodes.length, 1);
2496 expect(template2.content.nodes.first.text, 'Hello');
2497
2498 template = new Element.tag('template');
2499 template.innerHtml =
2500 '<template>'
2501 '<div></div>'
2502 '<template>'
2503 'Hello'
2504 '</template>'
2505 '</template>';
2506
2507 TemplateBindExtension.bootstrap(template);
2508 template2 = templateBind(templateBind(template).content.nodes.first);
2509 expect(template2.content.nodes.length, 2);
2510 var template3 = templateBind(template2.content.nodes.first.nextNode);
2511 expect(template3.content.nodes.length, 1);
2512 expect(template3.content.nodes.first.text, 'Hello');
2513 });
2514
2515 test('issue-285', () {
2516 var div = createTestHtml(
2517 '<template>'
2518 '<template bind if="{{show}}">'
2519 '<template id=del repeat="{{items}}">'
2520 '{{}}'
2521 '</template>'
2522 '</template>'
2523 '</template>');
2524
2525 var template = div.firstChild;
2526
2527 var model = toObservable({
2528 'show': true,
2529 'items': [1]
2530 });
2531
2532 div.append(templateBind(template).createInstance(model,
2533 new Issue285Syntax()));
2534
2535 return new Future(() {
2536 expect(template.nextNode.nextNode.nextNode.text, '2');
2537 model['show'] = false;
2538 }).then(endOfMicrotask).then((_) {
2539 model['show'] = true;
2540 }).then(endOfMicrotask).then((_) {
2541 expect(template.nextNode.nextNode.nextNode.text, '2');
2542 });
2543 });
2544
2545 test('Accessor value retrieval count', () {
2546 var div = createTestHtml(
2547 '<template bind>{{ prop }}</template>');
2548
2549 var model = new TestAccessorModel();
2550
2551 templateBind(div.firstChild).model = model;
2552
2553 return new Future(() {
2554 expect(model.count, 1);
2555
2556 model.value++;
2557 // Dart note: we don't handle getters in @observable, so we need to
2558 // notify regardless.
2559 model.notifyPropertyChange(#prop, 1, model.value);
2560
2561 }).then(endOfMicrotask).then((_) {
2562 expect(model.count, 2);
2563 });
2564 });
2565
2566 test('issue-141', () {
2567 var div = createTestHtml(
2568 '<template bind>'
2569 '<div foo="{{foo1}} {{foo2}}" bar="{{bar}}"></div>'
2570 '</template>');
2571
2572 var template = div.firstChild;
2573 var model = toObservable({
2574 'foo1': 'foo1Value',
2575 'foo2': 'foo2Value',
2576 'bar': 'barValue'
2577 });
2578
2579 templateBind(template).model = model;
2580 return new Future(() {
2581 expect(div.lastChild.attributes['bar'], 'barValue');
2582 });
2583 });
2584
2585 test('issue-18', () {
2586 var delegate = new Issue18Syntax();
2587
2588 var div = createTestHtml(
2589 '<template bind>'
2590 '<div class="foo: {{ bar }}"></div>'
2591 '</template>');
2592
2593 var template = div.firstChild;
2594 var model = toObservable({'bar': 2});
2595
2596 templateBind(template)..model = model..bindingDelegate = delegate;
2597
2598 return new Future(() {
2599 expect(div.lastChild.attributes['class'], 'foo: 2');
2600 });
2601 });
2602
2603 test('issue-152', () {
2604 var div = createTestHtml(
2605 '<template ref=notThere bind>XXX</template>');
2606
2607 var template = div.firstChild;
2608 templateBind(template).model = {};
2609
2610 return new Future(() {
2611 // if a ref cannot be located, a template will continue to use itself
2612 // as the source of template instances.
2613 expect(div.nodes[1].text, 'XXX');
2614 });
2615 });
2616 }
2617
2618 compatTests() {
2619 test('underbar bindings', () {
2620 var div = createTestHtml(
2621 '<template bind>'
2622 '<div _style="color: {{ color }};"></div>'
2623 '<img _src="{{ url }}">'
2624 '<a _href="{{ url2 }}">Link</a>'
2625 '<input type="number" _value="{{ number }}">'
2626 '</template>');
2627
2628 var template = div.firstChild;
2629 var model = toObservable({
2630 'color': 'red',
2631 'url': 'pic.jpg',
2632 'url2': 'link.html',
2633 'number': 4
2634 });
2635
2636 templateBind(template).model = model;
2637 return new Future(() {
2638 var subDiv = div.firstChild.nextNode;
2639 expect(subDiv.attributes['style'], 'color: red;');
2640
2641 var img = subDiv.nextNode;
2642 expect(img.attributes['src'], 'pic.jpg');
2643
2644 var a = img.nextNode;
2645 expect(a.attributes['href'], 'link.html');
2646
2647 var input = a.nextNode;
2648 expect(input.value, '4');
2649 });
2650 });
2651 }
2652
2653 // TODO(jmesserly): ideally we could test the type with isNoSuchMethodError,
2654 // however dart:js converts the nSM into a String at some point.
2655 // So for now we do string comparison.
2656 _isNoSuchMethodError(e) => '$e'.contains('NoSuchMethodError');
2657
2658 _expectNoSuchMethod(e) {
2659 // expect(e, isNoSuchMethodError);
2660 expect('$e', contains('NoSuchMethodError'));
2661 }
2662
2663 class Issue285Syntax extends BindingDelegate {
2664 prepareInstanceModel(template) {
2665 if (template.id == 'del') return (val) => val * 2;
2666 }
2667 }
2668
2669 class TestBindingSyntax extends BindingDelegate {
2670 prepareBinding(String path, name, node) {
2671 if (path.trim() == 'replaceme') {
2672 return (m, n, oneTime) => new PathObserver('replaced', '');
2673 }
2674 return null;
2675 }
2676 }
2677
2678 class UnbindingInNestedBindSyntax extends BindingDelegate {
2679 int expectedAge = 42;
2680 int count = 0;
2681
2682 prepareBinding(path, name, node) {
2683 if (name != 'text' || path != 'age') return null;
2684
2685 return (model, _, oneTime) {
2686 expect(model['age'], expectedAge);
2687 count++;
2688 return new PathObserver(model, path);
2689 };
2690 }
2691 }
2692
2693 class Issue18Syntax extends BindingDelegate {
2694 prepareBinding(path, name, node) {
2695 if (name != 'class') return null;
2696
2697 return (model, _, oneTime) => new PathObserver(model, path);
2698 }
2699 }
2700
2701 class BindIfMinimalDiscardChanges extends BindingDelegate {
2702 Map<String, int> discardChangesCalled;
2703
2704 BindIfMinimalDiscardChanges(this.discardChangesCalled) : super() {}
2705
2706 prepareBinding(path, name, node) {
2707 return (model, node, oneTime) =>
2708 new DiscardCountingPathObserver(discardChangesCalled, model, path);
2709 }
2710 }
2711
2712 class DiscardCountingPathObserver extends PathObserver {
2713 Map<String, int> discardChangesCalled;
2714
2715 DiscardCountingPathObserver(this.discardChangesCalled, model, path)
2716 : super(model, path) {}
2717
2718 get value {
2719 discardChangesCalled[path.toString()]++;
2720 return super.value;
2721 }
2722 }
2723
2724 class TestAccessorModel extends Observable {
2725 @observable var value = 1;
2726 var count = 0;
2727
2728 @reflectable
2729 get prop {
2730 count++;
2731 return value;
2732 }
2733 }
OLDNEW
« no previous file with comments | « pkg/template_binding/test/node_bind_test.html ('k') | pkg/template_binding/test/template_binding_test.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698