Chromium Code Reviews| 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.binding_syntax; | 5 library template_binding.test.binding_syntax; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 import 'dart:html'; | 9 import 'dart:html'; |
| 10 import 'package:template_binding/template_binding.dart'; | 10 import 'package:template_binding/template_binding.dart'; |
| 11 import 'package:observe/observe.dart'; | 11 import 'package:observe/observe.dart'; |
| 12 import 'package:unittest/unittest.dart'; | 12 import 'package:unittest/unittest.dart'; |
| 13 import 'utils.dart'; | 13 import 'utils.dart'; |
| 14 | 14 |
| 15 // Note: this test is executed by template_element_test.dart | 15 // Note: this test is executed by template_element_test.dart |
| 16 | 16 |
| 17 syntaxTests(FooBarModel fooModel([foo, bar])) { | 17 syntaxTests(FooBarModel fooModel([foo, bar])) { |
| 18 test('prepareBinding', () { | 18 test('prepareBinding', () { |
| 19 var model = fooModel('bar'); | 19 var model = fooModel('bar'); |
| 20 var testSyntax = new TestBindingSyntax(); | 20 var testSyntax = new TestBindingSyntax(); |
| 21 var div = createTestHtml( | 21 var div = createTestHtml( |
| 22 '<template bind>{{ foo }}' | 22 '<template bind>{{ foo }}' |
| 23 '<template bind>{{ foo }}</template>' | 23 '<template bind>{{ foo }}</template>' |
| 24 '</template>'); | 24 '</template>'); |
| 25 recursivelySetTemplateModel(div, model, testSyntax); | 25 var template = templateBind(div.firstChild); |
| 26 template..model = model..bindingDelegate = testSyntax; | |
|
Siggi Cherem (dart-lang)
2014/06/03 17:41:03
woah - I have no idea how to parse this :-). Consi
Jennifer Messerly
2014/06/04 04:42:16
added newlines. is it more clear?
Siggi Cherem (dart-lang)
2014/06/04 16:39:10
definitely, the precedence rules of .. have always
Jennifer Messerly
2014/06/04 17:52:16
:). It must've been a search replace. That was ...
| |
| 26 return new Future(() { | 27 return new Future(() { |
| 27 expect(div.nodes.length, 4); | 28 expect(div.nodes.length, 4); |
| 28 expect(div.nodes.last.text, 'bar'); | 29 expect(div.nodes.last.text, 'bar'); |
| 29 expect(div.nodes[2].tagName, 'TEMPLATE'); | 30 expect(div.nodes[2].tagName, 'TEMPLATE'); |
| 30 expect(testSyntax.log, [ | 31 expect(testSyntax.log, [ |
| 31 ['prepare', '', 'bind', 'TEMPLATE'], | 32 ['prepare', '', 'bind', 'TEMPLATE'], |
| 32 ['bindFn', model, 'TEMPLATE', 0], | 33 ['bindFn', model, 'TEMPLATE', 0], |
| 33 ['prepare', 'foo', 'text', 'TEXT'], | 34 ['prepare', 'foo', 'text', 'TEXT'], |
| 34 ['prepare', '', 'bind', 'TEMPLATE'], | 35 ['prepare', '', 'bind', 'TEMPLATE'], |
| 35 ['bindFn', model, 'TEXT', 2], | 36 ['bindFn', model, 'TEXT', 2], |
| 36 ['bindFn', model, 'TEMPLATE', 3], | 37 ['bindFn', model, 'TEMPLATE', 3], |
| 37 ['prepare', 'foo', 'text', 'TEXT'], | 38 ['prepare', 'foo', 'text', 'TEXT'], |
| 38 ['bindFn', model, 'TEXT', 6], | 39 ['bindFn', model, 'TEXT', 6], |
| 39 ]); | 40 ]); |
| 40 }); | 41 }); |
| 41 }); | 42 }); |
| 42 | 43 |
| 43 test('prepareInstanceModel', () { | 44 test('prepareInstanceModel', () { |
| 44 var model = toObservable([fooModel(1), fooModel(2), fooModel(3)]); | 45 var model = toObservable([fooModel(1), fooModel(2), fooModel(3)]); |
| 45 | 46 |
| 46 var testSyntax = new TestModelSyntax(); | 47 var testSyntax = new TestModelSyntax(); |
| 47 testSyntax.altModels.addAll([fooModel('a'), fooModel('b'), fooModel('c')]); | 48 testSyntax.altModels.addAll([fooModel('a'), fooModel('b'), fooModel('c')]); |
| 48 | 49 |
| 49 var div = createTestHtml('<template repeat>{{ foo }}</template>'); | 50 var div = createTestHtml('<template repeat>{{ foo }}</template>'); |
| 50 | 51 |
| 51 var template = div.nodes[0]; | 52 var template = div.nodes[0]; |
| 52 recursivelySetTemplateModel(div, model, testSyntax); | 53 templateBind(template)..model = model..bindingDelegate = testSyntax; |
| 53 return new Future(() { | 54 return new Future(() { |
| 54 | 55 |
| 55 expect(div.nodes.length, 4); | 56 expect(div.nodes.length, 4); |
| 56 expect(div.nodes[0].tagName, 'TEMPLATE'); | 57 expect(div.nodes[0].tagName, 'TEMPLATE'); |
| 57 expect(div.nodes[1].text, 'a'); | 58 expect(div.nodes[1].text, 'a'); |
| 58 expect(div.nodes[2].text, 'b'); | 59 expect(div.nodes[2].text, 'b'); |
| 59 expect(div.nodes[3].text, 'c'); | 60 expect(div.nodes[3].text, 'c'); |
| 60 | 61 |
| 61 expect(testSyntax.log, [ | 62 expect(testSyntax.log, [ |
| 62 ['prepare', template], | 63 ['prepare', template], |
| 63 ['bindFn', model[0]], | 64 ['bindFn', model[0]], |
| 64 ['bindFn', model[1]], | 65 ['bindFn', model[1]], |
| 65 ['bindFn', model[2]], | 66 ['bindFn', model[2]], |
| 66 ]); | 67 ]); |
| 67 }); | 68 }); |
| 68 }); | 69 }); |
| 69 | 70 |
| 70 test('prepareInstanceModel - reorder instances', () { | 71 test('prepareInstanceModel - reorder instances', () { |
| 71 var model = toObservable([0, 1, 2]); | 72 var model = toObservable([0, 1, 2]); |
| 72 | 73 |
| 73 var div = createTestHtml('<template repeat>{{}}</template>'); | 74 var div = createTestHtml('<template repeat>{{}}</template>'); |
| 74 var template = div.firstChild; | 75 var template = div.firstChild; |
| 75 var delegate = new TestInstanceModelSyntax(); | 76 var delegate = new TestInstanceModelSyntax(); |
| 76 | 77 |
| 77 recursivelySetTemplateModel(div, model, delegate); | 78 templateBind(template)..model = model..bindingDelegate = delegate; |
| 78 return new Future(() { | 79 return new Future(() { |
| 79 expect(delegate.prepareCount, 1); | 80 expect(delegate.prepareCount, 1); |
| 80 expect(delegate.callCount, 3); | 81 expect(delegate.callCount, 3); |
| 81 | 82 |
| 82 // Note: intentionally mutate in place. | 83 // Note: intentionally mutate in place. |
| 83 model.replaceRange(0, model.length, model.reversed.toList()); | 84 model.replaceRange(0, model.length, model.reversed.toList()); |
| 84 }).then(endOfMicrotask).then((_) { | 85 }).then(endOfMicrotask).then((_) { |
| 85 expect(delegate.prepareCount, 1); | 86 expect(delegate.prepareCount, 1); |
| 86 expect(delegate.callCount, 3); | 87 expect(delegate.callCount, 3); |
| 87 }); | 88 }); |
| 88 }); | 89 }); |
| 89 | 90 |
| 90 test('prepareInstancePositionChanged', () { | 91 test('prepareInstancePositionChanged', () { |
| 91 var model = toObservable(['a', 'b', 'c']); | 92 var model = toObservable(['a', 'b', 'c']); |
| 92 | 93 |
| 93 var div = createTestHtml('<template repeat>{{}}</template>'); | 94 var div = createTestHtml('<template repeat>{{}}</template>'); |
| 94 var delegate = new TestPositionChangedSyntax(); | 95 var delegate = new TestPositionChangedSyntax(); |
| 95 | 96 |
| 96 var template = div.nodes[0]; | 97 var template = div.nodes[0]; |
| 97 recursivelySetTemplateModel(div, model, delegate); | 98 templateBind(template)..model = model..bindingDelegate = delegate; |
| 98 return new Future(() { | 99 return new Future(() { |
| 99 | 100 |
| 100 expect(div.nodes.length, 4); | 101 expect(div.nodes.length, 4); |
| 101 expect(div.nodes[0].tagName, 'TEMPLATE'); | 102 expect(div.nodes[0].tagName, 'TEMPLATE'); |
| 102 expect(div.nodes[1].text, 'a'); | 103 expect(div.nodes[1].text, 'a'); |
| 103 expect(div.nodes[2].text, 'b'); | 104 expect(div.nodes[2].text, 'b'); |
| 104 expect(div.nodes[3].text, 'c'); | 105 expect(div.nodes[3].text, 'c'); |
| 105 | 106 |
| 106 expect(delegate.log, [ | 107 expect(delegate.log, [ |
| 107 ['prepare', template], | 108 ['prepare', template], |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 129 '<template repeat>{{ \$index }} - {{ \$ident }}</template>'); | 130 '<template repeat>{{ \$index }} - {{ \$ident }}</template>'); |
| 130 var template = templateBind(div.firstChild) | 131 var template = templateBind(div.firstChild) |
| 131 ..bindingDelegate = new UpdateBindingDelegateA() | 132 ..bindingDelegate = new UpdateBindingDelegateA() |
| 132 ..model = model; | 133 ..model = model; |
| 133 | 134 |
| 134 return new Future(() { | 135 return new Future(() { |
| 135 expect(div.nodes.length, 3); | 136 expect(div.nodes.length, 3); |
| 136 expect(div.nodes[1].text, 'i:0 - a:1'); | 137 expect(div.nodes[1].text, 'i:0 - a:1'); |
| 137 expect(div.nodes[2].text, 'i:1 - a:2'); | 138 expect(div.nodes[2].text, 'i:1 - a:2'); |
| 138 | 139 |
| 139 template.bindingDelegate = new UpdateBindingDelegateB(); | 140 expect(() { |
| 141 template.bindingDelegate = new UpdateBindingDelegateB(); | |
| 142 }, throws); | |
| 143 | |
| 144 template.clear(); | |
| 145 expect(div.nodes.length, 1); | |
| 146 | |
| 147 template | |
| 148 ..bindingDelegate = new UpdateBindingDelegateB() | |
| 149 ..model = model; | |
| 150 | |
| 140 model.add(3); | 151 model.add(3); |
| 141 }).then(nextMicrotask).then((_) { | 152 }).then(nextMicrotask).then((_) { |
| 153 // All instances should reflect delegateB | |
| 142 expect(4, div.nodes.length); | 154 expect(4, div.nodes.length); |
| 143 expect(div.nodes[1].text, 'i:0 - a:1'); | 155 expect(div.nodes[1].text, 'I:0 - A:1-narg'); |
| 144 expect(div.nodes[2].text, 'i:1 - a:2'); | 156 expect(div.nodes[2].text, 'I:2 - A:2-narg'); |
| 145 expect(div.nodes[3].text, 'I:4 - A:3-narg'); | 157 expect(div.nodes[3].text, 'I:4 - A:3-narg'); |
| 146 }); | 158 }); |
| 147 }); | 159 }); |
| 148 | 160 |
| 149 test('Basic', () { | 161 test('Basic', () { |
| 150 var model = fooModel(2, 4); | 162 var model = fooModel(2, 4); |
| 151 var div = createTestHtml( | 163 var div = createTestHtml( |
| 152 '<template bind>' | 164 '<template bind>' |
| 153 '{{ foo }} + {{ 2x: bar }} + {{ 4x: bar }}</template>'); | 165 '{{ foo }} + {{ 2x: bar }} + {{ 4x: bar }}</template>'); |
| 154 recursivelySetTemplateModel(div, model, new TimesTwoSyntax()); | 166 var template = templateBind(div.firstChild); |
| 167 template..model = model..bindingDelegate = new TimesTwoSyntax(); | |
| 155 return new Future(() { | 168 return new Future(() { |
| 156 expect(div.nodes.length, 2); | 169 expect(div.nodes.length, 2); |
| 157 expect(div.nodes.last.text, '2 + 8 + '); | 170 expect(div.nodes.last.text, '2 + 8 + '); |
| 158 | 171 |
| 159 model.foo = 4; | 172 model.foo = 4; |
| 160 model.bar = 8; | 173 model.bar = 8; |
| 161 }).then(endOfMicrotask).then((_) { | 174 }).then(endOfMicrotask).then((_) { |
| 162 expect(div.nodes.last.text, '4 + 16 + '); | 175 expect(div.nodes.last.text, '4 + 16 + '); |
| 163 }); | 176 }); |
| 164 }); | 177 }); |
| 165 | 178 |
| 179 test('CreateInstance', () { | |
| 180 var delegateFoo = new SimpleTextDelegate('foo'); | |
| 181 var delegateBar = new SimpleTextDelegate('bar'); | |
| 182 | |
| 183 var div = createTestHtml('<template bind>[[ 2x: bar ]]</template>'); | |
| 184 var template = templateBind(div.firstChild); | |
| 185 template..bindingDelegate = delegateFoo..model = {}; | |
| 186 | |
| 187 return new Future(() { | |
| 188 expect(div.nodes.length, 2); | |
| 189 expect(div.lastChild.text, 'foo'); | |
| 190 | |
| 191 var fragment = template.createInstance({}); | |
| 192 expect(fragment.nodes.length, 1); | |
| 193 expect(fragment.lastChild.text, 'foo'); | |
| 194 | |
| 195 fragment = template.createInstance({}, delegateBar); | |
| 196 expect(fragment.nodes.length, 1); | |
| 197 expect(fragment.lastChild.text, 'bar'); | |
| 198 }); | |
| 199 }); | |
| 200 | |
| 166 // Note: issue-141 test not included here as it's not related to the | 201 // Note: issue-141 test not included here as it's not related to the |
| 167 // BindingDelegate | 202 // BindingDelegate |
| 168 } | 203 } |
| 169 | 204 |
| 170 // TODO(jmesserly): mocks would be cleaner here. | 205 // TODO(jmesserly): mocks would be cleaner here. |
| 171 | 206 |
| 172 class TestBindingSyntax extends BindingDelegate { | 207 class TestBindingSyntax extends BindingDelegate { |
| 173 var log = []; | 208 var log = []; |
| 174 | 209 |
| 175 prepareBinding(String path, String name, Node node) { | 210 prepareBinding(String path, String name, Node node) { |
| 176 var tagName = node is Element ? node.tagName : 'TEXT'; | 211 var tagName = node is Element ? node.tagName : 'TEXT'; |
| 177 int id = log.length; | 212 int id = log.length; |
| 178 log.add(['prepare', path, name, tagName]); | 213 log.add(['prepare', path, name, tagName]); |
| 179 final outerNode = node; | 214 final outerNode = node; |
| 180 return (model, node, oneTime) { | 215 return (model, node, oneTime) { |
| 181 var tagName = node is Element ? node.tagName : 'TEXT'; | 216 var tagName = node is Element ? node.tagName : 'TEXT'; |
| 182 log.add(['bindFn', model, tagName, id]); | 217 log.add(['bindFn', model, tagName, id]); |
| 183 return oneTime ? new PropertyPath(path).getValueFrom(model) : | 218 return oneTime ? new PropertyPath(path).getValueFrom(model) : |
| 184 new PathObserver(model, path); | 219 new PathObserver(model, path); |
| 185 }; | 220 }; |
| 186 } | 221 } |
| 187 } | 222 } |
| 188 | 223 |
| 224 class SimpleTextDelegate extends BindingDelegate { | |
| 225 final String text; | |
| 226 SimpleTextDelegate(this.text); | |
| 227 | |
| 228 prepareBinding(path, name, node) { | |
| 229 if (name == 'text') return (model, _, oneTime) => text; | |
| 230 return null; | |
| 231 } | |
|
Siggi Cherem (dart-lang)
2014/06/03 17:41:03
nit: maybe use "?:":
prepareBinding(path, name, n
Jennifer Messerly
2014/06/04 04:42:16
Done.
| |
| 232 } | |
| 233 | |
| 189 class TestModelSyntax extends BindingDelegate { | 234 class TestModelSyntax extends BindingDelegate { |
| 190 var log = []; | 235 var log = []; |
| 191 var altModels = new ListQueue(); | 236 var altModels = new ListQueue(); |
| 192 | 237 |
| 193 prepareInstanceModel(template) { | 238 prepareInstanceModel(template) { |
| 194 log.add(['prepare', template]); | 239 log.add(['prepare', template]); |
| 195 return (model) { | 240 return (model) { |
| 196 log.add(['bindFn', model]); | 241 log.add(['bindFn', model]); |
| 197 return altModels.removeFirst(); | 242 return altModels.removeFirst(); |
| 198 }; | 243 }; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 263 | 308 |
| 264 prepareInstanceModel(template) => | 309 prepareInstanceModel(template) => |
| 265 (model) => toObservable({ 'id': '${model}-narg' }); | 310 (model) => toObservable({ 'id': '${model}-narg' }); |
| 266 | 311 |
| 267 | 312 |
| 268 prepareInstancePositionChanged(template) => (templateInstance, index) { | 313 prepareInstancePositionChanged(template) => (templateInstance, index) { |
| 269 templateInstance.model['index'] = 2 * index; | 314 templateInstance.model['index'] = 2 * index; |
| 270 }; | 315 }; |
| 271 } | 316 } |
| 272 | 317 |
| OLD | NEW |