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

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

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

Powered by Google App Engine
This is Rietveld 408576698