| OLD | NEW |
| 1 <!doctype html> | 1 <!doctype html> |
| 2 <!-- | 2 <!-- |
| 3 @license | 3 @license |
| 4 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | 4 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
| 5 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt | 5 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt |
| 6 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | 6 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
| 7 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt | 7 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt |
| 8 Code distributed by Google as part of the polymer project is also | 8 Code distributed by Google as part of the polymer project is also |
| 9 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt | 9 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt |
| 10 --> | 10 --> |
| 11 <html> | 11 <html> |
| 12 <head> | 12 <head> |
| 13 <title>firebase-collection</title> | 13 <title>firebase-collection</title> |
| 14 | 14 |
| 15 <script src="../../webcomponentsjs/webcomponents.js"></script> | 15 <script src="../../webcomponentsjs/webcomponents.js"></script> |
| 16 <script src="../../web-component-tester/browser.js"></script> | 16 <script src="../../web-component-tester/browser.js"></script> |
| 17 <script src="../../test-fixture/test-fixture-mocha.js"></script> | 17 <script src="../../test-fixture/test-fixture-mocha.js"></script> |
| 18 | 18 |
| 19 <link rel="import" href="../../polymer/polymer.html"> | 19 <link rel="import" href="../../polymer/polymer.html"> |
| 20 <link rel="import" href="../../promise-polyfill/promise-polyfill.html"> | |
| 21 <link rel="import" href="../../test-fixture/test-fixture.html"> | 20 <link rel="import" href="../../test-fixture/test-fixture.html"> |
| 22 <link rel="import" href="test-helpers.html"> | 21 <link rel="import" href="test-helpers.html"> |
| 23 <link rel="import" href="../firebase-collection.html"> | 22 <link rel="import" href="../firebase-collection.html"> |
| 24 </head> | 23 </head> |
| 25 <body> | 24 <body> |
| 26 <test-fixture id="TrivialCollection"> | 25 <test-fixture id="TrivialCollection"> |
| 27 <template> | 26 <template> |
| 28 <firebase-collection | 27 <firebase-collection log></firebase-collection> |
| 29 location="https://fb-element-demo.firebaseio.com/test/trivial" | |
| 30 log> | |
| 31 </firebase-collection> | |
| 32 </template> | |
| 33 </test-fixture> | |
| 34 <test-fixture id="PrimitiveCollection"> | |
| 35 <template> | |
| 36 <firebase-collection | |
| 37 location="https://fb-element-demo.firebaseio.com/test/primitives" | |
| 38 order-by-value | |
| 39 log> | |
| 40 </firebase-collection> | |
| 41 </template> | |
| 42 </test-fixture> | |
| 43 <test-fixture id="ChangingChildren"> | |
| 44 <template> | |
| 45 <firebase-collection | |
| 46 location="https://fb-element-demo.firebaseio.com/test/changing_children" | |
| 47 order-by-child="foo" | |
| 48 log> | |
| 49 </firebase-collection> | |
| 50 </template> | 28 </template> |
| 51 </test-fixture> | 29 </test-fixture> |
| 52 | 30 |
| 53 <test-fixture id="SyncingCollections"> | |
| 54 <template> | |
| 55 <firebase-collection | |
| 56 location="https://fb-element-demo.firebaseio.com/test/syncing" | |
| 57 log> | |
| 58 </firebase-collection> | |
| 59 <firebase-collection | |
| 60 location="https://fb-element-demo.firebaseio.com/test/syncing" | |
| 61 order-by-child="foo" | |
| 62 log> | |
| 63 </firebase-collection> | |
| 64 </template> | |
| 65 </test-fixture> | |
| 66 | |
| 67 <test-fixture id="BoundCollection"> | 31 <test-fixture id="BoundCollection"> |
| 68 <template> | 32 <template> |
| 69 <section> | 33 <section> |
| 70 <!-- TODO(cdata): Add support for elements like `dom-bind` at the root | 34 <!-- TODO(cdata): Add support for elements like `dom-bind` at the root |
| 71 of the template to `test-fixture`, so that we can remove this | 35 of the template to `test-fixture`, so that we can remove this |
| 72 wrapping `section`. --> | 36 wrapping `section`. --> |
| 73 <template is="dom-bind"> | 37 <template is="dom-bind"> |
| 74 <template is="dom-repeat" items="{{data}}"> | 38 <template is="dom-repeat" items="{{data}}"> |
| 75 <div>[[item.value]]</div> | 39 <div>[[item.value]]</div> |
| 76 </template> | 40 </template> |
| 77 <firebase-collection | 41 <firebase-collection |
| 78 location="https://fb-element-demo.firebaseio.com/test/empty" | |
| 79 data="{{data}}" | 42 data="{{data}}" |
| 80 log> | 43 log> |
| 81 </firebase-collection> | 44 </firebase-collection> |
| 82 </template> | 45 </template> |
| 83 </section> | 46 </section> |
| 84 </template> | 47 </template> |
| 85 </test-fixture> | 48 </test-fixture> |
| 86 | 49 |
| 87 <script> | 50 <script> |
| 88 suite('<firebase-collection>', function() { | 51 |
| 52 suite('firebase-collection', function() { |
| 89 var firebase; | 53 var firebase; |
| 90 | 54 |
| 91 suite('collection manipulation', function() { | 55 teardown(function() { |
| 56 if (firebase) { |
| 57 removeFirebase(firebase); |
| 58 } |
| 59 }); |
| 60 |
| 61 suite('basic usage', function() { |
| 62 var numberOfItems; |
| 63 |
| 64 setup(function() { |
| 65 numberOfItems = 3; |
| 66 firebase = fixtureFirebase('TrivialCollection', arrayOfObjects(numberO
fItems)); |
| 67 }); |
| 68 |
| 69 test('exposes data as an array', function() { |
| 70 expect(firebase.data).to.be.an('array'); |
| 71 expect(firebase.data.length).to.be.equal(numberOfItems); |
| 72 }); |
| 73 |
| 74 test('receives data from Firebase location', function() { |
| 75 expect(firebase.data[0].value).to.be.a('number'); |
| 76 }); |
| 77 }); |
| 78 |
| 79 suite('ordered primitives', function() { |
| 80 var numberOfItems; |
| 81 |
| 82 setup(function() { |
| 83 numberOfItems = 5; |
| 84 firebase = fixtureFirebase('TrivialCollection', arrayOfPrimitives(numb
erOfItems)); |
| 85 firebase.orderByValue = true; |
| 86 }); |
| 87 |
| 88 test('converts primitives into objects with a value key', function() { |
| 89 expect(firebase.data[0]).to.be.an('object'); |
| 90 }); |
| 91 |
| 92 test('orders primitives by value', function() { |
| 93 var lastValue = -Infinity; |
| 94 |
| 95 expect(firebase.data.length).to.be.equal(numberOfItems); |
| 96 |
| 97 firebase.data.forEach(function(datum) { |
| 98 expect(datum.value).to.not.be.lessThan(lastValue); |
| 99 lastValue = datum.value; |
| 100 }); |
| 101 }); |
| 102 }); |
| 103 |
| 104 suite('removing a value locally', function() { |
| 105 var numberOfItems; |
| 106 setup(function() { |
| 107 numberOfItems = 3; |
| 108 firebase = fixtureFirebase('TrivialCollection', arrayOfObjects(numberO
fItems)); |
| 109 }); |
| 110 |
| 111 test('works for data-bound changes', function() { |
| 112 firebase.splice('data', 0, 1); |
| 113 expect(firebase.data.length).to.be.equal(numberOfItems - 1); |
| 114 }); |
| 115 |
| 116 test('can be done with `remove`', function() { |
| 117 var objectToBeRemoved = firebase.data[0]; |
| 118 firebase.remove(objectToBeRemoved); |
| 119 |
| 120 expect(firebase.data.length).to.be.equal(numberOfItems - 1); |
| 121 expect(firebase.data.indexOf(objectToBeRemoved)).to.be.equal(-1); |
| 122 }); |
| 123 }); |
| 124 |
| 125 suite('adding a value locally', function() { |
| 126 setup(function() { |
| 127 firebase = fixtureFirebase('TrivialCollection'); |
| 128 }); |
| 129 |
| 130 test('works for data-bound changes', function(done) { |
| 131 var intendedValue = randomInt(); |
| 132 var index = firebase.push('data', intendedValue) - 1; |
| 133 |
| 134 // NOTE(cdata): See polymer/polymer#2491. |
| 135 Polymer.Base.async(function() { |
| 136 expect(firebase.data[index]).to.have.property('value'); |
| 137 expect(firebase.data[index].value).to.be.equal(intendedValue); |
| 138 done(); |
| 139 }, 1); |
| 140 }); |
| 141 |
| 142 test('can be done with `add`', function(done) { |
| 143 var object = randomObject(); |
| 144 var length = firebase.data.length; |
| 145 var foundObject; |
| 146 |
| 147 firebase.add(object); |
| 148 |
| 149 // NOTE(cdata): See polymer/polymer#2491. |
| 150 Polymer.Base.async(function() { |
| 151 expect(firebase.data.length).to.be.equal(length + 1); |
| 152 |
| 153 firebase.data.forEach(function(datum) { |
| 154 if (datum.value === object.value) { |
| 155 foundObject = datum; |
| 156 } |
| 157 }); |
| 158 |
| 159 expect(foundObject).to.be.okay; |
| 160 expect(foundObject.value).to.be.equal(object.value); |
| 161 done(); |
| 162 }, 1); |
| 163 }); |
| 164 }); |
| 165 |
| 166 suite('a changing child', function() { |
| 167 var numberOfItems; |
| 168 var remoteFirebase; |
| 169 |
| 170 setup(function() { |
| 171 numberOfItems = 3; |
| 172 firebase = fixtureFirebase('TrivialCollection', arrayOfObjects(numberO
fItems)); |
| 173 remoteFirebase = new Firebase(firebase.location); |
| 174 }); |
| 175 |
| 176 test('updates the child key in place with the new value', function() { |
| 177 var datum = firebase.data[0]; |
| 178 var newValue = 99999; |
| 179 var key = Polymer.Collection.get(firebase.data).getKey(datum); |
| 180 |
| 181 firebase.set('data.' + key + '.value', newValue); |
| 182 |
| 183 expect(firebase.data[0].value).to.be.equal(newValue); |
| 184 }); |
| 185 }); |
| 186 |
| 187 suite('syncing collections', function() { |
| 188 var numberOfItems; |
| 189 var remoteFirebase; |
| 190 |
| 191 setup(function() { |
| 192 numberOfItems = 3; |
| 193 |
| 194 firebase = fixtureFirebase('TrivialCollection', arrayOfObjects(3)); |
| 195 firebase.orderValueType = 'number'; |
| 196 firebase.orderByValue = true; |
| 197 |
| 198 remoteFirebase = new Firebase(firebase.location); |
| 199 }); |
| 200 |
| 201 test('sync a new item at the correct index', function() { |
| 202 var firstValue = firebase.data[0]; |
| 203 var secondValue = firebase.data[1]; |
| 204 var datum = firebase.data[0]; |
| 205 var key = Polymer.Collection.get(firebase.data).getKey(datum); |
| 206 var remoteValue; |
| 207 |
| 208 remoteFirebase.on('value', function(snapshot) { |
| 209 remoteValue = snapshot.val(); |
| 210 }); |
| 211 |
| 212 expect(remoteValue[0].value).to.be.equal(firebase.data[0].value); |
| 213 }); |
| 214 }); |
| 215 |
| 216 suite('data-bound collection manipulation', function() { |
| 217 var numberOfItems; |
| 218 var elements; |
| 92 var domBind; | 219 var domBind; |
| 93 var dom; | 220 |
| 94 | 221 setup(function() { |
| 95 setup(function() { | 222 elements = fixture('BoundCollection'); |
| 96 dom = fixture('BoundCollection'); | 223 domBind = elements.querySelector('[is=dom-bind]'); |
| 97 domBind = dom.querySelector('[is=dom-bind]'); | 224 firebase = elements.querySelector('firebase-collection'); |
| 98 firebase = dom.querySelector('firebase-collection'); | 225 firebase.location = fixtureLocation(arrayOfObjects(3)); |
| 99 }); | 226 numberOfItems = 3; |
| 100 | 227 }); |
| 101 test('added values reflect 1-to-1 in the DOM', function(done) { | 228 |
| 102 waitForEvent(firebase, 'firebase-value').then(function() { | 229 test('splices reflect in Firebase data', function(done) { |
| 103 waitForEvent(firebase, 'firebase-child-added').then(function() { | 230 domBind.splice('data', 0, 1, randomObject()); |
| 104 expect(dom.querySelectorAll('div').length).to.be.equal(firebase.da
ta.length); | 231 domBind.shift('data'); |
| 232 domBind.push.apply(domBind, ['data'].concat(arrayOfObjects(2))); |
| 233 |
| 234 // NOTE(cdata): See polymer/polymer#2491. |
| 235 Polymer.Base.async(function() { |
| 236 expect(firebase.data.length).to.be.equal(domBind.data.length); |
| 237 |
| 238 firebase.data.forEach(function(datum, index) { |
| 239 expect(domBind.data[index].value).to.be.equal(datum.value); |
| 240 }); |
| 241 |
| 242 done(); |
| 243 }, 1); |
| 244 }); |
| 245 |
| 246 test('splices reflect in the DOM', function(done) { |
| 247 var divs; |
| 248 |
| 249 firebase.push.apply(firebase, ['data'].concat(arrayOfObjects(3))); |
| 250 |
| 251 Polymer.Base.async(function() { |
| 252 divs = elements.querySelectorAll('div'); |
| 253 expect(divs.length).to.be.equal(firebase.data.length); |
| 254 |
| 255 domBind.splice('data', 2, 1, randomObject()); |
| 256 |
| 257 Polymer.Base.async(function() { |
| 258 divs = elements.querySelectorAll('div'); |
| 259 expect(divs.length).to.be.equal(firebase.data.length); |
| 260 |
| 261 firebase.data.forEach(function(datum, index) { |
| 262 var divValue = parseInt(divs[index].textContent, 10); |
| 263 expect(datum.value).to.be.equal(divValue); |
| 264 }); |
| 265 |
| 105 done(); | 266 done(); |
| 106 }); | 267 }, 1); |
| 107 domBind.unshift('data', { value: 'blah' }); | 268 }, 1); |
| 108 }); | |
| 109 }); | |
| 110 }); | |
| 111 | |
| 112 suite('basic usage', function() { | |
| 113 setup(function() { | |
| 114 firebase = fixture('TrivialCollection'); | |
| 115 }); | |
| 116 | |
| 117 teardown(function() { | |
| 118 firebase.disconnect(); | |
| 119 }); | |
| 120 | |
| 121 test('exposes data as an array', function(done) { | |
| 122 waitForEvent(firebase, 'firebase-child-added').then(function() { | |
| 123 expect(firebase.data).to.be.an('array'); | |
| 124 done(); | |
| 125 }).catch(function(e) { | |
| 126 done(e); | |
| 127 }); | |
| 128 }); | |
| 129 | |
| 130 test('receives data from Firebase location', function(done) { | |
| 131 waitForEvent(firebase, 'data-changed').then(function() { | |
| 132 expect(firebase.data[0].value).to.be.equal(true); | |
| 133 done(); | |
| 134 }).catch(function(e) { | |
| 135 done(e); | |
| 136 }); | |
| 137 }); | |
| 138 }); | |
| 139 | |
| 140 suite('ordered primitives', function() { | |
| 141 setup(function() { | |
| 142 firebase = fixture('PrimitiveCollection'); | |
| 143 }); | |
| 144 | |
| 145 teardown(function() { | |
| 146 firebase.disconnect(); | |
| 147 }); | |
| 148 | |
| 149 test('converts primitives into objects with a value key', function(done)
{ | |
| 150 waitForEvent(firebase, 'firebase-child-added').then(function() { | |
| 151 expect(firebase.data[0]).to.be.an('object'); | |
| 152 done(); | |
| 153 }).catch(function(e) { | |
| 154 done(e); | |
| 155 }); | |
| 156 }); | |
| 157 | |
| 158 test('orders primitives by value', function(done) { | |
| 159 waitForEvent(firebase, 'firebase-value').then(function() { | |
| 160 var lastValue = -Infinity; | |
| 161 expect(firebase.data.length).to.be.greaterThan(0); | |
| 162 firebase.data.forEach(function(item) { | |
| 163 expect(item.value).to.not.be.lessThan(lastValue); | |
| 164 lastValue = item.value; | |
| 165 }); | |
| 166 done(); | |
| 167 }).catch(function(e) { | |
| 168 done(e); | |
| 169 }); | |
| 170 }); | |
| 171 | |
| 172 suite('adding a value locally', function() { | |
| 173 setup(function(done) { | |
| 174 waitForEvent(firebase, 'firebase-value').then(function() { | |
| 175 done(); | |
| 176 }).catch(function(e) { | |
| 177 done(e); | |
| 178 }); | |
| 179 }); | |
| 180 | |
| 181 test('can be done with `add`', function(done) { | |
| 182 var length = firebase.data.length; | |
| 183 var newValue = firebase.data[firebase.data.length - 1].value + 1; | |
| 184 var key; | |
| 185 | |
| 186 waitForEvent(firebase, 'firebase-child-added').then(function() { | |
| 187 expect(firebase.data.length).to.be.equal(length + 1); | |
| 188 expect(firebase.data[firebase.data.length - 1].value).to.be.equal(
newValue); | |
| 189 done(); | |
| 190 }).catch(function(e) { | |
| 191 done(e); | |
| 192 }).then(function() { | |
| 193 firebase.removeByKey(key); | |
| 194 }); | |
| 195 | |
| 196 key = firebase.add(newValue).key(); | |
| 197 }); | |
| 198 }); | |
| 199 }); | |
| 200 | |
| 201 suite('a child changes', function() { | |
| 202 setup(function(done) { | |
| 203 firebase = fixture('ChangingChildren'); | |
| 204 waitForEvent(firebase, 'firebase-value').then(function() { | |
| 205 done(); | |
| 206 }).catch(function(e) { | |
| 207 done(e) | |
| 208 }); | |
| 209 }); | |
| 210 | |
| 211 test('adds a new child based on local changes'); | |
| 212 | |
| 213 test('updates the child key in place with the new value', function(done)
{ | |
| 214 var childrenKeys = []; | |
| 215 | |
| 216 waitForEvent(firebase, 'firebase-value').then(function() { | |
| 217 var middleValue = firebase.getByKey(childrenKeys[1]); | |
| 218 var changes; | |
| 219 | |
| 220 expect(middleValue.foo).to.be.equal(1); | |
| 221 expect(middleValue.bar).to.be.equal(1); | |
| 222 | |
| 223 changes = waitForEvent(firebase, 'firebase-child-changed'); | |
| 224 | |
| 225 firebase.set('data.' + firebase.data.indexOf(middleValue) + '.bar',
2); | |
| 226 | |
| 227 return changes; | |
| 228 }).then(function() { | |
| 229 var middleValue = firebase.getByKey(childrenKeys[1]); | |
| 230 | |
| 231 expect(middleValue.foo).to.be.equal(1); | |
| 232 expect(middleValue.bar).to.be.equal(2); | |
| 233 | |
| 234 done(); | |
| 235 }).catch(function(e) { | |
| 236 done(e); | |
| 237 }).then(function() { | |
| 238 childrenKeys.forEach(function(key) { | |
| 239 firebase.removeByKey(key); | |
| 240 }); | |
| 241 }); | |
| 242 | |
| 243 childrenKeys = [0, 1, 2].map(function(value, index) { | |
| 244 return firebase.add({ | |
| 245 foo: value, | |
| 246 bar: index | |
| 247 }).key(); | |
| 248 }); | |
| 249 }); | |
| 250 }); | |
| 251 | |
| 252 suite('syncing collections', function() { | |
| 253 var localFirebase; | |
| 254 var remoteFirebase; | |
| 255 | |
| 256 setup(function(done) { | |
| 257 firebase = fixture('SyncingCollections'); | |
| 258 localFirebase = firebase[0]; | |
| 259 remoteFirebase = firebase[1]; | |
| 260 Promise.all([ | |
| 261 waitForEvent(localFirebase, 'firebase-value'), | |
| 262 waitForEvent(remoteFirebase, 'firebase-value') | |
| 263 ]).then(function() { | |
| 264 done(); | |
| 265 }).catch(function(e) { | |
| 266 done(e); | |
| 267 }); | |
| 268 }); | |
| 269 | |
| 270 test('syncs a new item at the correct index', function(done) { | |
| 271 var data = { | |
| 272 foo: 100 | |
| 273 }; | |
| 274 var key; | |
| 275 | |
| 276 waitForEvent(remoteFirebase, 'firebase-value').then(function() { | |
| 277 var value = remoteFirebase.getByKey(key); | |
| 278 var lowValue = remoteFirebase.getByKey('lowValue'); | |
| 279 var highValue = remoteFirebase.getByKey('highValue'); | |
| 280 | |
| 281 var index = remoteFirebase.data.indexOf(value); | |
| 282 var lowIndex = remoteFirebase.data.indexOf(lowValue); | |
| 283 var highIndex = remoteFirebase.data.indexOf(highValue); | |
| 284 | |
| 285 expect(value).to.be.okay; | |
| 286 expect(index).to.be.lessThan(highIndex); | |
| 287 expect(index).to.be.greaterThan(lowIndex); | |
| 288 done(); | |
| 289 }).catch(function(e) { | |
| 290 done(e); | |
| 291 }).then(function() { | |
| 292 localFirebase.removeByKey(key); | |
| 293 }); | |
| 294 | |
| 295 key = localFirebase.add(data).key(); | |
| 296 }); | 269 }); |
| 297 }); | 270 }); |
| 298 }); | 271 }); |
| 272 |
| 299 </script> | 273 </script> |
| 300 | 274 |
| 301 </body> | 275 </body> |
| 302 </html> | 276 </html> |
| OLD | NEW |