Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 @TestOn('browser') | 4 @TestOn('browser') |
| 5 library polymer_elements.test.firebase_collection_test; | 5 library polymer_elements.test.firebase_collection_test; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:js'; | 8 import 'dart:js'; |
| 9 import 'package:polymer/polymer.dart'; | 9 import 'package:polymer/polymer.dart'; |
| 10 import 'package:polymer_elements/firebase_collection.dart'; | 10 import 'package:polymer_elements/firebase_collection.dart'; |
| 11 import 'package:web_components/web_components.dart'; | 11 import 'package:web_components/web_components.dart'; |
| 12 import 'package:test/test.dart'; | 12 import 'package:test/test.dart'; |
| 13 import 'common.dart'; | 13 import 'common.dart'; |
| 14 import 'firebase_test_helpers.dart'; | |
| 14 | 15 |
| 15 main() async { | 16 main() async { |
| 16 await initWebComponents(); | 17 await initWebComponents(); |
| 17 | 18 |
| 18 group('<firebase-collection>', () { | 19 group('firebase-collection', () { |
| 19 FirebaseCollection firebase; | 20 FirebaseCollection firebase; |
| 20 | 21 |
| 21 group('collection manipulation', () { | 22 tearDown(() { |
| 23 if (firebase != null) removeFirebase(firebase); | |
| 24 }); | |
| 25 | |
| 26 group('basic usage', () { | |
| 27 var numberOfItems; | |
| 28 | |
| 29 setUp(() { | |
| 30 numberOfItems = 3; | |
| 31 firebase = | |
| 32 fixtureFirebase('TrivialCollection', arrayOfObjects(numberOfItems)); | |
| 33 return wait(1); | |
| 34 }); | |
| 35 | |
| 36 test('exposes data as an array', () { | |
| 37 expect(firebase.data is List, isTrue); | |
| 38 expect(firebase.data.length, numberOfItems); | |
| 39 }); | |
| 40 | |
| 41 test('receives data from Firebase location', () { | |
| 42 expect(firebase.data[0]['value'] is num, isTrue); | |
| 43 }); | |
| 44 }); | |
| 45 | |
| 46 group('ordered primitives', () { | |
| 47 var numberOfItems; | |
| 48 | |
| 49 setUp(() { | |
| 50 numberOfItems = 5; | |
| 51 firebase = fixtureFirebase( | |
| 52 'TrivialCollection', arrayOfPrimitives(numberOfItems)); | |
| 53 firebase.orderByValue = true; | |
| 54 }); | |
| 55 | |
| 56 test('converts primitives into objects with a value key', () { | |
| 57 expect(firebase.data[0] is JsObject, isTrue); | |
| 58 }); | |
| 59 | |
| 60 test('orders primitives by value', () { | |
| 61 var lastValue = -1; | |
| 62 | |
| 63 expect(firebase.data.length, numberOfItems); | |
| 64 | |
| 65 firebase.data.forEach((datum) { | |
| 66 expect(datum['value'], isNot(lessThan(lastValue))); | |
| 67 lastValue = datum['value']; | |
| 68 }); | |
| 69 }); | |
| 70 }); | |
| 71 | |
| 72 group('removing a value locally', () { | |
| 73 var numberOfItems; | |
| 74 setUp(() { | |
| 75 numberOfItems = 3; | |
| 76 firebase = | |
| 77 fixtureFirebase('TrivialCollection', arrayOfObjects(numberOfItems)); | |
| 78 }); | |
| 79 | |
| 80 test('works for data-bound changes', () { | |
| 81 firebase.removeAt('data', 0); | |
| 82 expect(firebase.data.length, numberOfItems - 1); | |
| 83 }); | |
| 84 | |
| 85 test('can be done with `remove`', () { | |
| 86 var objectToBeRemoved = firebase.data[0]; | |
| 87 firebase.removeItem(objectToBeRemoved); | |
| 88 | |
| 89 expect(firebase.data.length, numberOfItems - 1); | |
| 90 expect(firebase.data.indexOf(objectToBeRemoved), -1); | |
| 91 }); | |
| 92 }); | |
| 93 | |
| 94 group('adding a value locally', () { | |
| 95 setUp(() { | |
| 96 firebase = fixtureFirebase('TrivialCollection'); | |
| 97 }); | |
| 98 | |
| 99 test('works for data-bound changes', () { | |
| 100 var done = new Completer(); | |
| 101 var intendedValue = randomInt(); | |
| 102 // Can't call `add` since it is overridden | |
| 103 var index = | |
| 104 firebase.jsElement.callMethod('push', ['data', intendedValue]) - 1; | |
|
Siggi Cherem (dart-lang)
2015/10/28 22:35:16
should `push` be exposed in the Dart API? Is it pa
jakemac
2015/10/29 16:07:29
I guess what should really happen here is changing
| |
| 105 | |
| 106 // NOTE(cdata): See polymer/polymer#2491. | |
| 107 firebase.async(() { | |
| 108 expect(firebase.data[index]['value'], isNotNull); | |
| 109 expect(firebase.data[index]['value'], intendedValue); | |
| 110 done.complete(); | |
| 111 }, waitTime: 1); | |
| 112 | |
| 113 return done.future; | |
| 114 }); | |
| 115 | |
| 116 test('can be done with `add`', () { | |
| 117 var done = new Completer(); | |
| 118 var object = randomObject(); | |
| 119 var length = firebase.data.length; | |
| 120 var foundObject; | |
| 121 | |
| 122 firebase.add(new JsObject.jsify(object)); | |
| 123 | |
| 124 // NOTE(cdata): See polymer/polymer#2491. | |
| 125 firebase.async(() { | |
| 126 expect(firebase.data.length, length + 1); | |
| 127 | |
| 128 firebase.data.forEach((datum) { | |
| 129 if (datum['value'] == object['value']) { | |
| 130 foundObject = datum; | |
| 131 } | |
| 132 }); | |
| 133 | |
| 134 expect(foundObject, isNotNull); | |
| 135 expect(foundObject['value'], object['value']); | |
| 136 done.complete(); | |
| 137 }, waitTime: 1); | |
| 138 | |
| 139 return done.future; | |
| 140 }); | |
| 141 }); | |
| 142 | |
| 143 group('a changing child', () { | |
| 144 var numberOfItems; | |
| 145 var remoteFirebase; | |
| 146 | |
| 147 setUp(() { | |
| 148 numberOfItems = 3; | |
| 149 firebase = | |
| 150 fixtureFirebase('TrivialCollection', arrayOfObjects(numberOfItems)); | |
| 151 remoteFirebase = new JsObject(context['Firebase'], [firebase.location]); | |
| 152 }); | |
| 153 | |
| 154 test('updates the child key in place with the new value', () { | |
| 155 var datum = firebase.data[0]; | |
| 156 var newValue = 99999; | |
| 157 var key = context['Polymer']['Collection'] | |
| 158 .callMethod('get', [firebase.data]).callMethod('getKey', [datum]); | |
| 159 | |
| 160 firebase.set('data.$key.value', newValue); | |
| 161 | |
| 162 expect(firebase.data[0]['value'], newValue); | |
| 163 }); | |
| 164 }); | |
| 165 | |
| 166 group('syncing collections', () { | |
| 167 var numberOfItems; | |
| 168 var remoteFirebase; | |
| 169 | |
| 170 setUp(() { | |
| 171 numberOfItems = 3; | |
| 172 | |
| 173 firebase = fixtureFirebase('TrivialCollection', arrayOfObjects(3)); | |
| 174 firebase.orderValueType = 'number'; | |
| 175 firebase.orderByValue = true; | |
| 176 | |
| 177 remoteFirebase = new JsObject(context['Firebase'], [firebase.location]); | |
| 178 }); | |
| 179 | |
| 180 test('sync a new item at the correct index', () { | |
| 181 var firstValue = firebase.data[0]; | |
| 182 var secondValue = firebase.data[1]; | |
| 183 var datum = firebase.data[0]; | |
| 184 var key = context['Polymer']['Collection'] | |
|
Siggi Cherem (dart-lang)
2015/10/28 22:35:16
do we want to eventually wrap these static APIs as
jakemac
2015/10/29 16:07:29
These collection apis are not really useful in gen
| |
| 185 .callMethod('get', [firebase.data]).callMethod('getKey', [datum]); | |
| 186 var remoteValue; | |
| 187 | |
| 188 remoteFirebase.callMethod('on', [ | |
| 189 'value', | |
| 190 (snapshot) { | |
| 191 remoteValue = snapshot.callMethod('val'); | |
| 192 } | |
| 193 ]); | |
| 194 | |
| 195 expect(remoteValue[0]['value'], firebase.data[0]['value']); | |
| 196 }); | |
| 197 }); | |
| 198 | |
| 199 group('data-bound collection manipulation', () { | |
| 200 var numberOfItems; | |
| 201 var elements; | |
| 22 DomBind domBind; | 202 DomBind domBind; |
| 23 var dom; | 203 |
| 24 | 204 setUp(() { |
| 25 setUp(() { | 205 elements = fixture('BoundCollection'); |
| 26 dom = fixture('BoundCollection'); | 206 domBind = elements.querySelector('[is=dom-bind]'); |
| 27 domBind = dom.querySelector('[is=dom-bind]'); | 207 firebase = elements.querySelector('firebase-collection'); |
| 28 firebase = dom.querySelector('firebase-collection'); | 208 firebase.location = fixtureLocation(arrayOfObjects(3)); |
| 29 }); | 209 numberOfItems = 3; |
| 30 | 210 }); |
| 31 test('added values reflect 1-to-1 in the DOM', () { | 211 |
| 32 return firebase.on['firebase-value'].first.then((_) { | 212 test('splices reflect in Firebase data', () { |
| 33 var done = firebase.on['firebase-child-added'].first.then((_) { | 213 var done = new Completer(); |
| 34 expect(dom.querySelectorAll('div').length, firebase.data.length); | 214 domBind.removeAt('data', 0); |
| 35 }); | 215 domBind.add('data', randomObject()); |
| 36 domBind.insert('data', 0, {'value': 'blah'}); | 216 domBind.removeAt('data', 0); |
| 37 domBind.render(); | 217 domBind.addAll('data', arrayOfObjects(2)); |
| 38 return done; | 218 |
| 39 }); | 219 // NOTE(cdata): See polymer/polymer#2491. |
| 40 }); | 220 firebase.async(() { |
| 41 }, skip: 'https://github.com/dart-lang/polymer_elements/issues/62'); | 221 expect(firebase.data.length, domBind['data'].length); |
| 42 | 222 |
| 43 group('basic usage', () { | 223 for (int i = 0; i < firebase.data.length; i++) { |
| 44 setUp(() { | 224 var datum = firebase.data[i]; |
| 45 firebase = fixture('TrivialCollection'); | 225 expect(domBind['data'][i]['value'], datum['value']); |
| 46 }); | 226 } |
| 47 | 227 |
| 48 tearDown(() { | 228 done.complete(); |
| 49 firebase.disconnect(); | 229 }, waitTime: 1); |
| 50 }); | 230 |
| 51 | 231 done.future; |
| 52 test('exposes data as an array', () { | 232 }); |
| 53 return firebase.on['firebase-child-added'].first.then((_) { | 233 |
| 54 expect(firebase.data is List, isTrue); | 234 test('splices reflect in the DOM', () { |
| 55 }); | 235 var divs; |
| 56 }); | 236 var done = new Completer(); |
| 57 | 237 |
| 58 test('receives data from Firebase location', () { | 238 firebase.jsElement.callMethod('push', |
| 59 return firebase.on['data-changed'].first.then((_) { | 239 ['data']..addAll(new JsObject.jsify(arrayOfObjects(3)) as JsArray)); |
| 60 expect(firebase.data[0]['value'], true); | 240 |
| 61 }); | 241 firebase.async(() { |
| 62 }); | 242 divs = elements.querySelectorAll('div'); |
| 63 }); | 243 expect(divs.length, firebase.data.length); |
| 64 | 244 |
| 65 group('ordered primitives', () { | 245 domBind.removeAt('data', 2); |
| 66 setUp(() { | 246 domBind.add('data', randomObject()); |
| 67 firebase = fixture('PrimitiveCollection'); | 247 |
| 68 }); | 248 firebase.async(() { |
| 69 | 249 divs = elements.querySelectorAll('div'); |
| 70 tearDown(() { | 250 expect(divs.length, firebase.data.length); |
| 71 firebase.disconnect(); | 251 |
| 72 }); | 252 for (int i = 0; i < firebase.data.length; i++) { |
| 73 | 253 var datum = firebase.data[i]; |
| 74 test('converts primitives into objects with a value key', () { | 254 var divValue = int.parse(divs[i].text); |
| 75 return firebase.on['firebase-child-added'].first.then((_) { | 255 expect(datum.value, divValue); |
| 76 expect(firebase.data[0], isNotNull); | 256 } |
| 77 }); | 257 |
| 78 }); | 258 done.complete(); |
| 79 | 259 }, waitTime: 1); |
| 80 test('orders primitives by value', () { | 260 }, waitTime: 1); |
| 81 return firebase.on['firebase-value'].first.then((_) { | 261 |
| 82 var lastValue = -1.0 / 0; | 262 return done.future; |
| 83 expect(firebase.data.length, greaterThan(0)); | 263 }, skip: 'https://github.com/dart-lang/polymer_elements/issues/62'); |
| 84 firebase.data.forEach((item) { | |
| 85 expect(item['value'], greaterThanOrEqualTo(lastValue)); | |
| 86 lastValue = item['value']; | |
| 87 }); | |
| 88 }); | |
| 89 }); | |
| 90 | |
| 91 group('adding a value locally', () { | |
| 92 setUp(() { | |
| 93 return firebase.on['firebase-value'].first; | |
| 94 }); | |
| 95 | |
| 96 test('can be done with `add`', () { | |
| 97 var length = firebase.data.length; | |
| 98 var newValue = firebase.data[firebase.data.length - 1]['value'] + 1; | |
| 99 var key; | |
| 100 | |
| 101 var done = firebase.on['firebase-child-added'].first.then((_) { | |
| 102 expect(firebase.data.length, length + 1); | |
| 103 expect(firebase.data[firebase.data.length - 1]['value'], newValue); | |
| 104 }).then((_) async { | |
| 105 await wait(1); | |
| 106 firebase.removeByKey(key); | |
| 107 }); | |
| 108 | |
| 109 key = firebase.add(newValue).callMethod('key'); | |
| 110 return done; | |
| 111 }); | |
| 112 }); | |
| 113 }); | |
| 114 | |
| 115 group('a child changes', () { | |
| 116 setUp(() { | |
| 117 firebase = fixture('ChangingChildren'); | |
| 118 return firebase.on['firebase-value'].first; | |
| 119 }); | |
| 120 | |
| 121 test('updates the child key in place with the new value', () { | |
| 122 var childrenKeys = []; | |
| 123 | |
| 124 var done = firebase.on['firebase-value'].first.then((_) async { | |
| 125 // Wait for childrenKeys to be populated | |
| 126 await new Future(() {}); | |
| 127 var middleValue = firebase.getByKey(childrenKeys[1]); | |
| 128 var changes; | |
| 129 | |
| 130 expect(middleValue['foo'], 1); | |
| 131 expect(middleValue['bar'], 1); | |
| 132 | |
| 133 changes = firebase.on['firebase-child-changed'].first; | |
| 134 | |
| 135 firebase.set('data.${firebase.data.indexOf(middleValue)}.bar', 2); | |
| 136 | |
| 137 return changes; | |
| 138 }).then((_) { | |
| 139 var middleValue = firebase.getByKey(childrenKeys[1]); | |
| 140 | |
| 141 expect(middleValue['foo'], 1); | |
| 142 expect(middleValue['bar'], 2); | |
| 143 }).then((_) { | |
| 144 childrenKeys.forEach((key) { | |
| 145 firebase.removeByKey(key); | |
| 146 }); | |
| 147 }); | |
| 148 | |
| 149 var index = -1; | |
| 150 childrenKeys = [0, 1, 2].map((value) { | |
| 151 index++; | |
| 152 return firebase | |
| 153 .add(new JsObject.jsify({'foo': value, 'bar': index})) | |
| 154 .callMethod('key'); | |
| 155 }).toList(); | |
| 156 | |
| 157 return done; | |
| 158 }); | |
| 159 }); | |
| 160 | |
| 161 group('syncing collections', () { | |
| 162 FirebaseCollection localFirebase; | |
| 163 FirebaseCollection remoteFirebase; | |
| 164 | |
| 165 setUp(() { | |
| 166 var children = fixture('SyncingCollections'); | |
| 167 localFirebase = children[0]; | |
| 168 remoteFirebase = children[1]; | |
| 169 return Future.wait([ | |
| 170 localFirebase.on['firebase-value'].first, | |
| 171 remoteFirebase.on['firebase-value'].first | |
| 172 ]); | |
| 173 }); | |
| 174 | |
| 175 test('syncs a new item at the correct index', () { | |
| 176 var data = {'foo': 100}; | |
| 177 var key; | |
| 178 | |
| 179 var done = remoteFirebase.on['firebase-value'].first.then((_) async { | |
| 180 await wait(1); | |
| 181 var value = remoteFirebase.getByKey(key); | |
| 182 var lowValue = remoteFirebase.getByKey('lowValue'); | |
| 183 var highValue = remoteFirebase.getByKey('highValue'); | |
| 184 | |
| 185 var index = remoteFirebase.data.indexOf(value); | |
| 186 var lowIndex = remoteFirebase.data.indexOf(lowValue); | |
| 187 var highIndex = remoteFirebase.data.indexOf(highValue); | |
| 188 | |
| 189 expect(value, isNotNull); | |
| 190 expect(index, lessThan(highIndex)); | |
| 191 expect(index, greaterThan(lowIndex)); | |
| 192 }).then((_) { | |
| 193 localFirebase.removeByKey(key); | |
| 194 }); | |
| 195 | |
| 196 key = localFirebase.add(new JsObject.jsify(data)).callMethod('key'); | |
| 197 | |
| 198 return done; | |
| 199 }); | |
| 200 }); | 264 }); |
| 201 }); | 265 }); |
| 202 } | 266 } |
| OLD | NEW |