| OLD | NEW | 
|     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  Loading... | 
|  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  Loading... | 
|  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  Loading... | 
|  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 } | 
| OLD | NEW |