| 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 binding_syntax_test; | |
| 6 | |
| 7 import 'dart:collection'; | |
| 8 import 'dart:html'; | |
| 9 import 'package:mdv/mdv.dart' as mdv; | |
| 10 import 'package:observe/observe.dart'; | |
| 11 import 'package:unittest/html_config.dart'; | |
| 12 import 'package:unittest/unittest.dart'; | |
| 13 import 'mdv_test_utils.dart'; | |
| 14 | |
| 15 // Note: this file ported from | |
| 16 // https://github.com/toolkitchen/mdv/blob/master/tests/syntax.js | |
| 17 | |
| 18 main() { | |
| 19 mdv.initialize(); | |
| 20 useHtmlConfiguration(); | |
| 21 | |
| 22 group('Syntax FooBarModel', () { | |
| 23 syntaxTests(([f, b]) => new FooBarModel(f, b)); | |
| 24 }); | |
| 25 group('Syntax FooBarNotifyModel', () { | |
| 26 syntaxTests(([f, b]) => new FooBarNotifyModel(f, b)); | |
| 27 }); | |
| 28 } | |
| 29 | |
| 30 syntaxTests(FooBarModel fooModel([foo, bar])) { | |
| 31 var testDiv; | |
| 32 | |
| 33 setUp(() { | |
| 34 document.body.append(testDiv = new DivElement()); | |
| 35 }); | |
| 36 | |
| 37 tearDown(() { | |
| 38 testDiv.remove(); | |
| 39 testDiv = null; | |
| 40 }); | |
| 41 | |
| 42 createTestHtml(s) { | |
| 43 var div = new DivElement(); | |
| 44 div.innerHtml = s; | |
| 45 testDiv.append(div); | |
| 46 | |
| 47 for (var node in div.queryAll('*')) { | |
| 48 if (node.isTemplate) TemplateElement.decorate(node); | |
| 49 } | |
| 50 | |
| 51 return div; | |
| 52 } | |
| 53 | |
| 54 observeTest('Registration', () { | |
| 55 var model = fooModel('bar'); | |
| 56 | |
| 57 var testSyntax = new TestBindingSyntax(); | |
| 58 var div = createTestHtml('<template bind>{{ foo }}' | |
| 59 '<template bind>{{ foo }}</template></template>'); | |
| 60 recursivelySetTemplateModel(div, model, testSyntax); | |
| 61 performMicrotaskCheckpoint(); | |
| 62 expect(div.nodes.length, 4); | |
| 63 expect(div.nodes.last.text, 'bar'); | |
| 64 expect(div.nodes[2].tagName, 'TEMPLATE'); | |
| 65 | |
| 66 expect(testSyntax.log, [ | |
| 67 [model, '', 'bind', 'TEMPLATE'], | |
| 68 [model, 'foo', 'text', null], | |
| 69 [model, '', 'bind', 'TEMPLATE'], | |
| 70 [model, 'foo', 'text', null], | |
| 71 ]); | |
| 72 }); | |
| 73 | |
| 74 observeTest('getInstanceModel', () { | |
| 75 var model = toObservable([fooModel(1), fooModel(2), fooModel(3)]); | |
| 76 | |
| 77 var testSyntax = new TestModelSyntax(); | |
| 78 testSyntax.altModels.addAll([fooModel('a'), fooModel('b'), fooModel('c')]); | |
| 79 | |
| 80 var div = createTestHtml('<template repeat>{{ foo }}</template>'); | |
| 81 | |
| 82 var template = div.nodes[0]; | |
| 83 recursivelySetTemplateModel(div, model, testSyntax); | |
| 84 performMicrotaskCheckpoint(); | |
| 85 | |
| 86 expect(div.nodes.length, 4); | |
| 87 expect(div.nodes[0].tagName, 'TEMPLATE'); | |
| 88 expect(div.nodes[1].text, 'a'); | |
| 89 expect(div.nodes[2].text, 'b'); | |
| 90 expect(div.nodes[3].text, 'c'); | |
| 91 | |
| 92 expect(testSyntax.log, [ | |
| 93 [template, model[0]], | |
| 94 [template, model[1]], | |
| 95 [template, model[2]], | |
| 96 ]); | |
| 97 }); | |
| 98 | |
| 99 observeTest('getInstanceModel - reorder instances', () { | |
| 100 var model = toObservable([0, 1, 2]); | |
| 101 | |
| 102 var div = createTestHtml('<template repeat syntax="Test">{{}}</template>'); | |
| 103 var template = div.firstChild; | |
| 104 var delegate = new TestInstanceModelSyntax(); | |
| 105 | |
| 106 recursivelySetTemplateModel(div, model, delegate); | |
| 107 performMicrotaskCheckpoint(); | |
| 108 expect(delegate.count, 3); | |
| 109 | |
| 110 // Note: intentionally mutate in place. | |
| 111 model.replaceRange(0, model.length, model.reversed.toList()); | |
| 112 performMicrotaskCheckpoint(); | |
| 113 expect(delegate.count, 3); | |
| 114 }); | |
| 115 | |
| 116 observeTest('Basic', () { | |
| 117 var model = fooModel(2, 4); | |
| 118 var div = createTestHtml( | |
| 119 '<template bind syntax="2x">' | |
| 120 '{{ foo }} + {{ 2x: bar }} + {{ 4x: bar }}</template>'); | |
| 121 recursivelySetTemplateModel(div, model, new TimesTwoSyntax()); | |
| 122 performMicrotaskCheckpoint(); | |
| 123 expect(div.nodes.length, 2); | |
| 124 expect(div.nodes.last.text, '2 + 8 + '); | |
| 125 | |
| 126 model.foo = 4; | |
| 127 model.bar = 8; | |
| 128 performMicrotaskCheckpoint(); | |
| 129 expect(div.nodes.last.text, '4 + 16 + '); | |
| 130 }); | |
| 131 } | |
| 132 | |
| 133 // TODO(jmesserly): mocks would be cleaner here. | |
| 134 | |
| 135 class TestBindingSyntax extends BindingDelegate { | |
| 136 var log = []; | |
| 137 | |
| 138 getBinding(model, String path, String name, Node node) { | |
| 139 log.add([model, path, name, node is Element ? node.tagName : null]); | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 class TestModelSyntax extends BindingDelegate { | |
| 144 var log = []; | |
| 145 var altModels = new ListQueue(); | |
| 146 | |
| 147 getInstanceModel(template, model) { | |
| 148 log.add([template, model]); | |
| 149 return altModels.removeFirst(); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 class TestInstanceModelSyntax extends BindingDelegate { | |
| 154 int count = 0; | |
| 155 getInstanceModel(template, model) { | |
| 156 count++; | |
| 157 return model; | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 // Note: this isn't a very smart whitespace handler. A smarter one would only | |
| 162 // trim indentation, not all whitespace. | |
| 163 // See "trimOrCompact" in the web_ui Pub package. | |
| 164 class WhitespaceRemover extends BindingDelegate { | |
| 165 int trimmed = 0; | |
| 166 int removed = 0; | |
| 167 | |
| 168 DocumentFragment getInstanceFragment(Element template) { | |
| 169 var instance = template.createInstance(); | |
| 170 var walker = new TreeWalker(instance, NodeFilter.SHOW_TEXT); | |
| 171 | |
| 172 var toRemove = []; | |
| 173 while (walker.nextNode() != null) { | |
| 174 var node = walker.currentNode; | |
| 175 var text = node.text.replaceAll('\n', '').trim(); | |
| 176 if (text.length != node.text.length) { | |
| 177 if (text.length == 0) { | |
| 178 toRemove.add(node); | |
| 179 } else { | |
| 180 trimmed++; | |
| 181 node.text = text; | |
| 182 } | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 for (var node in toRemove) node.remove(); | |
| 187 removed += toRemove.length; | |
| 188 | |
| 189 return instance; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 class TimesTwoSyntax extends BindingDelegate { | |
| 194 getBinding(model, path, name, node) { | |
| 195 path = path.trim(); | |
| 196 if (!path.startsWith('2x:')) return null; | |
| 197 | |
| 198 path = path.substring(3); | |
| 199 return new CompoundBinding((values) => values['value'] * 2) | |
| 200 ..bind('value', model, path); | |
| 201 } | |
| 202 } | |
| OLD | NEW |