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

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

Issue 50203004: port TemplateBinding to ed3266266e751b5ab1f75f8e0509d0d5f0ef35d8 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/template_binding/test/template_binding_test.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_element_test;
6
7 import 'dart:async';
8 import 'dart:collection';
9 import 'dart:html';
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 'utils.dart';
16
17 // Note: this file ported from
18 // https://github.com/toolkitchen/mdv/blob/master/tests/template_element.js
19 // TODO(jmesserly): submit a small cleanup patch to original. I fixed some
20 // cases where "div" and "t" were unintentionally using the JS global scope;
21 // look for "assertNodesAre".
22
23 main() {
24 useHtmlConfiguration();
25 group('Template Element', templateElementTests);
26 }
27
28 templateElementTests() {
29 setUp(() {
30 document.body.append(testDiv = new DivElement());
31 });
32
33 tearDown(() {
34 testDiv.remove();
35 testDiv = null;
36 });
37
38 var expando = new Expando('observeTest');
39 void addExpandos(node) {
40 while (node != null) {
41 expando[node] = node.text;
42 node = node.nextNode;
43 }
44 }
45
46 void checkExpandos(node) {
47 expect(node, isNotNull);
48 while (node != null) {
49 expect(expando[node], node.text);
50 node = node.nextNode;
51 }
52 }
53
54 observeTest('Template', () {
55 var div = createTestHtml('<template bind={{}}>text</template>');
56 recursivelySetTemplateModel(div, null);
57 performMicrotaskCheckpoint();
58 expect(div.nodes.length, 2);
59 expect(div.nodes.last.text, 'text');
60 });
61
62 observeTest('Template bind, no parent', () {
63 var div = createTestHtml('<template bind>text</template>');
64 var template = div.nodes[0];
65 template.remove();
66
67 recursivelySetTemplateModel(template, toObservable({}));
68 performMicrotaskCheckpoint();
69 expect(template.nodes.length, 0);
70 expect(template.nextNode, null);
71 });
72
73 observeTest('Template bind, no defaultView', () {
74 var div = createTestHtml('<template bind>text</template>');
75 var template = div.nodes[0];
76 var doc = document.implementation.createHtmlDocument('');
77 doc.adoptNode(div);
78 recursivelySetTemplateModel(template, toObservable({}));
79 performMicrotaskCheckpoint();
80 expect(div.nodes.length, 1);
81 });
82
83 observeTest('Template-Empty Bind', () {
84 var div = createTestHtml('<template bind>text</template>');
85 recursivelySetTemplateModel(div, null);
86 performMicrotaskCheckpoint();
87 expect(div.nodes.length, 2);
88 expect(div.nodes.last.text, 'text');
89 });
90
91 observeTest('Template Bind If', () {
92 var div = createTestHtml('<template bind if="{{ foo }}">text</template>');
93 // Note: changed this value from 0->null because zero is not falsey in Dart.
94 // See https://code.google.com/p/dart/issues/detail?id=11956
95 var m = toObservable({ 'foo': null });
96 recursivelySetTemplateModel(div, m);
97 performMicrotaskCheckpoint();
98 expect(div.nodes.length, 1);
99
100 m['foo'] = 1;
101 performMicrotaskCheckpoint();
102 expect(div.nodes.length, 2);
103 expect(div.lastChild.text, 'text');
104 });
105
106 observeTest('Template Bind If, 2', () {
107 var div = createTestHtml(
108 '<template bind="{{ foo }}" if="{{ bar }}">{{ bat }}</template>');
109 var m = toObservable({ 'bar': null, 'foo': { 'bat': 'baz' } });
110 recursivelySetTemplateModel(div, m);
111 performMicrotaskCheckpoint();
112 expect(div.nodes.length, 1);
113
114 m['bar'] = 1;
115 performMicrotaskCheckpoint();
116 expect(div.nodes.length, 2);
117 expect(div.lastChild.text, 'baz');
118 });
119
120 observeTest('Template If', () {
121 var div = createTestHtml('<template if="{{ foo }}">{{ value }}</template>');
122 // Note: changed this value from 0->null because zero is not falsey in Dart.
123 // See https://code.google.com/p/dart/issues/detail?id=11956
124 var m = toObservable({ 'foo': null, 'value': 'foo' });
125 recursivelySetTemplateModel(div, m);
126 performMicrotaskCheckpoint();
127 expect(div.nodes.length, 1);
128
129 m['foo'] = 1;
130 performMicrotaskCheckpoint();
131 expect(div.nodes.length, 2);
132 expect(div.lastChild.text, 'foo');
133 });
134
135 observeTest('Template Repeat If', () {
136 var div = createTestHtml(
137 '<template repeat="{{ foo }}" if="{{ bar }}">{{ }}</template>');
138 // Note: changed this value from 0->null because zero is not falsey in Dart.
139 // See https://code.google.com/p/dart/issues/detail?id=11956
140 var m = toObservable({ 'bar': null, 'foo': [1, 2, 3] });
141 recursivelySetTemplateModel(div, m);
142 performMicrotaskCheckpoint();
143 expect(div.nodes.length, 1);
144
145 m['bar'] = 1;
146 performMicrotaskCheckpoint();
147 expect(div.nodes.length, 4);
148 expect(div.nodes[1].text, '1');
149 expect(div.nodes[2].text, '2');
150 expect(div.nodes[3].text, '3');
151 });
152
153 observeTest('TextTemplateWithNullStringBinding', () {
154 var div = createTestHtml('<template bind={{}}>a{{b}}c</template>');
155 var model = toObservable({'b': 'B'});
156 recursivelySetTemplateModel(div, model);
157
158 performMicrotaskCheckpoint();
159 expect(div.nodes.length, 2);
160 expect(div.nodes.last.text, 'aBc');
161
162 model['b'] = 'b';
163 performMicrotaskCheckpoint();
164 expect(div.nodes.last.text, 'abc');
165
166 model['b'] = null;
167 performMicrotaskCheckpoint();
168 expect(div.nodes.last.text, 'ac');
169
170 model = null;
171 performMicrotaskCheckpoint();
172 // setting model isn't observable.
173 expect(div.nodes.last.text, 'ac');
174 });
175
176 observeTest('TextTemplateWithBindingPath', () {
177 var div = createTestHtml(
178 '<template bind="{{ data }}">a{{b}}c</template>');
179 var model = toObservable({ 'data': {'b': 'B'} });
180 recursivelySetTemplateModel(div, model);
181
182 performMicrotaskCheckpoint();
183 expect(div.nodes.length, 2);
184 expect(div.nodes.last.text, 'aBc');
185
186 model['data']['b'] = 'b';
187 performMicrotaskCheckpoint();
188 expect(div.nodes.last.text, 'abc');
189
190 model['data'] = toObservable({'b': 'X'});
191 performMicrotaskCheckpoint();
192 expect(div.nodes.last.text, 'aXc');
193
194 model['data'] = null;
195 performMicrotaskCheckpoint();
196 expect(div.nodes.last.text, 'ac');
197 });
198
199 observeTest('TextTemplateWithBindingAndConditional', () {
200 var div = createTestHtml(
201 '<template bind="{{}}" if="{{ d }}">a{{b}}c</template>');
202 var model = toObservable({'b': 'B', 'd': 1});
203 recursivelySetTemplateModel(div, model);
204
205 performMicrotaskCheckpoint();
206 expect(div.nodes.length, 2);
207 expect(div.nodes.last.text, 'aBc');
208
209 model['b'] = 'b';
210 performMicrotaskCheckpoint();
211 expect(div.nodes.last.text, 'abc');
212
213 // TODO(jmesserly): MDV set this to empty string and relies on JS conversion
214 // rules. Is that intended?
215 // See https://github.com/toolkitchen/mdv/issues/59
216 model['d'] = null;
217 performMicrotaskCheckpoint();
218 expect(div.nodes.length, 1);
219
220 model['d'] = 'here';
221 model['b'] = 'd';
222
223 performMicrotaskCheckpoint();
224 expect(div.nodes.length, 2);
225 expect(div.nodes.last.text, 'adc');
226 });
227
228 observeTest('TemplateWithTextBinding2', () {
229 var div = createTestHtml(
230 '<template bind="{{ b }}">a{{value}}c</template>');
231 expect(div.nodes.length, 1);
232 var model = toObservable({'b': {'value': 'B'}});
233 recursivelySetTemplateModel(div, model);
234
235 performMicrotaskCheckpoint();
236 expect(div.nodes.length, 2);
237 expect(div.nodes.last.text, 'aBc');
238
239 model['b'] = toObservable({'value': 'b'});
240 performMicrotaskCheckpoint();
241 expect(div.nodes.last.text, 'abc');
242 });
243
244 observeTest('TemplateWithAttributeBinding', () {
245 var div = createTestHtml(
246 '<template bind="{{}}">'
247 '<div foo="a{{b}}c"></div>'
248 '</template>');
249 var model = toObservable({'b': 'B'});
250 recursivelySetTemplateModel(div, model);
251
252 performMicrotaskCheckpoint();
253 expect(div.nodes.length, 2);
254 expect(div.nodes.last.attributes['foo'], 'aBc');
255
256 model['b'] = 'b';
257 performMicrotaskCheckpoint();
258 expect(div.nodes.last.attributes['foo'], 'abc');
259
260 model['b'] = 'X';
261 performMicrotaskCheckpoint();
262 expect(div.nodes.last.attributes['foo'], 'aXc');
263 });
264
265 observeTest('TemplateWithConditionalBinding', () {
266 var div = createTestHtml(
267 '<template bind="{{}}">'
268 '<div foo?="{{b}}"></div>'
269 '</template>');
270 var model = toObservable({'b': 'b'});
271 recursivelySetTemplateModel(div, model);
272
273 performMicrotaskCheckpoint();
274 expect(div.nodes.length, 2);
275 expect(div.nodes.last.attributes['foo'], '');
276 expect(div.nodes.last.attributes, isNot(contains('foo?')));
277
278 model['b'] = null;
279 performMicrotaskCheckpoint();
280 expect(div.nodes.last.attributes, isNot(contains('foo')));
281 });
282
283 observeTest('Repeat', () {
284 var div = createTestHtml(
285 '<template repeat="{{}}"">text</template>');
286
287 var model = toObservable([0, 1, 2]);
288 recursivelySetTemplateModel(div, model);
289
290 performMicrotaskCheckpoint();
291 expect(div.nodes.length, 4);
292
293 model.length = 1;
294 performMicrotaskCheckpoint();
295 expect(div.nodes.length, 2);
296
297 model.addAll(toObservable([3, 4]));
298 performMicrotaskCheckpoint();
299 expect(div.nodes.length, 4);
300
301 model.removeRange(1, 2);
302 performMicrotaskCheckpoint();
303 expect(div.nodes.length, 3);
304 });
305
306 observeTest('Repeat - Reuse Instances', () {
307 var div = createTestHtml('<template repeat>{{ val }}</template>');
308
309 var model = toObservable([
310 {'val': 10},
311 {'val': 5},
312 {'val': 2},
313 {'val': 8},
314 {'val': 1}
315 ]);
316 recursivelySetTemplateModel(div, model);
317
318 performMicrotaskCheckpoint();
319 expect(div.nodes.length, 6);
320 var template = div.firstChild;
321
322 addExpandos(template.nextNode);
323 checkExpandos(template.nextNode);
324
325 model.sort((a, b) => a['val'] - b['val']);
326 performMicrotaskCheckpoint();
327 checkExpandos(template.nextNode);
328
329 model = toObservable(model.reversed);
330 recursivelySetTemplateModel(div, model);
331 performMicrotaskCheckpoint();
332 checkExpandos(template.nextNode);
333
334 for (var item in model) {
335 item['val'] += 1;
336 }
337
338 performMicrotaskCheckpoint();
339 expect(div.nodes[1].text, "11");
340 expect(div.nodes[2].text, "9");
341 expect(div.nodes[3].text, "6");
342 expect(div.nodes[4].text, "3");
343 expect(div.nodes[5].text, "2");
344 });
345
346 observeTest('Bind - Reuse Instance', () {
347 var div = createTestHtml(
348 '<template bind="{{ foo }}">{{ bar }}</template>');
349
350 var model = toObservable({ 'foo': { 'bar': 5 }});
351 recursivelySetTemplateModel(div, model);
352
353 performMicrotaskCheckpoint();
354 expect(div.nodes.length, 2);
355 var template = div.firstChild;
356
357 addExpandos(template.nextNode);
358 checkExpandos(template.nextNode);
359
360 model = toObservable({'foo': model['foo']});
361 recursivelySetTemplateModel(div, model);
362 performMicrotaskCheckpoint();
363 checkExpandos(template.nextNode);
364 });
365
366 observeTest('Repeat-Empty', () {
367 var div = createTestHtml(
368 '<template repeat>text</template>');
369
370 var model = toObservable([0, 1, 2]);
371 recursivelySetTemplateModel(div, model);
372
373 performMicrotaskCheckpoint();
374 expect(div.nodes.length, 4);
375
376 model.length = 1;
377 performMicrotaskCheckpoint();
378 expect(div.nodes.length, 2);
379
380 model.addAll(toObservable([3, 4]));
381 performMicrotaskCheckpoint();
382 expect(div.nodes.length, 4);
383
384 model.removeRange(1, 2);
385 performMicrotaskCheckpoint();
386 expect(div.nodes.length, 3);
387 });
388
389 observeTest('Removal from iteration needs to unbind', () {
390 var div = createTestHtml(
391 '<template repeat="{{}}"><a>{{v}}</a></template>');
392 var model = toObservable([{'v': 0}, {'v': 1}, {'v': 2}, {'v': 3},
393 {'v': 4}]);
394 recursivelySetTemplateModel(div, model);
395 performMicrotaskCheckpoint();
396
397 var nodes = div.nodes.skip(1).toList();
398 var vs = model.toList();
399
400 for (var i = 0; i < 5; i++) {
401 expect(nodes[i].text, '$i');
402 }
403
404 model.length = 3;
405 performMicrotaskCheckpoint();
406 for (var i = 0; i < 5; i++) {
407 expect(nodes[i].text, '$i');
408 }
409
410 vs[3]['v'] = 33;
411 vs[4]['v'] = 44;
412 performMicrotaskCheckpoint();
413 for (var i = 0; i < 5; i++) {
414 expect(nodes[i].text, '$i');
415 }
416 });
417
418 observeTest('DOM Stability on Iteration', () {
419 var div = createTestHtml(
420 '<template repeat="{{}}">{{}}</template>');
421 var model = toObservable([1, 2, 3, 4, 5]);
422 recursivelySetTemplateModel(div, model);
423
424 performMicrotaskCheckpoint();
425
426 // Note: the node at index 0 is the <template>.
427 var nodes = div.nodes.toList();
428 expect(nodes.length, 6, reason: 'list has 5 items');
429
430 model.removeAt(0);
431 model.removeLast();
432
433 performMicrotaskCheckpoint();
434 expect(div.nodes.length, 4, reason: 'list has 3 items');
435 expect(identical(div.nodes[1], nodes[2]), true, reason: '2 not removed');
436 expect(identical(div.nodes[2], nodes[3]), true, reason: '3 not removed');
437 expect(identical(div.nodes[3], nodes[4]), true, reason: '4 not removed');
438
439 model.insert(0, 5);
440 model[2] = 6;
441 model.add(7);
442
443 performMicrotaskCheckpoint();
444
445 expect(div.nodes.length, 6, reason: 'list has 5 items');
446 expect(nodes.contains(div.nodes[1]), false, reason: '5 is a new node');
447 expect(identical(div.nodes[2], nodes[2]), true);
448 expect(nodes.contains(div.nodes[3]), false, reason: '6 is a new node');
449 expect(identical(div.nodes[4], nodes[4]), true);
450 expect(nodes.contains(div.nodes[5]), false, reason: '7 is a new node');
451
452 nodes = div.nodes.toList();
453
454 model.insert(2, 8);
455
456 performMicrotaskCheckpoint();
457
458 expect(div.nodes.length, 7, reason: 'list has 6 items');
459 expect(identical(div.nodes[1], nodes[1]), true);
460 expect(identical(div.nodes[2], nodes[2]), true);
461 expect(nodes.contains(div.nodes[3]), false, reason: '8 is a new node');
462 expect(identical(div.nodes[4], nodes[3]), true);
463 expect(identical(div.nodes[5], nodes[4]), true);
464 expect(identical(div.nodes[6], nodes[5]), true);
465 });
466
467 observeTest('Repeat2', () {
468 var div = createTestHtml(
469 '<template repeat="{{}}">{{value}}</template>');
470 expect(div.nodes.length, 1);
471
472 var model = toObservable([
473 {'value': 0},
474 {'value': 1},
475 {'value': 2}
476 ]);
477 recursivelySetTemplateModel(div, model);
478
479 performMicrotaskCheckpoint();
480 expect(div.nodes.length, 4);
481 expect(div.nodes[1].text, '0');
482 expect(div.nodes[2].text, '1');
483 expect(div.nodes[3].text, '2');
484
485 model[1]['value'] = 'One';
486 performMicrotaskCheckpoint();
487 expect(div.nodes.length, 4);
488 expect(div.nodes[1].text, '0');
489 expect(div.nodes[2].text, 'One');
490 expect(div.nodes[3].text, '2');
491
492 model.replaceRange(0, 1, toObservable([{'value': 'Zero'}]));
493 performMicrotaskCheckpoint();
494 expect(div.nodes.length, 4);
495 expect(div.nodes[1].text, 'Zero');
496 expect(div.nodes[2].text, 'One');
497 expect(div.nodes[3].text, '2');
498 });
499
500 observeTest('TemplateWithInputValue', () {
501 var div = createTestHtml(
502 '<template bind="{{}}">'
503 '<input value="{{x}}">'
504 '</template>');
505 var model = toObservable({'x': 'hi'});
506 recursivelySetTemplateModel(div, model);
507
508 performMicrotaskCheckpoint();
509 expect(div.nodes.length, 2);
510 expect(div.nodes.last.value, 'hi');
511
512 model['x'] = 'bye';
513 expect(div.nodes.last.value, 'hi');
514 performMicrotaskCheckpoint();
515 expect(div.nodes.last.value, 'bye');
516
517 div.nodes.last.value = 'hello';
518 dispatchEvent('input', div.nodes.last);
519 expect(model['x'], 'hello');
520 performMicrotaskCheckpoint();
521 expect(div.nodes.last.value, 'hello');
522 });
523
524 //////////////////////////////////////////////////////////////////////////////
525
526 observeTest('Decorated', () {
527 var div = createTestHtml(
528 '<template bind="{{ XX }}" id="t1">'
529 '<p>Crew member: {{name}}, Job title: {{title}}</p>'
530 '</template>'
531 '<template bind="{{ XY }}" id="t2" ref="t1"></template>');
532
533 var model = toObservable({
534 'XX': {'name': 'Leela', 'title': 'Captain'},
535 'XY': {'name': 'Fry', 'title': 'Delivery boy'},
536 'XZ': {'name': 'Zoidberg', 'title': 'Doctor'}
537 });
538 recursivelySetTemplateModel(div, model);
539
540 performMicrotaskCheckpoint();
541
542 var t1 = document.getElementById('t1');
543 var instance = t1.nextElementSibling;
544 expect(instance.text, 'Crew member: Leela, Job title: Captain');
545
546 var t2 = document.getElementById('t2');
547 instance = t2.nextElementSibling;
548 expect(instance.text, 'Crew member: Fry, Job title: Delivery boy');
549
550 expect(div.children.length, 4);
551 expect(div.nodes.length, 4);
552
553 expect(div.nodes[1].tagName, 'P');
554 expect(div.nodes[3].tagName, 'P');
555 });
556
557 observeTest('DefaultStyles', () {
558 var t = new Element.tag('template');
559 TemplateBindExtension.decorate(t);
560
561 document.body.append(t);
562 expect(t.getComputedStyle().display, 'none');
563
564 t.remove();
565 });
566
567
568 observeTest('Bind', () {
569 var div = createTestHtml('<template bind="{{}}">Hi {{ name }}</template>');
570 var model = toObservable({'name': 'Leela'});
571 recursivelySetTemplateModel(div, model);
572
573 performMicrotaskCheckpoint();
574 expect(div.nodes[1].text, 'Hi Leela');
575 });
576
577 observeTest('BindImperative', () {
578 var div = createTestHtml(
579 '<template>'
580 'Hi {{ name }}'
581 '</template>');
582 var t = div.nodes.first;
583
584 var model = toObservable({'name': 'Leela'});
585 nodeBind(t).bind('bind', model, '');
586
587 performMicrotaskCheckpoint();
588 expect(div.nodes[1].text, 'Hi Leela');
589 });
590
591 observeTest('BindPlaceHolderHasNewLine', () {
592 var div = createTestHtml(
593 '<template bind="{{}}">Hi {{\nname\n}}</template>');
594 var model = toObservable({'name': 'Leela'});
595 recursivelySetTemplateModel(div, model);
596
597 performMicrotaskCheckpoint();
598 expect(div.nodes[1].text, 'Hi Leela');
599 });
600
601 observeTest('BindWithRef', () {
602 var id = 't${new math.Random().nextDouble()}';
603 var div = createTestHtml(
604 '<template id="$id">'
605 'Hi {{ name }}'
606 '</template>'
607 '<template ref="$id" bind="{{}}"></template>');
608
609 var t1 = div.nodes.first;
610 var t2 = div.nodes[1];
611
612 expect(templateBind(t2).ref, t1);
613
614 var model = toObservable({'name': 'Fry'});
615 recursivelySetTemplateModel(div, model);
616
617 performMicrotaskCheckpoint();
618 expect(t2.nextNode.text, 'Hi Fry');
619 });
620
621 observeTest('BindChanged', () {
622 var model = toObservable({
623 'XX': {'name': 'Leela', 'title': 'Captain'},
624 'XY': {'name': 'Fry', 'title': 'Delivery boy'},
625 'XZ': {'name': 'Zoidberg', 'title': 'Doctor'}
626 });
627
628 var div = createTestHtml(
629 '<template bind="{{ XX }}">Hi {{ name }}</template>');
630
631 recursivelySetTemplateModel(div, model);
632
633 var t = div.nodes.first;
634 performMicrotaskCheckpoint();
635
636 expect(div.nodes.length, 2);
637 expect(t.nextNode.text, 'Hi Leela');
638
639 nodeBind(t).bind('bind', model, 'XZ');
640 performMicrotaskCheckpoint();
641
642 expect(div.nodes.length, 2);
643 expect(t.nextNode.text, 'Hi Zoidberg');
644 });
645
646 assertNodesAre(div, [arguments]) {
647 var expectedLength = arguments.length;
648 expect(div.nodes.length, expectedLength + 1);
649
650 for (var i = 0; i < arguments.length; i++) {
651 var targetNode = div.nodes[i + 1];
652 expect(targetNode.text, arguments[i]);
653 }
654 }
655
656 observeTest('Repeat3', () {
657 var div = createTestHtml(
658 '<template repeat="{{ contacts }}">Hi {{ name }}</template>');
659 var t = div.nodes.first;
660
661 var m = toObservable({
662 'contacts': [
663 {'name': 'Raf'},
664 {'name': 'Arv'},
665 {'name': 'Neal'}
666 ]
667 });
668
669 recursivelySetTemplateModel(div, m);
670 performMicrotaskCheckpoint();
671
672 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
673
674 m['contacts'].add(toObservable({'name': 'Alex'}));
675 performMicrotaskCheckpoint();
676 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal', 'Hi Alex']);
677
678 m['contacts'].replaceRange(0, 2,
679 toObservable([{'name': 'Rafael'}, {'name': 'Erik'}]));
680 performMicrotaskCheckpoint();
681 assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Neal', 'Hi Alex']);
682
683 m['contacts'].removeRange(1, 3);
684 performMicrotaskCheckpoint();
685 assertNodesAre(div, ['Hi Rafael', 'Hi Alex']);
686
687 m['contacts'].insertAll(1,
688 toObservable([{'name': 'Erik'}, {'name': 'Dimitri'}]));
689 performMicrotaskCheckpoint();
690 assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Dimitri', 'Hi Alex']);
691
692 m['contacts'].replaceRange(0, 1,
693 toObservable([{'name': 'Tab'}, {'name': 'Neal'}]));
694 performMicrotaskCheckpoint();
695 assertNodesAre(div, ['Hi Tab', 'Hi Neal', 'Hi Erik', 'Hi Dimitri',
696 'Hi Alex']);
697
698 m['contacts'] = toObservable([{'name': 'Alex'}]);
699 performMicrotaskCheckpoint();
700 assertNodesAre(div, ['Hi Alex']);
701
702 m['contacts'].length = 0;
703 performMicrotaskCheckpoint();
704 assertNodesAre(div, []);
705 });
706
707 observeTest('RepeatModelSet', () {
708 var div = createTestHtml(
709 '<template repeat="{{ contacts }}">'
710 'Hi {{ name }}'
711 '</template>');
712 var m = toObservable({
713 'contacts': [
714 {'name': 'Raf'},
715 {'name': 'Arv'},
716 {'name': 'Neal'}
717 ]
718 });
719 recursivelySetTemplateModel(div, m);
720
721 performMicrotaskCheckpoint();
722 var t = div.nodes.first;
723
724 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
725 });
726
727 observeTest('RepeatEmptyPath', () {
728 var div = createTestHtml(
729 '<template repeat="{{}}">Hi {{ name }}</template>');
730 var t = div.nodes.first;
731
732 var m = toObservable([
733 {'name': 'Raf'},
734 {'name': 'Arv'},
735 {'name': 'Neal'}
736 ]);
737 recursivelySetTemplateModel(div, m);
738
739 performMicrotaskCheckpoint();
740
741 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
742
743 m.add(toObservable({'name': 'Alex'}));
744 performMicrotaskCheckpoint();
745 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal', 'Hi Alex']);
746
747 m.replaceRange(0, 2, toObservable([{'name': 'Rafael'}, {'name': 'Erik'}]));
748 performMicrotaskCheckpoint();
749 assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Neal', 'Hi Alex']);
750
751 m.removeRange(1, 3);
752 performMicrotaskCheckpoint();
753 assertNodesAre(div, ['Hi Rafael', 'Hi Alex']);
754
755 m.insertAll(1, toObservable([{'name': 'Erik'}, {'name': 'Dimitri'}]));
756 performMicrotaskCheckpoint();
757 assertNodesAre(div, ['Hi Rafael', 'Hi Erik', 'Hi Dimitri', 'Hi Alex']);
758
759 m.replaceRange(0, 1, toObservable([{'name': 'Tab'}, {'name': 'Neal'}]));
760 performMicrotaskCheckpoint();
761 assertNodesAre(div, ['Hi Tab', 'Hi Neal', 'Hi Erik', 'Hi Dimitri',
762 'Hi Alex']);
763
764 m.length = 0;
765 m.add(toObservable({'name': 'Alex'}));
766 performMicrotaskCheckpoint();
767 assertNodesAre(div, ['Hi Alex']);
768 });
769
770 observeTest('RepeatNullModel', () {
771 var div = createTestHtml(
772 '<template repeat="{{}}">Hi {{ name }}</template>');
773 var t = div.nodes.first;
774
775 var m = null;
776 recursivelySetTemplateModel(div, m);
777
778 expect(div.nodes.length, 1);
779
780 t.attributes['iterate'] = '';
781 m = toObservable({});
782 recursivelySetTemplateModel(div, m);
783
784 performMicrotaskCheckpoint();
785 expect(div.nodes.length, 1);
786 });
787
788 observeTest('RepeatReuse', () {
789 var div = createTestHtml(
790 '<template repeat="{{}}">Hi {{ name }}</template>');
791 var t = div.nodes.first;
792
793 var m = toObservable([
794 {'name': 'Raf'},
795 {'name': 'Arv'},
796 {'name': 'Neal'}
797 ]);
798 recursivelySetTemplateModel(div, m);
799 performMicrotaskCheckpoint();
800
801 assertNodesAre(div, ['Hi Raf', 'Hi Arv', 'Hi Neal']);
802 var node1 = div.nodes[1];
803 var node2 = div.nodes[2];
804 var node3 = div.nodes[3];
805
806 m.replaceRange(1, 2, toObservable([{'name': 'Erik'}]));
807 performMicrotaskCheckpoint();
808 assertNodesAre(div, ['Hi Raf', 'Hi Erik', 'Hi Neal']);
809 expect(div.nodes[1], node1,
810 reason: 'model[0] did not change so the node should not have changed');
811 expect(div.nodes[2], isNot(equals(node2)),
812 reason: 'Should not reuse when replacing');
813 expect(div.nodes[3], node3,
814 reason: 'model[2] did not change so the node should not have changed');
815
816 node2 = div.nodes[2];
817 m.insert(0, toObservable({'name': 'Alex'}));
818 performMicrotaskCheckpoint();
819 assertNodesAre(div, ['Hi Alex', 'Hi Raf', 'Hi Erik', 'Hi Neal']);
820 });
821
822 observeTest('TwoLevelsDeepBug', () {
823 var div = createTestHtml(
824 '<template bind="{{}}"><span><span>{{ foo }}</span></span></template>');
825
826 var model = toObservable({'foo': 'bar'});
827 recursivelySetTemplateModel(div, model);
828 performMicrotaskCheckpoint();
829
830 expect(div.nodes[1].nodes[0].nodes[0].text, 'bar');
831 });
832
833 observeTest('Checked', () {
834 var div = createTestHtml(
835 '<template>'
836 '<input type="checkbox" checked="{{a}}">'
837 '</template>');
838 var t = div.nodes.first;
839 var m = toObservable({
840 'a': true
841 });
842 nodeBind(t).bind('bind', m, '');
843 performMicrotaskCheckpoint();
844
845 var instanceInput = t.nextNode;
846 expect(instanceInput.checked, true);
847
848 instanceInput.click();
849 expect(instanceInput.checked, false);
850
851 instanceInput.click();
852 expect(instanceInput.checked, true);
853 });
854
855 nestedHelper(s, start) {
856 var div = createTestHtml(s);
857
858 var m = toObservable({
859 'a': {
860 'b': 1,
861 'c': {'d': 2}
862 },
863 });
864
865 recursivelySetTemplateModel(div, m);
866 performMicrotaskCheckpoint();
867
868 var i = start;
869 expect(div.nodes[i++].text, '1');
870 expect(div.nodes[i++].tagName, 'TEMPLATE');
871 expect(div.nodes[i++].text, '2');
872
873 m['a']['b'] = 11;
874 performMicrotaskCheckpoint();
875 expect(div.nodes[start].text, '11');
876
877 m['a']['c'] = toObservable({'d': 22});
878 performMicrotaskCheckpoint();
879 expect(div.nodes[start + 2].text, '22');
880 }
881
882 observeTest('Nested', () {
883 nestedHelper(
884 '<template bind="{{a}}">'
885 '{{b}}'
886 '<template bind="{{c}}">'
887 '{{d}}'
888 '</template>'
889 '</template>', 1);
890 });
891
892 observeTest('NestedWithRef', () {
893 nestedHelper(
894 '<template id="inner">{{d}}</template>'
895 '<template id="outer" bind="{{a}}">'
896 '{{b}}'
897 '<template ref="inner" bind="{{c}}"></template>'
898 '</template>', 2);
899 });
900
901 nestedIterateInstantiateHelper(s, start) {
902 var div = createTestHtml(s);
903
904 var m = toObservable({
905 'a': [
906 {
907 'b': 1,
908 'c': {'d': 11}
909 },
910 {
911 'b': 2,
912 'c': {'d': 22}
913 }
914 ]
915 });
916
917 recursivelySetTemplateModel(div, m);
918 performMicrotaskCheckpoint();
919
920 var i = start;
921 expect(div.nodes[i++].text, '1');
922 expect(div.nodes[i++].tagName, 'TEMPLATE');
923 expect(div.nodes[i++].text, '11');
924 expect(div.nodes[i++].text, '2');
925 expect(div.nodes[i++].tagName, 'TEMPLATE');
926 expect(div.nodes[i++].text, '22');
927
928 m['a'][1] = toObservable({
929 'b': 3,
930 'c': {'d': 33}
931 });
932
933 performMicrotaskCheckpoint();
934 expect(div.nodes[start + 3].text, '3');
935 expect(div.nodes[start + 5].text, '33');
936 }
937
938 observeTest('NestedRepeatBind', () {
939 nestedIterateInstantiateHelper(
940 '<template repeat="{{a}}">'
941 '{{b}}'
942 '<template bind="{{c}}">'
943 '{{d}}'
944 '</template>'
945 '</template>', 1);
946 });
947
948 observeTest('NestedRepeatBindWithRef', () {
949 nestedIterateInstantiateHelper(
950 '<template id="inner">'
951 '{{d}}'
952 '</template>'
953 '<template repeat="{{a}}">'
954 '{{b}}'
955 '<template ref="inner" bind="{{c}}"></template>'
956 '</template>', 2);
957 });
958
959 nestedIterateIterateHelper(s, start) {
960 var div = createTestHtml(s);
961
962 var m = toObservable({
963 'a': [
964 {
965 'b': 1,
966 'c': [{'d': 11}, {'d': 12}]
967 },
968 {
969 'b': 2,
970 'c': [{'d': 21}, {'d': 22}]
971 }
972 ]
973 });
974
975 recursivelySetTemplateModel(div, m);
976 performMicrotaskCheckpoint();
977
978 var i = start;
979 expect(div.nodes[i++].text, '1');
980 expect(div.nodes[i++].tagName, 'TEMPLATE');
981 expect(div.nodes[i++].text, '11');
982 expect(div.nodes[i++].text, '12');
983 expect(div.nodes[i++].text, '2');
984 expect(div.nodes[i++].tagName, 'TEMPLATE');
985 expect(div.nodes[i++].text, '21');
986 expect(div.nodes[i++].text, '22');
987
988 m['a'][1] = toObservable({
989 'b': 3,
990 'c': [{'d': 31}, {'d': 32}, {'d': 33}]
991 });
992
993 i = start + 4;
994 performMicrotaskCheckpoint();
995 expect(div.nodes[start + 4].text, '3');
996 expect(div.nodes[start + 6].text, '31');
997 expect(div.nodes[start + 7].text, '32');
998 expect(div.nodes[start + 8].text, '33');
999 }
1000
1001 observeTest('NestedRepeatBind', () {
1002 nestedIterateIterateHelper(
1003 '<template repeat="{{a}}">'
1004 '{{b}}'
1005 '<template repeat="{{c}}">'
1006 '{{d}}'
1007 '</template>'
1008 '</template>', 1);
1009 });
1010
1011 observeTest('NestedRepeatRepeatWithRef', () {
1012 nestedIterateIterateHelper(
1013 '<template id="inner">'
1014 '{{d}}'
1015 '</template>'
1016 '<template repeat="{{a}}">'
1017 '{{b}}'
1018 '<template ref="inner" repeat="{{c}}"></template>'
1019 '</template>', 2);
1020 });
1021
1022 observeTest('NestedRepeatSelfRef', () {
1023 var div = createTestHtml(
1024 '<template id="t" repeat="{{}}">'
1025 '{{name}}'
1026 '<template ref="t" repeat="{{items}}"></template>'
1027 '</template>');
1028
1029 var m = toObservable([
1030 {
1031 'name': 'Item 1',
1032 'items': [
1033 {
1034 'name': 'Item 1.1',
1035 'items': [
1036 {
1037 'name': 'Item 1.1.1',
1038 'items': []
1039 }
1040 ]
1041 },
1042 {
1043 'name': 'Item 1.2'
1044 }
1045 ]
1046 },
1047 {
1048 'name': 'Item 2',
1049 'items': []
1050 },
1051 ]);
1052
1053 recursivelySetTemplateModel(div, m);
1054 performMicrotaskCheckpoint();
1055
1056 var i = 1;
1057 expect(div.nodes[i++].text, 'Item 1');
1058 expect(div.nodes[i++].tagName, 'TEMPLATE');
1059 expect(div.nodes[i++].text, 'Item 1.1');
1060 expect(div.nodes[i++].tagName, 'TEMPLATE');
1061 expect(div.nodes[i++].text, 'Item 1.1.1');
1062 expect(div.nodes[i++].tagName, 'TEMPLATE');
1063 expect(div.nodes[i++].text, 'Item 1.2');
1064 expect(div.nodes[i++].tagName, 'TEMPLATE');
1065 expect(div.nodes[i++].text, 'Item 2');
1066
1067 m[0] = toObservable({'name': 'Item 1 changed'});
1068
1069 i = 1;
1070 performMicrotaskCheckpoint();
1071 expect(div.nodes[i++].text, 'Item 1 changed');
1072 expect(div.nodes[i++].tagName, 'TEMPLATE');
1073 expect(div.nodes[i++].text, 'Item 2');
1074 });
1075
1076 observeTest('Attribute Template Option/Optgroup', () {
1077 var div = createTestHtml(
1078 '<template bind>'
1079 '<select selectedIndex="{{ selected }}">'
1080 '<optgroup template repeat="{{ groups }}" label="{{ name }}">'
1081 '<option template repeat="{{ items }}">{{ val }}</option>'
1082 '</optgroup>'
1083 '</select>'
1084 '</template>');
1085
1086 var m = toObservable({
1087 'selected': 1,
1088 'groups': [{
1089 'name': 'one', 'items': [{ 'val': 0 }, { 'val': 1 }]
1090 }],
1091 });
1092
1093 recursivelySetTemplateModel(div, m);
1094 performMicrotaskCheckpoint();
1095
1096 var select = div.nodes[0].nextNode;
1097 expect(select.nodes.length, 2);
1098
1099 scheduleMicrotask(expectAsync0(() {
1100 scheduleMicrotask(expectAsync0(() {
1101 // TODO(jmesserly): this should be called sooner.
1102 expect(select.selectedIndex, 1);
1103 }));
1104 }));
1105 expect(select.nodes[0].tagName, 'TEMPLATE');
1106 expect((templateBind(templateBind(select.nodes[0]).ref)
1107 .content.nodes[0] as Element).tagName, 'OPTGROUP');
1108
1109 var optgroup = select.nodes[1];
1110 expect(optgroup.nodes[0].tagName, 'TEMPLATE');
1111 expect(optgroup.nodes[1].tagName, 'OPTION');
1112 expect(optgroup.nodes[1].text, '0');
1113 expect(optgroup.nodes[2].tagName, 'OPTION');
1114 expect(optgroup.nodes[2].text, '1');
1115 });
1116
1117 observeTest('NestedIterateTableMixedSemanticNative', () {
1118 if (!parserHasNativeTemplate) return;
1119
1120 var div = createTestHtml(
1121 '<table><tbody>'
1122 '<template repeat="{{}}">'
1123 '<tr>'
1124 '<td template repeat="{{}}" class="{{ val }}">{{ val }}</td>'
1125 '</tr>'
1126 '</template>'
1127 '</tbody></table>');
1128
1129 var m = toObservable([
1130 [{ 'val': 0 }, { 'val': 1 }],
1131 [{ 'val': 2 }, { 'val': 3 }]
1132 ]);
1133
1134 recursivelySetTemplateModel(div, m);
1135 performMicrotaskCheckpoint();
1136
1137 var tbody = div.nodes[0].nodes[0];
1138
1139 // 1 for the <tr template>, 2 * (1 tr)
1140 expect(tbody.nodes.length, 3);
1141
1142 // 1 for the <td template>, 2 * (1 td)
1143 expect(tbody.nodes[1].nodes.length, 3);
1144
1145 expect(tbody.nodes[1].nodes[1].text, '0');
1146 expect(tbody.nodes[1].nodes[2].text, '1');
1147
1148 // 1 for the <td template>, 2 * (1 td)
1149 expect(tbody.nodes[2].nodes.length, 3);
1150 expect(tbody.nodes[2].nodes[1].text, '2');
1151 expect(tbody.nodes[2].nodes[2].text, '3');
1152
1153 // Asset the 'class' binding is retained on the semantic template (just
1154 // check the last one).
1155 expect(tbody.nodes[2].nodes[2].attributes["class"], '3');
1156 });
1157
1158 observeTest('NestedIterateTable', () {
1159 var div = createTestHtml(
1160 '<table><tbody>'
1161 '<tr template repeat="{{}}">'
1162 '<td template repeat="{{}}" class="{{ val }}">{{ val }}</td>'
1163 '</tr>'
1164 '</tbody></table>');
1165
1166 var m = toObservable([
1167 [{ 'val': 0 }, { 'val': 1 }],
1168 [{ 'val': 2 }, { 'val': 3 }]
1169 ]);
1170
1171 recursivelySetTemplateModel(div, m);
1172 performMicrotaskCheckpoint();
1173
1174 var i = 1;
1175 var tbody = div.nodes[0].nodes[0];
1176
1177 // 1 for the <tr template>, 2 * (1 tr)
1178 expect(tbody.nodes.length, 3);
1179
1180 // 1 for the <td template>, 2 * (1 td)
1181 expect(tbody.nodes[1].nodes.length, 3);
1182 expect(tbody.nodes[1].nodes[1].text, '0');
1183 expect(tbody.nodes[1].nodes[2].text, '1');
1184
1185 // 1 for the <td template>, 2 * (1 td)
1186 expect(tbody.nodes[2].nodes.length, 3);
1187 expect(tbody.nodes[2].nodes[1].text, '2');
1188 expect(tbody.nodes[2].nodes[2].text, '3');
1189
1190 // Asset the 'class' binding is retained on the semantic template (just
1191 // check the last one).
1192 expect(tbody.nodes[2].nodes[2].attributes['class'], '3');
1193 });
1194
1195 observeTest('NestedRepeatDeletionOfMultipleSubTemplates', () {
1196 var div = createTestHtml(
1197 '<ul>'
1198 '<template repeat="{{}}" id=t1>'
1199 '<li>{{name}}'
1200 '<ul>'
1201 '<template ref=t1 repaet="{{items}}"></template>'
1202 '</ul>'
1203 '</li>'
1204 '</template>'
1205 '</ul>');
1206
1207 var m = toObservable([
1208 {
1209 'name': 'Item 1',
1210 'items': [
1211 {
1212 'name': 'Item 1.1'
1213 }
1214 ]
1215 }
1216 ]);
1217
1218 recursivelySetTemplateModel(div, m);
1219
1220 performMicrotaskCheckpoint();
1221 m.removeAt(0);
1222 performMicrotaskCheckpoint();
1223 });
1224
1225 observeTest('DeepNested', () {
1226 var div = createTestHtml(
1227 '<template bind="{{a}}">'
1228 '<p>'
1229 '<template bind="{{b}}">'
1230 '{{ c }}'
1231 '</template>'
1232 '</p>'
1233 '</template>');
1234
1235 var m = toObservable({
1236 'a': {
1237 'b': {
1238 'c': 42
1239 }
1240 }
1241 });
1242 recursivelySetTemplateModel(div, m);
1243 performMicrotaskCheckpoint();
1244
1245 expect(div.nodes[1].tagName, 'P');
1246 expect(div.nodes[1].nodes.first.tagName, 'TEMPLATE');
1247 expect(div.nodes[1].nodes[1].text, '42');
1248 });
1249
1250 observeTest('TemplateContentRemoved', () {
1251 var div = createTestHtml('<template bind="{{}}">{{ }}</template>');
1252 var model = 42;
1253
1254 recursivelySetTemplateModel(div, model);
1255 performMicrotaskCheckpoint();
1256 expect(div.nodes[1].text, '42');
1257 expect(div.nodes[0].text, '');
1258 });
1259
1260 observeTest('TemplateContentRemovedEmptyArray', () {
1261 var div = createTestHtml('<template iterate>Remove me</template>');
1262 var model = toObservable([]);
1263
1264 recursivelySetTemplateModel(div, model);
1265 performMicrotaskCheckpoint();
1266 expect(div.nodes.length, 1);
1267 expect(div.nodes[0].text, '');
1268 });
1269
1270 observeTest('TemplateContentRemovedNested', () {
1271 var div = createTestHtml(
1272 '<template bind="{{}}">'
1273 '{{ a }}'
1274 '<template bind="{{}}">'
1275 '{{ b }}'
1276 '</template>'
1277 '</template>');
1278
1279 var model = toObservable({
1280 'a': 1,
1281 'b': 2
1282 });
1283 recursivelySetTemplateModel(div, model);
1284 performMicrotaskCheckpoint();
1285
1286 expect(div.nodes[0].text, '');
1287 expect(div.nodes[1].text, '1');
1288 expect(div.nodes[2].text, '');
1289 expect(div.nodes[3].text, '2');
1290 });
1291
1292 observeTest('BindWithUndefinedModel', () {
1293 var div = createTestHtml(
1294 '<template bind="{{}}" if="{{}}">{{ a }}</template>');
1295
1296 var model = toObservable({'a': 42});
1297 recursivelySetTemplateModel(div, model);
1298 performMicrotaskCheckpoint();
1299 expect(div.nodes[1].text, '42');
1300
1301 model = null;
1302 recursivelySetTemplateModel(div, model);
1303 performMicrotaskCheckpoint();
1304 expect(div.nodes.length, 1);
1305
1306 model = toObservable({'a': 42});
1307 recursivelySetTemplateModel(div, model);
1308 performMicrotaskCheckpoint();
1309 expect(div.nodes[1].text, '42');
1310 });
1311
1312 observeTest('BindNested', () {
1313 var div = createTestHtml(
1314 '<template bind="{{}}">'
1315 'Name: {{ name }}'
1316 '<template bind="{{wife}}" if="{{wife}}">'
1317 'Wife: {{ name }}'
1318 '</template>'
1319 '<template bind="{{child}}" if="{{child}}">'
1320 'Child: {{ name }}'
1321 '</template>'
1322 '</template>');
1323
1324 var m = toObservable({
1325 'name': 'Hermes',
1326 'wife': {
1327 'name': 'LaBarbara'
1328 }
1329 });
1330 recursivelySetTemplateModel(div, m);
1331 performMicrotaskCheckpoint();
1332
1333 expect(div.nodes.length, 5);
1334 expect(div.nodes[1].text, 'Name: Hermes');
1335 expect(div.nodes[3].text, 'Wife: LaBarbara');
1336
1337 m['child'] = toObservable({'name': 'Dwight'});
1338 performMicrotaskCheckpoint();
1339 expect(div.nodes.length, 6);
1340 expect(div.nodes[5].text, 'Child: Dwight');
1341
1342 m.remove('wife');
1343 performMicrotaskCheckpoint();
1344 expect(div.nodes.length, 5);
1345 expect(div.nodes[4].text, 'Child: Dwight');
1346 });
1347
1348 observeTest('BindRecursive', () {
1349 var div = createTestHtml(
1350 '<template bind="{{}}" if="{{}}" id="t">'
1351 'Name: {{ name }}'
1352 '<template bind="{{friend}}" if="{{friend}}" ref="t"></template>'
1353 '</template>');
1354
1355 var m = toObservable({
1356 'name': 'Fry',
1357 'friend': {
1358 'name': 'Bender'
1359 }
1360 });
1361 recursivelySetTemplateModel(div, m);
1362 performMicrotaskCheckpoint();
1363
1364 expect(div.nodes.length, 5);
1365 expect(div.nodes[1].text, 'Name: Fry');
1366 expect(div.nodes[3].text, 'Name: Bender');
1367
1368 m['friend']['friend'] = toObservable({'name': 'Leela'});
1369 performMicrotaskCheckpoint();
1370 expect(div.nodes.length, 7);
1371 expect(div.nodes[5].text, 'Name: Leela');
1372
1373 m['friend'] = toObservable({'name': 'Leela'});
1374 performMicrotaskCheckpoint();
1375 expect(div.nodes.length, 5);
1376 expect(div.nodes[3].text, 'Name: Leela');
1377 });
1378
1379 observeTest('Template - Self is terminator', () {
1380 var div = createTestHtml(
1381 '<template repeat>{{ foo }}'
1382 '<template bind></template>'
1383 '</template>');
1384
1385 var m = toObservable([{ 'foo': 'bar' }]);
1386 recursivelySetTemplateModel(div, m);
1387 performMicrotaskCheckpoint();
1388
1389 m.add(toObservable({ 'foo': 'baz' }));
1390 recursivelySetTemplateModel(div, m);
1391 performMicrotaskCheckpoint();
1392
1393 expect(div.nodes.length, 5);
1394 expect(div.nodes[1].text, 'bar');
1395 expect(div.nodes[3].text, 'baz');
1396 });
1397
1398 observeTest('Template - Same Contents, Different Array has no effect', () {
1399 if (!MutationObserver.supported) return;
1400
1401 var div = createTestHtml('<template repeat>{{ foo }}</template>');
1402
1403 var m = toObservable([{ 'foo': 'bar' }, { 'foo': 'bat'}]);
1404 recursivelySetTemplateModel(div, m);
1405 performMicrotaskCheckpoint();
1406
1407 var observer = new MutationObserver((records, _) {});
1408 observer.observe(div, childList: true);
1409
1410 var template = div.firstChild;
1411 nodeBind(template).bind('repeat', toObservable(m.toList()), '');
1412 performMicrotaskCheckpoint();
1413 var records = observer.takeRecords();
1414 expect(records.length, 0);
1415 });
1416
1417 observeTest('RecursiveRef', () {
1418 var div = createTestHtml(
1419 '<template bind>'
1420 '<template id=src>{{ foo }}</template>'
1421 '<template bind ref=src></template>'
1422 '</template>');
1423
1424 var m = toObservable({'foo': 'bar'});
1425 recursivelySetTemplateModel(div, m);
1426 performMicrotaskCheckpoint();
1427
1428 expect(div.nodes.length, 4);
1429 expect(div.nodes[3].text, 'bar');
1430 });
1431
1432 observeTest('ChangeFromBindToRepeat', () {
1433 var div = createTestHtml(
1434 '<template bind="{{a}}">'
1435 '{{ length }}'
1436 '</template>');
1437 var template = div.nodes.first;
1438
1439 // Note: this test data is a little different from the JS version, because
1440 // we allow binding to the "length" field of the Map in preference to
1441 // binding keys.
1442 var m = toObservable({
1443 'a': [
1444 [],
1445 { 'b': [1,2,3,4] },
1446 // Note: this will use the Map "length" property, not the "length" key.
1447 {'length': 42, 'c': 123}
1448 ]
1449 });
1450 recursivelySetTemplateModel(div, m);
1451 performMicrotaskCheckpoint();
1452
1453 expect(div.nodes.length, 2);
1454 expect(div.nodes[1].text, '3');
1455
1456 nodeBind(template)
1457 ..unbind('bind')
1458 ..bind('repeat', m, 'a');
1459 performMicrotaskCheckpoint();
1460 expect(div.nodes.length, 4);
1461 expect(div.nodes[1].text, '0');
1462 expect(div.nodes[2].text, '1');
1463 expect(div.nodes[3].text, '2');
1464
1465 nodeBind(template).unbind('repeat');
1466 nodeBind(template).bind('bind', m, 'a.1.b');
1467
1468 performMicrotaskCheckpoint();
1469 expect(div.nodes.length, 2);
1470 expect(div.nodes[1].text, '4');
1471 });
1472
1473 observeTest('ChangeRefId', () {
1474 var div = createTestHtml(
1475 '<template id="a">a:{{ }}</template>'
1476 '<template id="b">b:{{ }}</template>'
1477 '<template repeat="{{}}">'
1478 '<template ref="a" bind="{{}}"></template>'
1479 '</template>');
1480 var model = toObservable([]);
1481 recursivelySetTemplateModel(div, model);
1482 performMicrotaskCheckpoint();
1483
1484 expect(div.nodes.length, 3);
1485
1486 document.getElementById('a').id = 'old-a';
1487 document.getElementById('b').id = 'a';
1488
1489 model..add(1)..add(2);
1490 performMicrotaskCheckpoint();
1491
1492 expect(div.nodes.length, 7);
1493 expect(div.nodes[4].text, 'b:1');
1494 expect(div.nodes[6].text, 'b:2');
1495 });
1496
1497 observeTest('Content', () {
1498 var div = createTestHtml(
1499 '<template><a></a></template>'
1500 '<template><b></b></template>');
1501 var templateA = div.nodes.first;
1502 var templateB = div.nodes.last;
1503 var contentA = templateBind(templateA).content;
1504 var contentB = templateBind(templateB).content;
1505 expect(contentA, isNotNull);
1506
1507 expect(templateA.ownerDocument, isNot(equals(contentA.ownerDocument)));
1508 expect(templateB.ownerDocument, isNot(equals(contentB.ownerDocument)));
1509
1510 expect(templateB.ownerDocument, templateA.ownerDocument);
1511 expect(contentB.ownerDocument, contentA.ownerDocument);
1512
1513 expect(templateA.ownerDocument.window, window);
1514 expect(templateB.ownerDocument.window, window);
1515
1516 expect(contentA.ownerDocument.window, null);
1517 expect(contentB.ownerDocument.window, null);
1518
1519 expect(contentA.nodes.last, contentA.nodes.first);
1520 expect(contentA.nodes.first.tagName, 'A');
1521
1522 expect(contentB.nodes.last, contentB.nodes.first);
1523 expect(contentB.nodes.first.tagName, 'B');
1524 });
1525
1526 observeTest('NestedContent', () {
1527 var div = createTestHtml(
1528 '<template>'
1529 '<template></template>'
1530 '</template>');
1531 var templateA = div.nodes.first;
1532 var templateB = templateBind(templateA).content.nodes.first;
1533
1534 expect(templateB.ownerDocument, templateBind(templateA)
1535 .content.ownerDocument);
1536 expect(templateBind(templateB).content.ownerDocument,
1537 templateBind(templateA).content.ownerDocument);
1538 });
1539
1540 observeTest('BindShadowDOM', () {
1541 if (ShadowRoot.supported) {
1542 var root = createShadowTestHtml(
1543 '<template bind="{{}}">Hi {{ name }}</template>');
1544 var model = toObservable({'name': 'Leela'});
1545 recursivelySetTemplateModel(root, model);
1546 performMicrotaskCheckpoint();
1547 expect(root.nodes[1].text, 'Hi Leela');
1548 }
1549 });
1550
1551 observeTest('BindShadowDOM createInstance', () {
1552 if (ShadowRoot.supported) {
1553 var model = toObservable({'name': 'Leela'});
1554 var template = new Element.html('<template>Hi {{ name }}</template>');
1555 var root = createShadowTestHtml('');
1556 root.nodes.add(templateBind(template).createInstance(model));
1557
1558 performMicrotaskCheckpoint();
1559 expect(root.text, 'Hi Leela');
1560
1561 model['name'] = 'Fry';
1562 performMicrotaskCheckpoint();
1563 expect(root.text, 'Hi Fry');
1564 }
1565 });
1566
1567 observeTest('BindShadowDOM Template Ref', () {
1568 if (ShadowRoot.supported) {
1569 var root = createShadowTestHtml(
1570 '<template id=foo>Hi</template><template bind ref=foo></template>');
1571 recursivelySetTemplateModel(root, toObservable({}));
1572 performMicrotaskCheckpoint();
1573 expect(root.nodes.length, 3);
1574 }
1575 });
1576
1577 // https://github.com/toolkitchen/mdv/issues/8
1578 observeTest('UnbindingInNestedBind', () {
1579 var div = createTestHtml(
1580 '<template bind="{{outer}}" if="{{outer}}" syntax="testHelper">'
1581 '<template bind="{{inner}}" if="{{inner}}">'
1582 '{{ age }}'
1583 '</template>'
1584 '</template>');
1585
1586 var syntax = new UnbindingInNestedBindSyntax();
1587 var model = toObservable({
1588 'outer': {
1589 'inner': {
1590 'age': 42
1591 }
1592 }
1593 });
1594
1595 recursivelySetTemplateModel(div, model, syntax);
1596
1597 performMicrotaskCheckpoint();
1598 expect(syntax.count, 1);
1599
1600 var inner = model['outer']['inner'];
1601 model['outer'] = null;
1602
1603 performMicrotaskCheckpoint();
1604 expect(syntax.count, 1);
1605
1606 model['outer'] = toObservable({'inner': {'age': 2}});
1607 syntax.expectedAge = 2;
1608
1609 performMicrotaskCheckpoint();
1610 expect(syntax.count, 2);
1611 });
1612
1613 // https://github.com/toolkitchen/mdv/issues/8
1614 observeTest('DontCreateInstancesForAbandonedIterators', () {
1615 var div = createTestHtml(
1616 '<template bind="{{}} {{}}">'
1617 '<template bind="{{}}">Foo'
1618 '</template>'
1619 '</template>');
1620 recursivelySetTemplateModel(div, null);
1621 performMicrotaskCheckpoint();
1622 });
1623
1624 observeTest('CreateInstance', () {
1625 var div = createTestHtml(
1626 '<template bind="{{a}}">'
1627 '<template bind="{{b}}">'
1628 '{{ foo }}:{{ replaceme }}'
1629 '</template>'
1630 '</template>');
1631 var outer = templateBind(div.nodes.first);
1632 var model = toObservable({'b': {'foo': 'bar'}});
1633
1634 var host = new DivElement();
1635 var instance = outer.createInstance(model, new TestBindingSyntax());
1636 expect(outer.content.nodes.first,
1637 templateBind(instance.nodes.first).ref);
1638
1639 host.append(instance);
1640 performMicrotaskCheckpoint();
1641 expect(host.firstChild.nextNode.text, 'bar:replaced');
1642 });
1643
1644 observeTest('Bootstrap', () {
1645 var div = new DivElement();
1646 div.innerHtml =
1647 '<template>'
1648 '<div></div>'
1649 '<template>'
1650 'Hello'
1651 '</template>'
1652 '</template>';
1653
1654 TemplateBindExtension.bootstrap(div);
1655 var template = templateBind(div.nodes.first);
1656 expect(template.content.nodes.length, 2);
1657 var template2 = templateBind(template.content.nodes.first.nextNode);
1658 expect(template2.content.nodes.length, 1);
1659 expect(template2.content.nodes.first.text, 'Hello');
1660
1661 template = new Element.tag('template');
1662 template.innerHtml =
1663 '<template>'
1664 '<div></div>'
1665 '<template>'
1666 'Hello'
1667 '</template>'
1668 '</template>';
1669
1670 TemplateBindExtension.bootstrap(template);
1671 template2 = templateBind(templateBind(template).content.nodes.first);
1672 expect(template2.content.nodes.length, 2);
1673 var template3 = templateBind(template2.content.nodes.first.nextNode);
1674 expect(template3.content.nodes.length, 1);
1675 expect(template3.content.nodes.first.text, 'Hello');
1676 });
1677 }
1678
1679 class TestBindingSyntax extends BindingDelegate {
1680 getBinding(model, String path, name, node) {
1681 if (path.trim() == 'replaceme') return new ObservableBox('replaced');
1682 return null;
1683 }
1684 }
1685
1686 class UnbindingInNestedBindSyntax extends BindingDelegate {
1687 int expectedAge = 42;
1688 int count = 0;
1689
1690 getBinding(model, path, name, node) {
1691 if (name != 'text' || path != 'age')
1692 return;
1693
1694 expect(model['age'], expectedAge);
1695 count++;
1696 }
1697 }
OLDNEW
« no previous file with comments | « pkg/template_binding/test/template_binding_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698