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

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

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

Powered by Google App Engine
This is Rietveld 408576698