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

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

Issue 34453003: Rename mdv -> template_binding, remove experiemntal apis from dart:html (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 2 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 custom_element_bindings_test; 5 library template_binding.test.custom_element_bindings_test;
6 6
7 import 'dart:async';
7 import 'dart:html'; 8 import 'dart:html';
8 import 'package:mdv/mdv.dart' as mdv; 9 import 'package:template_binding/template_binding.dart';
9 import 'package:observe/observe.dart' show toObservable; 10 import 'package:observe/observe.dart' show toObservable;
10 import 'package:unittest/html_config.dart'; 11 import 'package:unittest/html_config.dart';
11 import 'package:unittest/unittest.dart'; 12 import 'package:unittest/unittest.dart';
12 import 'mdv_test_utils.dart'; 13 import 'utils.dart';
14
15 Future _registered;
13 16
14 main() { 17 main() {
15 mdv.initialize();
16 useHtmlConfiguration(); 18 useHtmlConfiguration();
19
20 _registered = loadCustomElementPolyfill().then((_) {
21 document.register('my-custom-element', MyCustomElement);
22 document.register('with-attrs-custom-element', WithAttrsCustomElement);
23 });
24
17 group('Custom Element Bindings', customElementBindingsTest); 25 group('Custom Element Bindings', customElementBindingsTest);
18 } 26 }
19 27
28 Future loadCustomElementPolyfill() {
29 if (!document.supportsRegister) {
30 var script = new ScriptElement()
31 ..src = '/packages/custom_element/custom-elements.debug.js';
32 document.head.append(script);
33 return document.body.on['WebComponentsReady'].first;
Siggi Cherem (dart-lang) 2013/10/23 01:41:11 I believe the auto-wrapping of this test puts the
Jennifer Messerly 2013/10/23 02:05:02 sure. FYI it's copied from HTML tests
34 }
35 return new Future.value();
36 }
37
20 customElementBindingsTest() { 38 customElementBindingsTest() {
21 var testDiv;
22
23 setUp(() { 39 setUp(() {
24 document.body.append(testDiv = new DivElement()); 40 document.body.append(testDiv = new DivElement());
41 return _registered;
25 }); 42 });
26 43
27 tearDown(() { 44 tearDown(() {
28 testDiv.remove(); 45 testDiv.remove();
29 testDiv = null; 46 testDiv = null;
30 }); 47 });
31 48
32 createTestHtml(s) {
33 var div = new DivElement();
34 div.setInnerHtml(s, treeSanitizer: new NullTreeSanitizer());
35 testDiv.append(div);
36
37 for (var node in div.queryAll('*')) {
38 if (node.isTemplate) TemplateElement.decorate(node);
39 }
40
41 return div;
42 }
43
44
45 observeTest('override bind/unbind/unbindAll', () { 49 observeTest('override bind/unbind/unbindAll', () {
46 var element = new MyCustomElement(); 50 var element = new MyCustomElement();
47 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)}); 51 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)});
48 52
49 element.bind('my-point', model, 'a'); 53 nodeBind(element)
50 element.bind('scary-monster', model, 'b'); 54 ..bind('my-point', model, 'a')
55 ..bind('scary-monster', model, 'b');
51 56
52 expect(element.attributes, isNot(contains('my-point'))); 57 expect(element.attributes, isNot(contains('my-point')));
53 expect(element.attributes, isNot(contains('scary-monster'))); 58 expect(element.attributes, isNot(contains('scary-monster')));
54 59
55 expect(element.myPoint, model['a']); 60 expect(element.myPoint, model['a']);
56 expect(element.scaryMonster, model['b']); 61 expect(element.scaryMonster, model['b']);
57 62
58 model['a'] = null; 63 model['a'] = null;
59 performMicrotaskCheckpoint(); 64 performMicrotaskCheckpoint();
60 expect(element.myPoint, null); 65 expect(element.myPoint, null);
61 element.unbind('my-point'); 66 nodeBind(element).unbind('my-point');
62 67
63 model['a'] = new Point(1, 2); 68 model['a'] = new Point(1, 2);
64 model['b'] = new Monster(200); 69 model['b'] = new Monster(200);
65 performMicrotaskCheckpoint(); 70 performMicrotaskCheckpoint();
66 expect(element.scaryMonster, model['b']); 71 expect(element.scaryMonster, model['b']);
67 expect(element.myPoint, null, reason: 'a was unbound'); 72 expect(element.myPoint, null, reason: 'a was unbound');
68 73
69 element.unbindAll(); 74 nodeBind(element).unbindAll();
70 model['b'] = null; 75 model['b'] = null;
71 performMicrotaskCheckpoint(); 76 performMicrotaskCheckpoint();
72 expect(element.scaryMonster.health, 200); 77 expect(element.scaryMonster.health, 200);
73 }); 78 });
74 79
75 observeTest('override attribute setter', () { 80 observeTest('override attribute setter', () {
76 var element = new WithAttrsCustomElement().real; 81 var element = new WithAttrsCustomElement();
77 var model = toObservable({'a': 1, 'b': 2}); 82 var model = toObservable({'a': 1, 'b': 2});
78 element.bind('hidden?', model, 'a'); 83 nodeBind(element).bind('hidden?', model, 'a');
79 element.bind('id', model, 'b'); 84 nodeBind(element).bind('id', model, 'b');
80 85
81 expect(element.attributes, contains('hidden')); 86 expect(element.attributes, contains('hidden'));
82 expect(element.attributes['hidden'], ''); 87 expect(element.attributes['hidden'], '');
83 expect(element.id, '2'); 88 expect(element.id, '2');
84 89
85 model['a'] = null; 90 model['a'] = null;
86 performMicrotaskCheckpoint(); 91 performMicrotaskCheckpoint();
87 expect(element.attributes, isNot(contains('hidden')), 92 expect(element.attributes, isNot(contains('hidden')),
88 reason: 'null is false-y'); 93 reason: 'null is false-y');
89 94
90 model['a'] = false; 95 model['a'] = false;
91 performMicrotaskCheckpoint(); 96 performMicrotaskCheckpoint();
92 expect(element.attributes, isNot(contains('hidden'))); 97 expect(element.attributes, isNot(contains('hidden')));
93 98
94 model['a'] = 'foo'; 99 model['a'] = 'foo';
95 // TODO(jmesserly): this is here to force an ordering between the two 100 // TODO(jmesserly): this is here to force an ordering between the two
96 // changes. Otherwise the order depends on what order StreamController 101 // changes. Otherwise the order depends on what order StreamController
97 // chooses to fire the two listeners in. 102 // chooses to fire the two listeners in.
98 performMicrotaskCheckpoint(); 103 performMicrotaskCheckpoint();
99 104
100 model['b'] = 'x'; 105 model['b'] = 'x';
101 performMicrotaskCheckpoint(); 106 performMicrotaskCheckpoint();
102 expect(element.attributes, contains('hidden')); 107 expect(element.attributes, contains('hidden'));
103 expect(element.attributes['hidden'], ''); 108 expect(element.attributes['hidden'], '');
104 expect(element.id, 'x'); 109 expect(element.id, 'x');
105 110
106 expect(element.xtag.attributes.log, [ 111 expect(element.attributes.log, [
107 ['remove', 'hidden?'], 112 ['remove', 'hidden?'],
108 ['[]=', 'hidden', ''], 113 ['[]=', 'hidden', ''],
109 ['[]=', 'id', '2'], 114 ['[]=', 'id', '2'],
110 ['remove', 'hidden'], 115 ['remove', 'hidden'],
111 ['remove', 'hidden'], 116 ['remove', 'hidden'],
112 ['[]=', 'hidden', ''], 117 ['[]=', 'hidden', ''],
113 ['[]=', 'id', 'x'], 118 ['[]=', 'id', 'x'],
114 ]); 119 ]);
115 }); 120 });
116 121
117 observeTest('template bind uses overridden custom element bind', () { 122 observeTest('template bind uses overridden custom element bind', () {
118 123
119 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)}); 124 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)});
120
121 var div = createTestHtml('<template bind>' 125 var div = createTestHtml('<template bind>'
122 '<my-custom-element my-point="{{a}}" scary-monster="{{b}}">' 126 '<my-custom-element my-point="{{a}}" scary-monster="{{b}}">'
123 '</my-custom-element>' 127 '</my-custom-element>'
124 '</template>'); 128 '</template>');
125 129
126 callback(fragment) { 130 templateBind(div.query('template')).model = model;
127 for (var e in fragment.queryAll('my-custom-element')) {
128 new MyCustomElement.attach(e);
129 }
130 }
131 mdv.instanceCreated.add(callback);
132
133 div.query('template').model = model;
134 performMicrotaskCheckpoint(); 131 performMicrotaskCheckpoint();
135 132
136 var element = div.nodes[1]; 133 var element = div.nodes[1];
137 134
138 expect(element.xtag is MyCustomElement, true, 135 expect(element is MyCustomElement, true,
139 reason: '${element.xtag} should be a MyCustomElement'); 136 reason: '${element} should be a MyCustomElement');
140 137
141 expect(element.xtag.myPoint, model['a']); 138 expect(element.myPoint, model['a']);
142 expect(element.xtag.scaryMonster, model['b']); 139 expect(element.scaryMonster, model['b']);
143 140
144 expect(element.attributes, isNot(contains('my-point'))); 141 expect(element.attributes, isNot(contains('my-point')));
145 expect(element.attributes, isNot(contains('scary-monster'))); 142 expect(element.attributes, isNot(contains('scary-monster')));
146 143
147 model['a'] = null; 144 model['a'] = null;
148 performMicrotaskCheckpoint(); 145 performMicrotaskCheckpoint();
149 expect(element.xtag.myPoint, null); 146 expect(element.myPoint, null);
150 147
151 div.query('template').model = null; 148 templateBind(div.query('template')).model = null;
152 performMicrotaskCheckpoint(); 149 performMicrotaskCheckpoint();
153 150
154 expect(element.parentNode, null, reason: 'element was detached'); 151 expect(element.parentNode, null, reason: 'element was detached');
155 152
156 model['a'] = new Point(1, 2); 153 model['a'] = new Point(1, 2);
157 model['b'] = new Monster(200); 154 model['b'] = new Monster(200);
158 performMicrotaskCheckpoint(); 155 performMicrotaskCheckpoint();
159 156
160 expect(element.xtag.myPoint, null, reason: 'model was unbound'); 157 expect(element.myPoint, null, reason: 'model was unbound');
161 expect(element.xtag.scaryMonster.health, 100, reason: 'model was unbound'); 158 expect(element.scaryMonster.health, 100, reason: 'model was unbound');
162
163 mdv.instanceCreated.remove(callback);
164 }); 159 });
165 160
166 } 161 }
167 162
168 class Monster { 163 class Monster {
169 int health; 164 int health;
170 Monster(this.health); 165 Monster(this.health);
171 } 166 }
172 167
173 /** Demonstrates a custom element overriding bind/unbind/unbindAll. */ 168 /** Demonstrates a custom element overriding bind/unbind/unbindAll. */
174 class MyCustomElement implements Element { 169 class MyCustomElement extends HtmlElement implements NodeBindExtension {
175 final Element real;
176
177 Point myPoint; 170 Point myPoint;
178 Monster scaryMonster; 171 Monster scaryMonster;
179 172
180 MyCustomElement() : this.attach(new Element.tag('my-custom-element')); 173 factory MyCustomElement() => new Element.tag('my-custom-element');
181 174
182 MyCustomElement.attach(this.real) { 175 MyCustomElement.created() : super.created();
183 real.xtag = this;
184 }
185
186 get attributes => real.attributes;
187 get bindings => real.bindings;
188 176
189 NodeBinding createBinding(String name, model, String path) { 177 NodeBinding createBinding(String name, model, String path) {
178 print('!!! createBinding $name $model $path');
Siggi Cherem (dart-lang) 2013/10/23 01:41:11 delete
Jennifer Messerly 2013/10/23 02:05:02 good catch!
190 switch (name) { 179 switch (name) {
191 case 'my-point': 180 case 'my-point':
192 case 'scary-monster': 181 case 'scary-monster':
193 return new _MyCustomBinding(this, name, model, path); 182 return new _MyCustomBinding(this, name, model, path);
194 } 183 }
195 return real.createBinding(name, model, path); 184 return nodeBindFallback(this).createBinding(name, model, path);
196 } 185 }
197 186
198 bind(String name, model, String path) => real.bind(name, model, path); 187 bind(name, model, path) => nodeBindFallback(this).bind(name, model, path);
199 void unbind(String name) => real.unbind(name); 188 unbind(name) => nodeBindFallback(this).unbind(name);
200 void unbindAll() => real.unbindAll(); 189 unbindAll() => nodeBindFallback(this).unbindAll();
201 } 190 }
202 191
203 class _MyCustomBinding extends mdv.NodeBinding { 192 class _MyCustomBinding extends NodeBinding {
204 _MyCustomBinding(MyCustomElement node, property, model, path) 193 _MyCustomBinding(MyCustomElement node, property, model, path)
205 : super(node, property, model, path) { 194 : super(node, property, model, path) {
206 195
207 node.attributes.remove(property); 196 node.attributes.remove(property);
208 } 197 }
209 198
210 MyCustomElement get node => super.node; 199 MyCustomElement get node => super.node;
211 200
212 void boundValueChanged(newValue) { 201 void boundValueChanged(newValue) {
213 if (property == 'my-point') node.myPoint = newValue; 202 if (property == 'my-point') node.myPoint = newValue;
214 if (property == 'scary-monster') node.scaryMonster = newValue; 203 if (property == 'scary-monster') node.scaryMonster = newValue;
215 } 204 }
216 } 205 }
217 206
218 207
219 /** 208 /**
220 * Demonstrates a custom element can override attributes []= and remove. 209 * Demonstrates a custom element can override attributes []= and remove.
221 * and see changes that the data binding system is making to the attributes. 210 * and see changes that the data binding system is making to the attributes.
222 */ 211 */
223 class WithAttrsCustomElement implements Element { 212 class WithAttrsCustomElement extends HtmlElement {
224 final Element real; 213 AttributeMapWrapper<String, String> _attributes;
225 final AttributeMapWrapper<String, String> attributes;
226 214
227 factory WithAttrsCustomElement() { 215 factory WithAttrsCustomElement() =>
228 var real = new Element.tag('with-attrs-custom-element'); 216 new Element.tag('with-attrs-custom-element');
229 var attributes = new AttributeMapWrapper(real.attributes); 217
230 return new WithAttrsCustomElement._(real, attributes); 218 WithAttrsCustomElement.created() : super.created() {
219 _attributes = new AttributeMapWrapper(super.attributes);
231 } 220 }
232 221
233 WithAttrsCustomElement._(this.real, this.attributes) { 222 get attributes => _attributes;
234 real.xtag = this;
235 }
236
237 createBinding(String name, model, String path) =>
238 real.createBinding(name, model, path);
239 bind(String name, model, String path) => real.bind(name, model, path);
240 void unbind(String name) => real.unbind(name);
241 void unbindAll() => real.unbindAll();
242 } 223 }
243 224
244 // TODO(jmesserly): would be nice to use mocks when mirrors work on dart2js. 225 // TODO(jmesserly): would be nice to use mocks when mirrors work on dart2js.
245 class AttributeMapWrapper<K, V> implements Map<K, V> { 226 class AttributeMapWrapper<K, V> implements Map<K, V> {
246 final List log = []; 227 final List log = [];
247 Map<K, V> _map; 228 Map<K, V> _map;
248 229
249 AttributeMapWrapper(this._map); 230 AttributeMapWrapper(this._map);
250 231
251 bool containsValue(Object value) => _map.containsValue(value); 232 bool containsValue(Object value) => _map.containsValue(value);
252 bool containsKey(Object key) => _map.containsKey(key); 233 bool containsKey(Object key) => _map.containsKey(key);
253 V operator [](Object key) => _map[key]; 234 V operator [](Object key) => _map[key];
254 235
255 void operator []=(K key, V value) { 236 void operator []=(K key, V value) {
256 log.add(['[]=', key, value]); 237 log.add(['[]=', key, value]);
257 _map[key] = value; 238 _map[key] = value;
258 } 239 }
259 240
260 V putIfAbsent(K key, V ifAbsent()) => _map.putIfAbsent(key, ifAbsent); 241 V putIfAbsent(K key, V ifAbsent()) => _map.putIfAbsent(key, ifAbsent);
261 242
262 V remove(Object key) { 243 V remove(Object key) {
263 log.add(['remove', key]); 244 log.add(['remove', key]);
264 _map.remove(key); 245 _map.remove(key);
265 } 246 }
266 247
248 void addAll(Map<K, V> other) => _map.addAll(other);
267 void clear() => _map.clear(); 249 void clear() => _map.clear();
268 void forEach(void f(K key, V value)) => _map.forEach(f); 250 void forEach(void f(K key, V value)) => _map.forEach(f);
269 Iterable<K> get keys => _map.keys; 251 Iterable<K> get keys => _map.keys;
270 Iterable<V> get values => _map.values; 252 Iterable<V> get values => _map.values;
271 int get length => _map.length; 253 int get length => _map.length;
272 bool get isEmpty => _map.isEmpty; 254 bool get isEmpty => _map.isEmpty;
273 bool get isNotEmpty => _map.isNotEmpty; 255 bool get isNotEmpty => _map.isNotEmpty;
274 } 256 }
275
276 /**
277 * Sanitizer which does nothing.
278 */
279 class NullTreeSanitizer implements NodeTreeSanitizer {
280 void sanitizeTree(Node node) {}
281 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698