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 |