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

Side by Side Diff: pkg/mdv/test/element_bindings_test.dart

Issue 19771010: implement dirty checking for @observable objects (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: logging for loops in dirty checking Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library node_bindings_test; 5 library node_bindings_test;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:html'; 8 import 'dart:html';
9 import 'package:mdv/mdv.dart' as mdv; 9 import 'package:mdv/mdv.dart' as mdv;
10 import 'package:observe/observe.dart'; 10 import 'package:observe/observe.dart';
11 import 'package:unittest/html_config.dart'; 11 import 'package:unittest/html_config.dart';
12 import 'package:unittest/unittest.dart'; 12 import 'package:unittest/unittest.dart';
13 import 'observe_utils.dart'; 13 import 'mdv_test_utils.dart';
14 14
15 // Note: this file ported from 15 // Note: this file ported from
16 // https://github.com/toolkitchen/mdv/blob/master/tests/element_bindings.js 16 // https://github.com/toolkitchen/mdv/blob/master/tests/element_bindings.js
17 17
18 main() { 18 main() {
19 mdv.initialize(); 19 mdv.initialize();
20 useHtmlConfiguration(); 20 useHtmlConfiguration();
21 group('Element Bindings', elementBindingTests); 21 group('Element Bindings', elementBindingTests);
22 } 22 }
23 23
(...skipping 10 matching lines...) Expand all
34 34
35 tearDown(() { 35 tearDown(() {
36 testDiv.remove(); 36 testDiv.remove();
37 testDiv = null; 37 testDiv = null;
38 }); 38 });
39 39
40 dispatchEvent(type, target) { 40 dispatchEvent(type, target) {
41 target.dispatchEvent(new Event(type, cancelable: false)); 41 target.dispatchEvent(new Event(type, cancelable: false));
42 } 42 }
43 43
44 test('Text', () { 44 observeTest('Text', () {
45 var template = new Element.html('<template bind>{{a}} and {{b}}'); 45 var template = new Element.html('<template bind>{{a}} and {{b}}');
46 testDiv.append(template); 46 testDiv.append(template);
47 var model = toSymbolMap({'a': 1, 'b': 2}); 47 var model = toSymbolMap({'a': 1, 'b': 2});
48 template.model = model; 48 template.model = model;
49 deliverChangeRecords(); 49 performMicrotaskCheckpoint();
50 var text = testDiv.nodes[1]; 50 var text = testDiv.nodes[1];
51 expect(text.text, '1 and 2'); 51 expect(text.text, '1 and 2');
52 52
53 model[sym('a')] = 3; 53 model[sym('a')] = 3;
54 deliverChangeRecords(); 54 performMicrotaskCheckpoint();
55 expect(text.text, '3 and 2'); 55 expect(text.text, '3 and 2');
56 }); 56 });
57 57
58 test('SimpleBinding', () { 58 observeTest('SimpleBinding', () {
59 var el = new DivElement(); 59 var el = new DivElement();
60 var model = toSymbolMap({'a': '1'}); 60 var model = toSymbolMap({'a': '1'});
61 el.bind('foo', model, 'a'); 61 el.bind('foo', model, 'a');
62 deliverChangeRecords(); 62 performMicrotaskCheckpoint();
63 expect(el.attributes['foo'], '1'); 63 expect(el.attributes['foo'], '1');
64 64
65 model[sym('a')] = '2'; 65 model[sym('a')] = '2';
66 deliverChangeRecords(); 66 performMicrotaskCheckpoint();
67 expect(el.attributes['foo'], '2'); 67 expect(el.attributes['foo'], '2');
68 68
69 model[sym('a')] = 232.2; 69 model[sym('a')] = 232.2;
70 deliverChangeRecords(); 70 performMicrotaskCheckpoint();
71 expect(el.attributes['foo'], '232.2'); 71 expect(el.attributes['foo'], '232.2');
72 72
73 model[sym('a')] = 232; 73 model[sym('a')] = 232;
74 deliverChangeRecords(); 74 performMicrotaskCheckpoint();
75 expect(el.attributes['foo'], '232'); 75 expect(el.attributes['foo'], '232');
76 76
77 model[sym('a')] = null; 77 model[sym('a')] = null;
78 deliverChangeRecords(); 78 performMicrotaskCheckpoint();
79 expect(el.attributes['foo'], ''); 79 expect(el.attributes['foo'], '');
80 }); 80 });
81 81
82 test('SimpleBindingWithDashes', () { 82 observeTest('SimpleBindingWithDashes', () {
83 var el = new DivElement(); 83 var el = new DivElement();
84 var model = toSymbolMap({'a': '1'}); 84 var model = toSymbolMap({'a': '1'});
85 el.bind('foo-bar', model, 'a'); 85 el.bind('foo-bar', model, 'a');
86 deliverChangeRecords(); 86 performMicrotaskCheckpoint();
87 expect(el.attributes['foo-bar'], '1'); 87 expect(el.attributes['foo-bar'], '1');
88 88
89 model[sym('a')] = '2'; 89 model[sym('a')] = '2';
90 deliverChangeRecords(); 90 performMicrotaskCheckpoint();
91 expect(el.attributes['foo-bar'], '2'); 91 expect(el.attributes['foo-bar'], '2');
92 }); 92 });
93 93
94 test('SimpleBindingWithComment', () { 94 observeTest('SimpleBindingWithComment', () {
95 var el = new DivElement(); 95 var el = new DivElement();
96 el.innerHtml = '<!-- Comment -->'; 96 el.innerHtml = '<!-- Comment -->';
97 var model = toSymbolMap({'a': '1'}); 97 var model = toSymbolMap({'a': '1'});
98 el.bind('foo-bar', model, 'a'); 98 el.bind('foo-bar', model, 'a');
99 deliverChangeRecords(); 99 performMicrotaskCheckpoint();
100 expect(el.attributes['foo-bar'], '1'); 100 expect(el.attributes['foo-bar'], '1');
101 101
102 model[sym('a')] = '2'; 102 model[sym('a')] = '2';
103 deliverChangeRecords(); 103 performMicrotaskCheckpoint();
104 expect(el.attributes['foo-bar'], '2'); 104 expect(el.attributes['foo-bar'], '2');
105 }); 105 });
106 106
107 test('PlaceHolderBindingText', () { 107 observeTest('PlaceHolderBindingText', () {
108 var model = toSymbolMap({ 108 var model = toSymbolMap({
109 'adj': 'cruel', 109 'adj': 'cruel',
110 'noun': 'world' 110 'noun': 'world'
111 }); 111 });
112 112
113 var el = new DivElement(); 113 var el = new DivElement();
114 el.text = 'dummy'; 114 el.text = 'dummy';
115 el.nodes.first.text = 'Hello {{ adj }} {{noun}}!'; 115 el.nodes.first.text = 'Hello {{ adj }} {{noun}}!';
116 var template = new Element.html('<template bind>'); 116 var template = new Element.html('<template bind>');
117 template.content.append(el); 117 template.content.append(el);
118 testDiv.append(template); 118 testDiv.append(template);
119 template.model = model; 119 template.model = model;
120 120
121 deliverChangeRecords(); 121 performMicrotaskCheckpoint();
122 el = testDiv.nodes[1].nodes.first; 122 el = testDiv.nodes[1].nodes.first;
123 expect(el.text, 'Hello cruel world!'); 123 expect(el.text, 'Hello cruel world!');
124 124
125 model[sym('adj')] = 'happy'; 125 model[sym('adj')] = 'happy';
126 deliverChangeRecords(); 126 performMicrotaskCheckpoint();
127 expect(el.text, 'Hello happy world!'); 127 expect(el.text, 'Hello happy world!');
128 }); 128 });
129 129
130 test('InputElementTextBinding', () { 130 observeTest('InputElementTextBinding', () {
131 var model = toSymbolMap({'val': 'ping'}); 131 var model = toSymbolMap({'val': 'ping'});
132 132
133 var el = new InputElement(); 133 var el = new InputElement();
134 el.bind('value', model, 'val'); 134 el.bind('value', model, 'val');
135 deliverChangeRecords(); 135 performMicrotaskCheckpoint();
136 expect(el.value, 'ping'); 136 expect(el.value, 'ping');
137 137
138 el.value = 'pong'; 138 el.value = 'pong';
139 dispatchEvent('input', el); 139 dispatchEvent('input', el);
140 expect(model[sym('val')], 'pong'); 140 expect(model[sym('val')], 'pong');
141 141
142 // Try a deep path. 142 // Try a deep path.
143 model = toSymbolMap({'a': {'b': {'c': 'ping'}}}); 143 model = toSymbolMap({'a': {'b': {'c': 'ping'}}});
144 144
145 el.bind('value', model, 'a.b.c'); 145 el.bind('value', model, 'a.b.c');
146 deliverChangeRecords(); 146 performMicrotaskCheckpoint();
147 expect(el.value, 'ping'); 147 expect(el.value, 'ping');
148 148
149 el.value = 'pong'; 149 el.value = 'pong';
150 dispatchEvent('input', el); 150 dispatchEvent('input', el);
151 expect(observePath(model, 'a.b.c').value, 'pong'); 151 expect(observePath(model, 'a.b.c').value, 'pong');
152 152
153 // Start with the model property being absent. 153 // Start with the model property being absent.
154 model[sym('a')][sym('b')].remove(sym('c')); 154 model[sym('a')][sym('b')].remove(sym('c'));
155 deliverChangeRecords(); 155 performMicrotaskCheckpoint();
156 expect(el.value, ''); 156 expect(el.value, '');
157 157
158 el.value = 'pong'; 158 el.value = 'pong';
159 dispatchEvent('input', el); 159 dispatchEvent('input', el);
160 expect(observePath(model, 'a.b.c').value, 'pong'); 160 expect(observePath(model, 'a.b.c').value, 'pong');
161 deliverChangeRecords(); 161 performMicrotaskCheckpoint();
162 162
163 // Model property unreachable (and unsettable). 163 // Model property unreachable (and unsettable).
164 model[sym('a')].remove(sym('b')); 164 model[sym('a')].remove(sym('b'));
165 deliverChangeRecords(); 165 performMicrotaskCheckpoint();
166 expect(el.value, ''); 166 expect(el.value, '');
167 167
168 el.value = 'pong'; 168 el.value = 'pong';
169 dispatchEvent('input', el); 169 dispatchEvent('input', el);
170 expect(observePath(model, 'a.b.c').value, null); 170 expect(observePath(model, 'a.b.c').value, null);
171 }); 171 });
172 172
173 test('InputElementCheckbox', () { 173 observeTest('InputElementCheckbox', () {
174 var model = toSymbolMap({'val': true}); 174 var model = toSymbolMap({'val': true});
175 175
176 var el = new InputElement(); 176 var el = new InputElement();
177 testDiv.append(el); 177 testDiv.append(el);
178 el.type = 'checkbox'; 178 el.type = 'checkbox';
179 el.bind('checked', model, 'val'); 179 el.bind('checked', model, 'val');
180 deliverChangeRecords(); 180 performMicrotaskCheckpoint();
181 expect(el.checked, true); 181 expect(el.checked, true);
182 182
183 model[sym('val')] = false; 183 model[sym('val')] = false;
184 deliverChangeRecords(); 184 performMicrotaskCheckpoint();
185 expect(el.checked, false); 185 expect(el.checked, false);
186 186
187 el.click(); 187 el.click();
188 expect(model[sym('val')], true); 188 expect(model[sym('val')], true);
189 189
190 el.click(); 190 el.click();
191 expect(model[sym('val')], false); 191 expect(model[sym('val')], false);
192 192
193 el.onClick.listen((_) { 193 el.onClick.listen((_) {
194 expect(model[sym('val')], true); 194 expect(model[sym('val')], true);
195 }); 195 });
196 el.onChange.listen((_) { 196 el.onChange.listen((_) {
197 expect(model[sym('val')], true); 197 expect(model[sym('val')], true);
198 }); 198 });
199 199
200 el.dispatchEvent(new MouseEvent('click', view: window)); 200 el.dispatchEvent(new MouseEvent('click', view: window));
201 }); 201 });
202 202
203 test('InputElementCheckbox - binding updated on click', () { 203 observeTest('InputElementCheckbox - binding updated on click', () {
204 var model = toSymbolMap({'val': true}); 204 var model = toSymbolMap({'val': true});
205 205
206 var el = new InputElement(); 206 var el = new InputElement();
207 testDiv.append(el); 207 testDiv.append(el);
208 el.type = 'checkbox'; 208 el.type = 'checkbox';
209 el.bind('checked', model, 'val'); 209 el.bind('checked', model, 'val');
210 deliverChangeRecords(); 210 performMicrotaskCheckpoint();
211 expect(el.checked, true); 211 expect(el.checked, true);
212 212
213 el.onClick.listen((_) { 213 el.onClick.listen((_) {
214 expect(model[sym('val')], false); 214 expect(model[sym('val')], false);
215 }); 215 });
216 216
217 el.dispatchEvent(new MouseEvent('click', view: window)); 217 el.dispatchEvent(new MouseEvent('click', view: window));
218 }); 218 });
219 219
220 test('InputElementCheckbox - binding updated on change', () { 220 observeTest('InputElementCheckbox - binding updated on change', () {
221 var model = toSymbolMap({'val': true}); 221 var model = toSymbolMap({'val': true});
222 222
223 var el = new InputElement(); 223 var el = new InputElement();
224 testDiv.append(el); 224 testDiv.append(el);
225 el.type = 'checkbox'; 225 el.type = 'checkbox';
226 el.bind('checked', model, 'val'); 226 el.bind('checked', model, 'val');
227 deliverChangeRecords(); 227 performMicrotaskCheckpoint();
228 expect(el.checked, true); 228 expect(el.checked, true);
229 229
230 el.onChange.listen((_) { 230 el.onChange.listen((_) {
231 expect(model[sym('val')], false); 231 expect(model[sym('val')], false);
232 }); 232 });
233 233
234 el.dispatchEvent(new MouseEvent('click', view: window)); 234 el.dispatchEvent(new MouseEvent('click', view: window));
235 }); 235 });
236 236
237 test('InputElementRadio', () { 237 observeTest('InputElementRadio', () {
238 var model = toSymbolMap({'val1': true, 'val2': false, 'val3': false, 238 var model = toSymbolMap({'val1': true, 'val2': false, 'val3': false,
239 'val4': true}); 239 'val4': true});
240 var RADIO_GROUP_NAME = 'test'; 240 var RADIO_GROUP_NAME = 'observeTest';
241 241
242 var container = testDiv; 242 var container = testDiv;
243 243
244 var el1 = new InputElement(); 244 var el1 = new InputElement();
245 testDiv.append(el1); 245 testDiv.append(el1);
246 el1.type = 'radio'; 246 el1.type = 'radio';
247 el1.name = RADIO_GROUP_NAME; 247 el1.name = RADIO_GROUP_NAME;
248 el1.bind('checked', model, 'val1'); 248 el1.bind('checked', model, 'val1');
249 249
250 var el2 = new InputElement(); 250 var el2 = new InputElement();
251 testDiv.append(el2); 251 testDiv.append(el2);
252 el2.type = 'radio'; 252 el2.type = 'radio';
253 el2.name = RADIO_GROUP_NAME; 253 el2.name = RADIO_GROUP_NAME;
254 el2.bind('checked', model, 'val2'); 254 el2.bind('checked', model, 'val2');
255 255
256 var el3 = new InputElement(); 256 var el3 = new InputElement();
257 testDiv.append(el3); 257 testDiv.append(el3);
258 el3.type = 'radio'; 258 el3.type = 'radio';
259 el3.name = RADIO_GROUP_NAME; 259 el3.name = RADIO_GROUP_NAME;
260 el3.bind('checked', model, 'val3'); 260 el3.bind('checked', model, 'val3');
261 261
262 var el4 = new InputElement(); 262 var el4 = new InputElement();
263 testDiv.append(el4); 263 testDiv.append(el4);
264 el4.type = 'radio'; 264 el4.type = 'radio';
265 el4.name = 'othergroup'; 265 el4.name = 'othergroup';
266 el4.bind('checked', model, 'val4'); 266 el4.bind('checked', model, 'val4');
267 267
268 deliverChangeRecords(); 268 performMicrotaskCheckpoint();
269 expect(el1.checked, true); 269 expect(el1.checked, true);
270 expect(el2.checked, false); 270 expect(el2.checked, false);
271 expect(el3.checked, false); 271 expect(el3.checked, false);
272 expect(el4.checked, true); 272 expect(el4.checked, true);
273 273
274 model[sym('val1')] = false; 274 model[sym('val1')] = false;
275 model[sym('val2')] = true; 275 model[sym('val2')] = true;
276 deliverChangeRecords(); 276 performMicrotaskCheckpoint();
277 expect(el1.checked, false); 277 expect(el1.checked, false);
278 expect(el2.checked, true); 278 expect(el2.checked, true);
279 expect(el3.checked, false); 279 expect(el3.checked, false);
280 expect(el4.checked, true); 280 expect(el4.checked, true);
281 281
282 el1.checked = true; 282 el1.checked = true;
283 dispatchEvent('change', el1); 283 dispatchEvent('change', el1);
284 expect(model[sym('val1')], true); 284 expect(model[sym('val1')], true);
285 expect(model[sym('val2')], false); 285 expect(model[sym('val2')], false);
286 expect(model[sym('val3')], false); 286 expect(model[sym('val3')], false);
287 expect(model[sym('val4')], true); 287 expect(model[sym('val4')], true);
288 288
289 el3.checked = true; 289 el3.checked = true;
290 dispatchEvent('change', el3); 290 dispatchEvent('change', el3);
291 expect(model[sym('val1')], false); 291 expect(model[sym('val1')], false);
292 expect(model[sym('val2')], false); 292 expect(model[sym('val2')], false);
293 expect(model[sym('val3')], true); 293 expect(model[sym('val3')], true);
294 expect(model[sym('val4')], true); 294 expect(model[sym('val4')], true);
295 }); 295 });
296 296
297 test('InputElementRadioMultipleForms', () { 297 observeTest('InputElementRadioMultipleForms', () {
298 var model = toSymbolMap({'val1': true, 'val2': false, 'val3': false, 298 var model = toSymbolMap({'val1': true, 'val2': false, 'val3': false,
299 'val4': true}); 299 'val4': true});
300 var RADIO_GROUP_NAME = 'test'; 300 var RADIO_GROUP_NAME = 'observeTest';
301 301
302 var form1 = new FormElement(); 302 var form1 = new FormElement();
303 testDiv.append(form1); 303 testDiv.append(form1);
304 var form2 = new FormElement(); 304 var form2 = new FormElement();
305 testDiv.append(form2); 305 testDiv.append(form2);
306 306
307 var el1 = new InputElement(); 307 var el1 = new InputElement();
308 form1.append(el1); 308 form1.append(el1);
309 el1.type = 'radio'; 309 el1.type = 'radio';
310 el1.name = RADIO_GROUP_NAME; 310 el1.name = RADIO_GROUP_NAME;
(...skipping 10 matching lines...) Expand all
321 el3.type = 'radio'; 321 el3.type = 'radio';
322 el3.name = RADIO_GROUP_NAME; 322 el3.name = RADIO_GROUP_NAME;
323 el3.bind('checked', model, 'val3'); 323 el3.bind('checked', model, 'val3');
324 324
325 var el4 = new InputElement(); 325 var el4 = new InputElement();
326 form2.append(el4); 326 form2.append(el4);
327 el4.type = 'radio'; 327 el4.type = 'radio';
328 el4.name = RADIO_GROUP_NAME; 328 el4.name = RADIO_GROUP_NAME;
329 el4.bind('checked', model, 'val4'); 329 el4.bind('checked', model, 'val4');
330 330
331 deliverChangeRecords(); 331 performMicrotaskCheckpoint();
332 expect(el1.checked, true); 332 expect(el1.checked, true);
333 expect(el2.checked, false); 333 expect(el2.checked, false);
334 expect(el3.checked, false); 334 expect(el3.checked, false);
335 expect(el4.checked, true); 335 expect(el4.checked, true);
336 336
337 el2.checked = true; 337 el2.checked = true;
338 dispatchEvent('change', el2); 338 dispatchEvent('change', el2);
339 expect(model[sym('val1')], false); 339 expect(model[sym('val1')], false);
340 expect(model[sym('val2')], true); 340 expect(model[sym('val2')], true);
341 341
342 // Radio buttons in form2 should be unaffected 342 // Radio buttons in form2 should be unaffected
343 expect(model[sym('val3')], false); 343 expect(model[sym('val3')], false);
344 expect(model[sym('val4')], true); 344 expect(model[sym('val4')], true);
345 345
346 el3.checked = true; 346 el3.checked = true;
347 dispatchEvent('change', el3); 347 dispatchEvent('change', el3);
348 expect(model[sym('val3')], true); 348 expect(model[sym('val3')], true);
349 expect(model[sym('val4')], false); 349 expect(model[sym('val4')], false);
350 350
351 // Radio buttons in form1 should be unaffected 351 // Radio buttons in form1 should be unaffected
352 expect(model[sym('val1')], false); 352 expect(model[sym('val1')], false);
353 expect(model[sym('val2')], true); 353 expect(model[sym('val2')], true);
354 }); 354 });
355 355
356 test('BindToChecked', () { 356 observeTest('BindToChecked', () {
357 var div = new DivElement(); 357 var div = new DivElement();
358 testDiv.append(div); 358 testDiv.append(div);
359 var child = new DivElement(); 359 var child = new DivElement();
360 div.append(child); 360 div.append(child);
361 var input = new InputElement(); 361 var input = new InputElement();
362 child.append(input); 362 child.append(input);
363 input.type = 'checkbox'; 363 input.type = 'checkbox';
364 364
365 var model = toSymbolMap({'a': {'b': false}}); 365 var model = toSymbolMap({'a': {'b': false}});
366 input.bind('checked', model, 'a.b'); 366 input.bind('checked', model, 'a.b');
367 367
368 input.click(); 368 input.click();
369 expect(model[sym('a')][sym('b')], true); 369 expect(model[sym('a')][sym('b')], true);
370 370
371 input.click(); 371 input.click();
372 expect(model[sym('a')][sym('b')], false); 372 expect(model[sym('a')][sym('b')], false);
373 }); 373 });
374 374
375 test('Select selectedIndex', () { 375 observeTest('Select selectedIndex', () {
376 var select = new SelectElement(); 376 var select = new SelectElement();
377 testDiv.append(select); 377 testDiv.append(select);
378 var option0 = select.append(new OptionElement()); 378 var option0 = select.append(new OptionElement());
379 var option1 = select.append(new OptionElement()); 379 var option1 = select.append(new OptionElement());
380 var option2 = select.append(new OptionElement()); 380 var option2 = select.append(new OptionElement());
381 381
382 var model = toSymbolMap({'val': 2}); 382 var model = toSymbolMap({'val': 2});
383 383
384 select.bind('selectedIndex', model, 'val'); 384 select.bind('selectedIndex', model, 'val');
385 deliverChangeRecords(); 385 performMicrotaskCheckpoint();
386 expect(select.selectedIndex, 2); 386 expect(select.selectedIndex, 2);
387 387
388 select.selectedIndex = 1; 388 select.selectedIndex = 1;
389 dispatchEvent('change', select); 389 dispatchEvent('change', select);
390 expect(model[sym('val')], 1); 390 expect(model[sym('val')], 1);
391 }); 391 });
392 392
393 test('MultipleReferences', () { 393 observeTest('MultipleReferences', () {
394 var el = new DivElement(); 394 var el = new DivElement();
395 var template = new Element.html('<template bind>'); 395 var template = new Element.html('<template bind>');
396 template.content.append(el); 396 template.content.append(el);
397 testDiv.append(template); 397 testDiv.append(template);
398 398
399 var model = toSymbolMap({'foo': 'bar'}); 399 var model = toSymbolMap({'foo': 'bar'});
400 el.attributes['foo'] = '{{foo}} {{foo}}'; 400 el.attributes['foo'] = '{{foo}} {{foo}}';
401 template.model = model; 401 template.model = model;
402 402
403 deliverChangeRecords(); 403 performMicrotaskCheckpoint();
404 el = testDiv.nodes[1]; 404 el = testDiv.nodes[1];
405 expect(el.attributes['foo'], 'bar bar'); 405 expect(el.attributes['foo'], 'bar bar');
406 }); 406 });
407 } 407 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698