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

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

Issue 132403010: big update to observe, template_binding, polymer (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library template_binding.test.custom_element_bindings_test; 5 library template_binding.test.custom_element_bindings_test;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:html'; 8 import 'dart:html';
9 import 'package:custom_element/polyfill.dart'; 9 import 'package:custom_element/polyfill.dart';
10 import 'package:template_binding/template_binding.dart'; 10 import 'package:template_binding/template_binding.dart';
11 import 'package:observe/observe.dart' show toObservable; 11 import 'package:observe/observe.dart';
12 import 'package:unittest/html_config.dart'; 12 import 'package:unittest/html_config.dart';
13 import 'package:unittest/unittest.dart'; 13 import 'package:unittest/unittest.dart';
14 import 'utils.dart'; 14 import 'utils.dart';
15 15
16 Future _registered; 16 Future _registered;
17 17
18 main() { 18 main() => dirtyCheckZone().run(() {
19 useHtmlConfiguration(); 19 useHtmlConfiguration();
20 20
21 _registered = loadCustomElementPolyfill().then((_) { 21 _registered = loadCustomElementPolyfill().then((_) {
22 document.register('my-custom-element', MyCustomElement); 22 document.register('my-custom-element', MyCustomElement);
23 document.register('with-attrs-custom-element', WithAttrsCustomElement); 23 document.register('with-attrs-custom-element', WithAttrsCustomElement);
24 }); 24 });
25 25
26 group('Custom Element Bindings', customElementBindingsTest); 26 group('Custom Element Bindings', customElementBindingsTest);
27 } 27 });
28 28
29 customElementBindingsTest() { 29 customElementBindingsTest() {
30 setUp(() { 30 setUp(() {
31 document.body.append(testDiv = new DivElement()); 31 document.body.append(testDiv = new DivElement());
32 return _registered; 32 return _registered;
33 }); 33 });
34 34
35 tearDown(() { 35 tearDown(() {
36 testDiv.remove(); 36 testDiv.remove();
37 testDiv = null; 37 testDiv = null;
38 }); 38 });
39 39
40 observeTest('override bind/unbind/unbindAll', () { 40 test('override bind/unbind/unbindAll', () {
41 var element = new MyCustomElement(); 41 var element = new MyCustomElement();
42 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)}); 42 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)});
43 43
44 nodeBind(element) 44 nodeBind(element)
45 ..bind('my-point', model, 'a') 45 ..bind('my-point', new PathObserver(model, 'a'))
46 ..bind('scary-monster', model, 'b'); 46 ..bind('scary-monster', new PathObserver(model, 'b'));
47 47
48 expect(element.attributes, isNot(contains('my-point'))); 48 expect(element.attributes, isNot(contains('my-point')));
49 expect(element.attributes, isNot(contains('scary-monster'))); 49 expect(element.attributes, isNot(contains('scary-monster')));
50 50
51 expect(element.myPoint, model['a']); 51 expect(element.myPoint, model['a']);
52 expect(element.scaryMonster, model['b']); 52 expect(element.scaryMonster, model['b']);
53 53
54 model['a'] = null; 54 model['a'] = null;
55 performMicrotaskCheckpoint(); 55 return new Future(() {
56 expect(element.myPoint, null); 56 expect(element.myPoint, null);
57 nodeBind(element).unbind('my-point'); 57 nodeBind(element).unbind('my-point');
58 58
59 model['a'] = new Point(1, 2); 59 model['a'] = new Point(1, 2);
60 model['b'] = new Monster(200); 60 model['b'] = new Monster(200);
61 performMicrotaskCheckpoint(); 61 }).then(endOfMicrotask).then((_) {
62 expect(element.scaryMonster, model['b']); 62 expect(element.scaryMonster, model['b']);
63 expect(element.myPoint, null, reason: 'a was unbound'); 63 expect(element.myPoint, null, reason: 'a was unbound');
64 64
65 nodeBind(element).unbindAll(); 65 nodeBind(element).unbindAll();
66 model['b'] = null; 66 model['b'] = null;
67 performMicrotaskCheckpoint(); 67 }).then(endOfMicrotask).then((_) {
68 expect(element.scaryMonster.health, 200); 68 expect(element.scaryMonster.health, 200);
69 });
69 }); 70 });
70 71
71 observeTest('override attribute setter', () { 72 test('override attribute setter', () {
72 var element = new WithAttrsCustomElement(); 73 var element = new WithAttrsCustomElement();
73 var model = toObservable({'a': 1, 'b': 2}); 74 var model = toObservable({'a': 1, 'b': 2});
74 nodeBind(element).bind('hidden?', model, 'a'); 75 nodeBind(element).bind('hidden?', new PathObserver(model, 'a'));
75 nodeBind(element).bind('id', model, 'b'); 76 nodeBind(element).bind('id', new PathObserver(model, 'b'));
76 77
77 expect(element.attributes, contains('hidden')); 78 expect(element.attributes, contains('hidden'));
78 expect(element.attributes['hidden'], ''); 79 expect(element.attributes['hidden'], '');
79 expect(element.id, '2'); 80 expect(element.id, '2');
80 81
81 model['a'] = null; 82 model['a'] = null;
82 performMicrotaskCheckpoint(); 83 return new Future(() {
83 expect(element.attributes, isNot(contains('hidden')), 84 expect(element.attributes, isNot(contains('hidden')),
84 reason: 'null is false-y'); 85 reason: 'null is false-y');
85 86
86 model['a'] = false; 87 model['a'] = false;
87 performMicrotaskCheckpoint(); 88 }).then(endOfMicrotask).then((_) {
88 expect(element.attributes, isNot(contains('hidden'))); 89 expect(element.attributes, isNot(contains('hidden')));
89 90
90 model['a'] = 'foo'; 91 model['a'] = 'foo';
91 // TODO(jmesserly): this is here to force an ordering between the two 92 // TODO(jmesserly): this is here to force an ordering between the two
92 // changes. Otherwise the order depends on what order StreamController 93 // changes. Otherwise the order depends on what order StreamController
93 // chooses to fire the two listeners in. 94 // chooses to fire the two listeners in.
94 performMicrotaskCheckpoint(); 95 }).then(endOfMicrotask).then((_) {
95 96
96 model['b'] = 'x'; 97 model['b'] = 'x';
97 performMicrotaskCheckpoint(); 98 }).then(endOfMicrotask).then((_) {
98 expect(element.attributes, contains('hidden')); 99 expect(element.attributes, contains('hidden'));
99 expect(element.attributes['hidden'], ''); 100 expect(element.attributes['hidden'], '');
100 expect(element.id, 'x'); 101 expect(element.id, 'x');
101 102
102 expect(element.attributes.log, [ 103 expect(element.attributes.log, [
103 ['remove', 'hidden?'], 104 ['remove', 'hidden?'],
104 ['[]=', 'hidden', ''], 105 ['[]=', 'hidden', ''],
105 ['[]=', 'id', '2'], 106 ['[]=', 'id', '2'],
106 ['remove', 'hidden'], 107 ['remove', 'hidden'],
107 ['remove', 'hidden'], 108 ['remove', 'hidden'],
108 ['[]=', 'hidden', ''], 109 ['[]=', 'hidden', ''],
109 ['[]=', 'id', 'x'], 110 ['[]=', 'id', 'x'],
110 ]); 111 ]);
112 });
111 }); 113 });
112 114
113 observeTest('template bind uses overridden custom element bind', () { 115 test('template bind uses overridden custom element bind', () {
114 116
115 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)}); 117 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)});
116 var div = createTestHtml('<template bind>' 118 var div = createTestHtml('<template bind>'
117 '<my-custom-element my-point="{{a}}" scary-monster="{{b}}">' 119 '<my-custom-element my-point="{{a}}" scary-monster="{{b}}">'
118 '</my-custom-element>' 120 '</my-custom-element>'
119 '</template>'); 121 '</template>');
120 122
121 templateBind(div.query('template')).model = model; 123 templateBind(div.query('template')).model = model;
122 performMicrotaskCheckpoint(); 124 var element;
125 return new Future(() {
126 print('!!! running future');
127 element = div.nodes[1];
123 128
124 var element = div.nodes[1]; 129 expect(element is MyCustomElement, true,
130 reason: '$element should be a MyCustomElement');
125 131
126 expect(element is MyCustomElement, true, 132 expect(element.myPoint, model['a']);
127 reason: '$element should be a MyCustomElement'); 133 expect(element.scaryMonster, model['b']);
128 134
129 expect(element.myPoint, model['a']); 135 expect(element.attributes, isNot(contains('my-point')));
130 expect(element.scaryMonster, model['b']); 136 expect(element.attributes, isNot(contains('scary-monster')));
131 137
132 expect(element.attributes, isNot(contains('my-point'))); 138 model['a'] = null;
133 expect(element.attributes, isNot(contains('scary-monster'))); 139 }).then(endOfMicrotask).then((_) {
140 expect(element.myPoint, null);
134 141
135 model['a'] = null; 142 templateBind(div.query('template')).model = null;
136 performMicrotaskCheckpoint(); 143 }).then(endOfMicrotask).then((_) {
137 expect(element.myPoint, null);
138 144
139 templateBind(div.query('template')).model = null; 145 expect(element.parentNode, null, reason: 'element was detached');
140 performMicrotaskCheckpoint();
141 146
142 expect(element.parentNode, null, reason: 'element was detached'); 147 model['a'] = new Point(1, 2);
148 model['b'] = new Monster(200);
149 }).then(endOfMicrotask).then((_) {
143 150
144 model['a'] = new Point(1, 2); 151 expect(element.myPoint, null, reason: 'model was unbound');
145 model['b'] = new Monster(200); 152 expect(element.scaryMonster.health, 100, reason: 'model was unbound');
146 performMicrotaskCheckpoint(); 153 });
147
148 expect(element.myPoint, null, reason: 'model was unbound');
149 expect(element.scaryMonster.health, 100, reason: 'model was unbound');
150 }); 154 });
151 155
152 } 156 }
153 157
154 class Monster { 158 class Monster {
155 int health; 159 int health;
156 Monster(this.health); 160 Monster(this.health);
157 } 161 }
158 162
159 /** Demonstrates a custom element overriding bind/unbind/unbindAll. */ 163 /** Demonstrates a custom element overriding bind/unbind/unbindAll. */
160 class MyCustomElement extends HtmlElement implements NodeBindExtension { 164 class MyCustomElement extends HtmlElement implements NodeBindExtension {
161 Point myPoint; 165 Point myPoint;
162 Monster scaryMonster; 166 Monster scaryMonster;
163 167
164 factory MyCustomElement() => new Element.tag('my-custom-element'); 168 factory MyCustomElement() => new Element.tag('my-custom-element');
165 169
166 MyCustomElement.created() : super.created(); 170 MyCustomElement.created() : super.created();
167 171
168 NodeBinding bind(String name, model, [String path]) { 172 Bindable bind(String name, value, {oneTime: false}) {
169 switch (name) { 173 switch (name) {
170 case 'my-point': 174 case 'my-point':
171 case 'scary-monster': 175 case 'scary-monster':
172 attributes.remove(name); 176 attributes.remove(name);
177 if (oneTime) {
178 _setProperty(name, value);
179 return null;
180 }
173 unbind(name); 181 unbind(name);
174 return bindings[name] = new _MyCustomBinding(this, name, model, path); 182 _setProperty(name, value.open((x) => _setProperty(name, x)));
183 return bindings[name] = value;
175 } 184 }
176 return nodeBindFallback(this).bind(name, model, path); 185 return nodeBindFallback(this).bind(name, value, oneTime: oneTime);
177 } 186 }
178 187
179 unbind(name) => nodeBindFallback(this).unbind(name); 188 unbind(name) => nodeBindFallback(this).unbind(name);
180 unbindAll() => nodeBindFallback(this).unbindAll(); 189 unbindAll() => nodeBindFallback(this).unbindAll();
181 get bindings => nodeBindFallback(this).bindings; 190 get bindings => nodeBindFallback(this).bindings;
182 get templateInstance => nodeBindFallback(this).templateInstance; 191 get templateInstance => nodeBindFallback(this).templateInstance;
183 }
184 192
185 class _MyCustomBinding extends NodeBinding { 193 void _setProperty(String property, newValue) {
186 _MyCustomBinding(MyCustomElement node, property, model, path) 194 if (property == 'my-point') myPoint = newValue;
187 : super(node, property, model, path) { 195 if (property == 'scary-monster') scaryMonster = newValue;
188
189 node.attributes.remove(property);
190 }
191
192 MyCustomElement get node => super.node;
193
194 void valueChanged(newValue) {
195 if (property == 'my-point') node.myPoint = newValue;
196 if (property == 'scary-monster') node.scaryMonster = newValue;
197 } 196 }
198 } 197 }
199 198
200 199
201 /** 200 /**
202 * Demonstrates a custom element can override attributes []= and remove. 201 * Demonstrates a custom element can override attributes []= and remove.
203 * and see changes that the data binding system is making to the attributes. 202 * and see changes that the data binding system is making to the attributes.
204 */ 203 */
205 class WithAttrsCustomElement extends HtmlElement { 204 class WithAttrsCustomElement extends HtmlElement {
206 AttributeMapWrapper _attributes; 205 AttributeMapWrapper _attributes;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 239
241 void addAll(Map<K, V> other) => _map.addAll(other); 240 void addAll(Map<K, V> other) => _map.addAll(other);
242 void clear() => _map.clear(); 241 void clear() => _map.clear();
243 void forEach(void f(K key, V value)) => _map.forEach(f); 242 void forEach(void f(K key, V value)) => _map.forEach(f);
244 Iterable<K> get keys => _map.keys; 243 Iterable<K> get keys => _map.keys;
245 Iterable<V> get values => _map.values; 244 Iterable<V> get values => _map.values;
246 int get length => _map.length; 245 int get length => _map.length;
247 bool get isEmpty => _map.isEmpty; 246 bool get isEmpty => _map.isEmpty;
248 bool get isNotEmpty => _map.isNotEmpty; 247 bool get isNotEmpty => _map.isNotEmpty;
249 } 248 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698