OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library template_binding.test.node_bindings_test; | |
6 | |
7 import 'dart:html'; | |
8 | |
9 @MirrorsUsed(targets: const [NodeBinding], override: | |
10 'template_binding.test.node_bindings_test') | |
11 import 'dart:mirrors'; | |
12 | |
13 import 'package:observe/observe.dart' show toObservable, PathObserver; | |
14 import 'package:template_binding/template_binding.dart' | |
15 show nodeBind, NodeBinding; | |
16 import 'package:unittest/html_config.dart'; | |
17 import 'package:unittest/unittest.dart'; | |
18 import 'utils.dart'; | |
19 | |
20 // Note: this file ported from | |
21 // https://github.com/toolkitchen/mdv/blob/master/tests/node_bindings.js | |
22 | |
23 main() { | |
24 useHtmlConfiguration(); | |
25 | |
26 setUp(() { | |
27 document.body.append(testDiv = new DivElement()); | |
28 }); | |
29 | |
30 tearDown(() { | |
31 testDiv.remove(); | |
32 testDiv = null; | |
33 }); | |
34 | |
35 | |
36 group('Text bindings', testBindings); | |
37 group('Element attribute bindings', elementBindings); | |
38 group('Form Element bindings', formBindings); | |
39 } | |
40 | |
41 testBindings() { | |
42 observeTest('Basic', () { | |
43 var text = new Text('hi'); | |
44 var model = toObservable({'a': 1}); | |
45 nodeBind(text).bind('text', model, 'a'); | |
46 expect(text.text, '1'); | |
47 | |
48 model['a'] = 2; | |
49 performMicrotaskCheckpoint(); | |
50 expect(text.text, '2'); | |
51 | |
52 nodeBind(text).unbind('text'); | |
53 model['a'] = 3; | |
54 performMicrotaskCheckpoint(); | |
55 expect(text.text, '2'); | |
56 | |
57 // TODO(rafaelw): Throw on binding to unavailable property? | |
58 }); | |
59 | |
60 observeTest('No Path', () { | |
61 var text = new Text('hi'); | |
62 var model = 1; | |
63 nodeBind(text).bind('text', model); | |
64 expect(text.text, '1'); | |
65 }); | |
66 | |
67 observeTest('Path unreachable', () { | |
68 var text = testDiv.append(new Text('hi')); | |
69 var model = 1; | |
70 nodeBind(text).bind('text', model, 'a'); | |
71 expect(text.text, ''); | |
72 }); | |
73 | |
74 observeTest('Observer is Model', () { | |
75 _observer(x) => reflect(x).getField( | |
76 privateSymbol(NodeBinding, '_observer')).reflectee; | |
77 | |
78 var text = new Text(''); | |
79 var model = toObservable({'a': {'b': {'c': 1}}}); | |
80 var observer = new PathObserver(model, 'a.b.c'); | |
81 nodeBind(text).bind('text', observer, 'value'); | |
82 expect(text.text, '1'); | |
83 | |
84 var binding = nodeBind(text).bindings['text']; | |
85 expect(binding, isNotNull); | |
86 expect(privateField(binding, '_observer'), observer, | |
87 reason: 'should reuse observer'); | |
88 | |
89 model['a']['b']['c'] = 2; | |
90 performMicrotaskCheckpoint(); | |
91 expect(text.text, '2'); | |
92 nodeBind(text).unbind('text'); | |
93 }); | |
94 } | |
95 | |
96 elementBindings() { | |
97 observeTest('Basic', () { | |
98 var el = new DivElement(); | |
99 var model = toObservable({'a': '1'}); | |
100 nodeBind(el).bind('foo', model, 'a'); | |
101 performMicrotaskCheckpoint(); | |
102 expect(el.attributes['foo'], '1'); | |
103 | |
104 model['a'] = '2'; | |
105 performMicrotaskCheckpoint(); | |
106 expect(el.attributes['foo'], '2'); | |
107 | |
108 model['a'] = 232.2; | |
109 performMicrotaskCheckpoint(); | |
110 expect(el.attributes['foo'], '232.2'); | |
111 | |
112 model['a'] = 232; | |
113 performMicrotaskCheckpoint(); | |
114 expect(el.attributes['foo'], '232'); | |
115 | |
116 model['a'] = null; | |
117 performMicrotaskCheckpoint(); | |
118 expect(el.attributes['foo'], ''); | |
119 }); | |
120 | |
121 observeTest('No Path', () { | |
122 var el = testDiv.append(new DivElement()); | |
123 var model = 1; | |
124 nodeBind(el).bind('foo', model); | |
125 expect(el.attributes['foo'], '1'); | |
126 }); | |
127 | |
128 observeTest('Path unreachable', () { | |
129 var el = testDiv.append(new DivElement()); | |
130 var model = toObservable({}); | |
131 nodeBind(el).bind('foo', model, 'bar'); | |
132 expect(el.attributes['foo'], ''); | |
133 }); | |
134 | |
135 observeTest('Dashes', () { | |
136 var el = testDiv.append(new DivElement()); | |
137 var model = toObservable({'a': '1'}); | |
138 nodeBind(el).bind('foo-bar', model, 'a'); | |
139 performMicrotaskCheckpoint(); | |
140 expect(el.attributes['foo-bar'], '1'); | |
141 | |
142 model['a'] = '2'; | |
143 performMicrotaskCheckpoint(); | |
144 expect(el.attributes['foo-bar'], '2'); | |
145 }); | |
146 | |
147 observeTest('Element.id, Element.hidden?', () { | |
148 var element = new DivElement(); | |
149 var model = toObservable({'a': 1, 'b': 2}); | |
150 nodeBind(element).bind('hidden?', model, 'a'); | |
151 nodeBind(element).bind('id', model, 'b'); | |
152 | |
153 expect(element.attributes, contains('hidden')); | |
154 expect(element.attributes['hidden'], ''); | |
155 expect(element.id, '2'); | |
156 | |
157 model['a'] = null; | |
158 performMicrotaskCheckpoint(); | |
159 expect(element.attributes, isNot(contains('hidden')), | |
160 reason: 'null is false-y'); | |
161 | |
162 model['a'] = false; | |
163 performMicrotaskCheckpoint(); | |
164 expect(element.attributes, isNot(contains('hidden'))); | |
165 | |
166 model['a'] = 'foo'; | |
167 model['b'] = 'x'; | |
168 performMicrotaskCheckpoint(); | |
169 expect(element.attributes, contains('hidden')); | |
170 expect(element.attributes['hidden'], ''); | |
171 expect(element.id, 'x'); | |
172 }); | |
173 | |
174 observeTest('Element.id - path unreachable', () { | |
175 var element = testDiv.append(new DivElement()); | |
176 var model = toObservable({}); | |
177 nodeBind(element).bind('id', model, 'a'); | |
178 expect(element.id, ''); | |
179 }); | |
180 } | |
181 | |
182 formBindings() { | |
183 inputTextAreaValueTest(String tagName) { | |
184 var el = new Element.tag(tagName); | |
185 testDiv.nodes.add(el); | |
186 var model = toObservable({'x': 42}); | |
187 nodeBind(el).bind('value', model, 'x'); | |
188 expect(el.value, '42'); | |
189 | |
190 model['x'] = 'Hi'; | |
191 expect(el.value, '42', reason: 'changes delivered async'); | |
192 performMicrotaskCheckpoint(); | |
193 expect(el.value, 'Hi'); | |
194 | |
195 el.value = 'changed'; | |
196 dispatchEvent('input', el); | |
197 expect(model['x'], 'changed'); | |
198 | |
199 nodeBind(el).unbind('value'); | |
200 | |
201 el.value = 'changed again'; | |
202 dispatchEvent('input', el); | |
203 expect(model['x'], 'changed'); | |
204 | |
205 nodeBind(el).bind('value', model, 'x'); | |
206 model['x'] = null; | |
207 performMicrotaskCheckpoint(); | |
208 expect(el.value, ''); | |
209 } | |
210 | |
211 inputTextAreaNoPath(String tagName) { | |
212 var el = testDiv.append(new Element.tag(tagName)); | |
213 var model = 42; | |
214 nodeBind(el).bind('value', model); | |
215 expect(el.value, '42'); | |
216 } | |
217 | |
218 inputTextAreaPathUnreachable(String tagName) { | |
219 var el = testDiv.append(new Element.tag(tagName)); | |
220 var model = toObservable({}); | |
221 nodeBind(el).bind('value', model, 'a'); | |
222 expect(el.value, ''); | |
223 } | |
224 | |
225 observeTest('Input.value', | |
226 () => inputTextAreaValueTest('input')); | |
227 observeTest('Input.value - no path', | |
228 () => inputTextAreaNoPath('input')); | |
229 observeTest('Input.value - path unreachable', | |
230 () => inputTextAreaPathUnreachable('input')); | |
231 observeTest('TextArea.value', | |
232 () => inputTextAreaValueTest('textarea')); | |
233 observeTest('TextArea.value - no path', | |
234 () => inputTextAreaNoPath('textarea')); | |
235 observeTest('TextArea.value - path unreachable', | |
236 () => inputTextAreaPathUnreachable('textarea')); | |
237 | |
238 observeTest('Radio Input', () { | |
239 var input = new InputElement(); | |
240 input.type = 'radio'; | |
241 var model = toObservable({'x': true}); | |
242 nodeBind(input).bind('checked', model, 'x'); | |
243 expect(input.checked, true); | |
244 | |
245 model['x'] = false; | |
246 expect(input.checked, true); | |
247 performMicrotaskCheckpoint(); | |
248 expect(input.checked, false,reason: 'model change should update checked'); | |
249 | |
250 input.checked = true; | |
251 dispatchEvent('change', input); | |
252 expect(model['x'], true, reason: 'input.checked should set model'); | |
253 | |
254 nodeBind(input).unbind('checked'); | |
255 | |
256 input.checked = false; | |
257 dispatchEvent('change', input); | |
258 expect(model['x'], true, | |
259 reason: 'disconnected binding should not fire'); | |
260 }); | |
261 | |
262 observeTest('Input.value - user value rejected', () { | |
263 var model = toObservable({'val': 'ping'}); | |
264 | |
265 var el = new InputElement(); | |
266 nodeBind(el).bind('value', model, 'val'); | |
267 performMicrotaskCheckpoint(); | |
268 expect(el.value, 'ping'); | |
269 | |
270 el.value = 'pong'; | |
271 dispatchEvent('input', el); | |
272 expect(model['val'], 'pong'); | |
273 | |
274 // Try a deep path. | |
275 model = toObservable({'a': {'b': {'c': 'ping'}}}); | |
276 | |
277 nodeBind(el).bind('value', model, 'a.b.c'); | |
278 performMicrotaskCheckpoint(); | |
279 expect(el.value, 'ping'); | |
280 | |
281 el.value = 'pong'; | |
282 dispatchEvent('input', el); | |
283 expect(new PathObserver(model, 'a.b.c').value, 'pong'); | |
284 | |
285 // Start with the model property being absent. | |
286 model['a']['b'].remove('c'); | |
287 performMicrotaskCheckpoint(); | |
288 expect(el.value, ''); | |
289 | |
290 el.value = 'pong'; | |
291 dispatchEvent('input', el); | |
292 expect(new PathObserver(model, 'a.b.c').value, 'pong'); | |
293 performMicrotaskCheckpoint(); | |
294 | |
295 // Model property unreachable (and unsettable). | |
296 model['a'].remove('b'); | |
297 performMicrotaskCheckpoint(); | |
298 expect(el.value, ''); | |
299 | |
300 el.value = 'pong'; | |
301 dispatchEvent('input', el); | |
302 expect(new PathObserver(model, 'a.b.c').value, null); | |
303 }); | |
304 | |
305 observeTest('(Checkbox)Input.checked', () { | |
306 var el = testDiv.append(new InputElement()); | |
307 el.type = 'checkbox'; | |
308 | |
309 var model = toObservable({'x': true}); | |
310 nodeBind(el).bind('checked', model, 'x'); | |
311 expect(el.checked, true); | |
312 | |
313 model['x'] = false; | |
314 expect(el.checked, true, reason: 'changes delivered async'); | |
315 performMicrotaskCheckpoint(); | |
316 expect(el.checked, false); | |
317 | |
318 el.click(); | |
319 expect(model['x'], true); | |
320 performMicrotaskCheckpoint(); | |
321 | |
322 el.click(); | |
323 expect(model['x'], false); | |
324 }); | |
325 | |
326 observeTest('(Checkbox)Input.checked - path unreachable', () { | |
327 var input = testDiv.append(new InputElement()); | |
328 input.type = 'checkbox'; | |
329 var model = toObservable({}); | |
330 nodeBind(input).bind('checked', model, 'x'); | |
331 expect(input.checked, false); | |
332 }); | |
333 | |
334 observeTest('(Checkbox)Input.checked 2', () { | |
335 var model = toObservable({'val': true}); | |
336 | |
337 var el = testDiv.append(new InputElement()); | |
338 el.type = 'checkbox'; | |
339 nodeBind(el).bind('checked', model, 'val'); | |
340 performMicrotaskCheckpoint(); | |
341 expect(el.checked, true); | |
342 | |
343 model['val'] = false; | |
344 performMicrotaskCheckpoint(); | |
345 expect(el.checked, false); | |
346 | |
347 el.click(); | |
348 expect(model['val'], true); | |
349 | |
350 el.click(); | |
351 expect(model['val'], false); | |
352 | |
353 el.onClick.listen((_) { | |
354 expect(model['val'], true); | |
355 }); | |
356 el.onChange.listen((_) { | |
357 expect(model['val'], true); | |
358 }); | |
359 | |
360 el.dispatchEvent(new MouseEvent('click', view: window)); | |
361 }); | |
362 | |
363 observeTest('(Checkbox)Input.checked - binding updated on click', () { | |
364 var model = toObservable({'val': true}); | |
365 | |
366 var el = new InputElement(); | |
367 testDiv.append(el); | |
368 el.type = 'checkbox'; | |
369 nodeBind(el).bind('checked', model, 'val'); | |
370 performMicrotaskCheckpoint(); | |
371 expect(el.checked, true); | |
372 | |
373 el.onClick.listen((_) { | |
374 expect(model['val'], false); | |
375 }); | |
376 | |
377 el.dispatchEvent(new MouseEvent('click', view: window)); | |
378 }); | |
379 | |
380 observeTest('(Checkbox)Input.checked - binding updated on change', () { | |
381 var model = toObservable({'val': true}); | |
382 | |
383 var el = new InputElement(); | |
384 testDiv.append(el); | |
385 el.type = 'checkbox'; | |
386 nodeBind(el).bind('checked', model, 'val'); | |
387 performMicrotaskCheckpoint(); | |
388 expect(el.checked, true); | |
389 | |
390 el.onChange.listen((_) { | |
391 expect(model['val'], false); | |
392 }); | |
393 | |
394 el.dispatchEvent(new MouseEvent('click', view: window)); | |
395 }); | |
396 | |
397 observeTest('(Radio)Input.checked', () { | |
398 var input = testDiv.append(new InputElement()); | |
399 input.type = 'radio'; | |
400 var model = toObservable({'x': true}); | |
401 nodeBind(input).bind('checked', model, 'x'); | |
402 expect(input.checked, true); | |
403 | |
404 model['x'] = false; | |
405 expect(input.checked, true); | |
406 performMicrotaskCheckpoint(); | |
407 expect(input.checked, false); | |
408 | |
409 input.checked = true; | |
410 dispatchEvent('change', input); | |
411 expect(model['x'], true); | |
412 | |
413 nodeBind(input).unbind('checked'); | |
414 | |
415 input.checked = false; | |
416 dispatchEvent('change', input); | |
417 expect(model['x'], true); | |
418 }); | |
419 | |
420 radioInputChecked2(host) { | |
421 var model = toObservable({'val1': true, 'val2': false, 'val3': false, | |
422 'val4': true}); | |
423 var RADIO_GROUP_NAME = 'test'; | |
424 | |
425 var container = host.append(new DivElement()); | |
426 | |
427 var el1 = container.append(new InputElement()); | |
428 el1.type = 'radio'; | |
429 el1.name = RADIO_GROUP_NAME; | |
430 nodeBind(el1).bind('checked', model, 'val1'); | |
431 | |
432 var el2 = container.append(new InputElement()); | |
433 el2.type = 'radio'; | |
434 el2.name = RADIO_GROUP_NAME; | |
435 nodeBind(el2).bind('checked', model, 'val2'); | |
436 | |
437 var el3 = container.append(new InputElement()); | |
438 el3.type = 'radio'; | |
439 el3.name = RADIO_GROUP_NAME; | |
440 nodeBind(el3).bind('checked', model, 'val3'); | |
441 | |
442 var el4 = container.append(new InputElement()); | |
443 el4.type = 'radio'; | |
444 el4.name = 'othergroup'; | |
445 nodeBind(el4).bind('checked', model, 'val4'); | |
446 | |
447 performMicrotaskCheckpoint(); | |
448 expect(el1.checked, true); | |
449 expect(el2.checked, false); | |
450 expect(el3.checked, false); | |
451 expect(el4.checked, true); | |
452 | |
453 model['val1'] = false; | |
454 model['val2'] = true; | |
455 performMicrotaskCheckpoint(); | |
456 expect(el1.checked, false); | |
457 expect(el2.checked, true); | |
458 expect(el3.checked, false); | |
459 expect(el4.checked, true); | |
460 | |
461 el1.checked = true; | |
462 dispatchEvent('change', el1); | |
463 expect(model['val1'], true); | |
464 expect(model['val2'], false); | |
465 expect(model['val3'], false); | |
466 expect(model['val4'], true); | |
467 | |
468 el3.checked = true; | |
469 dispatchEvent('change', el3); | |
470 expect(model['val1'], false); | |
471 expect(model['val2'], false); | |
472 expect(model['val3'], true); | |
473 expect(model['val4'], true); | |
474 } | |
475 | |
476 observeTest('(Radio)Input.checked 2', () { | |
477 radioInputChecked2(testDiv); | |
478 }); | |
479 | |
480 observeTest('(Radio)Input.checked 2 - ShadowRoot', () { | |
481 if (!ShadowRoot.supported) return; | |
482 | |
483 var div = new DivElement(); | |
484 var shadowRoot = div.createShadowRoot(); | |
485 radioInputChecked2(shadowRoot); | |
486 unbindAll(shadowRoot); | |
487 }); | |
488 | |
489 radioInputCheckedMultipleForms(host) { | |
490 var model = toObservable({'val1': true, 'val2': false, 'val3': false, | |
491 'val4': true}); | |
492 var RADIO_GROUP_NAME = 'observeTest'; | |
493 | |
494 var container = testDiv.append(new DivElement()); | |
495 var form1 = new FormElement(); | |
496 container.append(form1); | |
497 var form2 = new FormElement(); | |
498 container.append(form2); | |
499 | |
500 var el1 = new InputElement(); | |
501 form1.append(el1); | |
502 el1.type = 'radio'; | |
503 el1.name = RADIO_GROUP_NAME; | |
504 nodeBind(el1).bind('checked', model, 'val1'); | |
505 | |
506 var el2 = new InputElement(); | |
507 form1.append(el2); | |
508 el2.type = 'radio'; | |
509 el2.name = RADIO_GROUP_NAME; | |
510 nodeBind(el2).bind('checked', model, 'val2'); | |
511 | |
512 var el3 = new InputElement(); | |
513 form2.append(el3); | |
514 el3.type = 'radio'; | |
515 el3.name = RADIO_GROUP_NAME; | |
516 nodeBind(el3).bind('checked', model, 'val3'); | |
517 | |
518 var el4 = new InputElement(); | |
519 form2.append(el4); | |
520 el4.type = 'radio'; | |
521 el4.name = RADIO_GROUP_NAME; | |
522 nodeBind(el4).bind('checked', model, 'val4'); | |
523 | |
524 performMicrotaskCheckpoint(); | |
525 expect(el1.checked, true); | |
526 expect(el2.checked, false); | |
527 expect(el3.checked, false); | |
528 expect(el4.checked, true); | |
529 | |
530 el2.checked = true; | |
531 dispatchEvent('change', el2); | |
532 expect(model['val1'], false); | |
533 expect(model['val2'], true); | |
534 | |
535 // Radio buttons in form2 should be unaffected | |
536 expect(model['val3'], false); | |
537 expect(model['val4'], true); | |
538 | |
539 el3.checked = true; | |
540 dispatchEvent('change', el3); | |
541 expect(model['val3'], true); | |
542 expect(model['val4'], false); | |
543 | |
544 // Radio buttons in form1 should be unaffected | |
545 expect(model['val1'], false); | |
546 expect(model['val2'], true); | |
547 } | |
548 | |
549 observeTest('(Radio)Input.checked - multiple forms', () { | |
550 radioInputCheckedMultipleForms(testDiv); | |
551 }); | |
552 | |
553 observeTest('(Radio)Input.checked 2 - ShadowRoot', () { | |
554 if (!ShadowRoot.supported) return; | |
555 | |
556 var shadowRoot = new DivElement().createShadowRoot(); | |
557 radioInputChecked2(shadowRoot); | |
558 unbindAll(shadowRoot); | |
559 }); | |
560 | |
561 observeTest('Select.selectedIndex', () { | |
562 var select = new SelectElement(); | |
563 testDiv.append(select); | |
564 var option0 = select.append(new OptionElement()); | |
565 var option1 = select.append(new OptionElement()); | |
566 var option2 = select.append(new OptionElement()); | |
567 | |
568 var model = toObservable({'val': 2}); | |
569 | |
570 nodeBind(select).bind('selectedIndex', model, 'val'); | |
571 performMicrotaskCheckpoint(); | |
572 expect(select.selectedIndex, 2); | |
573 | |
574 select.selectedIndex = 1; | |
575 dispatchEvent('change', select); | |
576 expect(model['val'], 1); | |
577 }); | |
578 | |
579 observeTest('Select.selectedIndex - path NaN', () { | |
580 var select = new SelectElement(); | |
581 testDiv.append(select); | |
582 var option0 = select.append(new OptionElement()); | |
583 var option1 = select.append(new OptionElement()); | |
584 option1.selected = true; | |
585 var option2 = select.append(new OptionElement()); | |
586 | |
587 var model = toObservable({'val': 'foo'}); | |
588 | |
589 nodeBind(select).bind('selectedIndex', model, 'val'); | |
590 performMicrotaskCheckpoint(); | |
591 expect(select.selectedIndex, 0); | |
592 }); | |
593 | |
594 observeTest('Select.selectedIndex - path unreachable', () { | |
595 var select = new SelectElement(); | |
596 testDiv.append(select); | |
597 var option0 = select.append(new OptionElement()); | |
598 var option1 = select.append(new OptionElement()); | |
599 option1.selected = true; | |
600 var option2 = select.append(new OptionElement()); | |
601 | |
602 var model = toObservable({}); | |
603 | |
604 nodeBind(select).bind('selectedIndex', model, 'val'); | |
605 performMicrotaskCheckpoint(); | |
606 expect(select.selectedIndex, 0); | |
607 }); | |
608 | |
609 observeTest('Option.value', () { | |
610 var option = testDiv.append(new OptionElement()); | |
611 var model = toObservable({'x': 42}); | |
612 nodeBind(option).bind('value', model, 'x'); | |
613 expect(option.value, '42'); | |
614 | |
615 model['x'] = 'Hi'; | |
616 expect(option.value, '42'); | |
617 performMicrotaskCheckpoint(); | |
618 expect(option.value, 'Hi'); | |
619 }); | |
620 | |
621 observeTest('Select.value', () { | |
622 var select = testDiv.append(new SelectElement()); | |
623 testDiv.append(select); | |
624 var option0 = select.append(new OptionElement()); | |
625 var option1 = select.append(new OptionElement()); | |
626 var option2 = select.append(new OptionElement()); | |
627 | |
628 var model = toObservable({ | |
629 'opt0': 'a', | |
630 'opt1': 'b', | |
631 'opt2': 'c', | |
632 'selected': 'b' | |
633 }); | |
634 | |
635 nodeBind(option0).bind('value', model, 'opt0'); | |
636 nodeBind(option1).bind('value', model, 'opt1'); | |
637 nodeBind(option2).bind('value', model, 'opt2'); | |
638 | |
639 nodeBind(select).bind('value', model, 'selected'); | |
640 performMicrotaskCheckpoint(); | |
641 expect(select.value, 'b'); | |
642 | |
643 select.value = 'c'; | |
644 dispatchEvent('change', select); | |
645 expect(model['selected'], 'c'); | |
646 | |
647 model['opt2'] = 'X'; | |
648 performMicrotaskCheckpoint(); | |
649 expect(select.value, 'X'); | |
650 expect(model['selected'], 'X'); | |
651 | |
652 model['selected'] = 'a'; | |
653 performMicrotaskCheckpoint(); | |
654 expect(select.value, 'a'); | |
655 }); | |
656 } | |
657 | |
658 privateField(x, name) => reflect(x).getField( | |
659 privateSymbol(reflect(x).type, name)).reflectee; | |
660 | |
661 // TODO(jmesserly): fix this when it's easier to get a private symbol. | |
662 privateSymbol(TypeMirror type, String name) { | |
663 while (type != null) { | |
664 var symbol = type.variables.keys.firstWhere( | |
665 (s) => MirrorSystem.getName(s) == name, orElse: () => null); | |
666 if (symbol != null) return symbol; | |
667 type = type.superclass; | |
668 } | |
669 return null; | |
670 } | |
OLD | NEW |