/* * Copyright (c) 2016-present Invertase Limited & Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this library except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ const { PATH } = require('../helpers'); const TEST_PATH = `${PATH}/on`; describe('database().ref().on()', () => { it('throws if event type is invalid', async () => { try { await firebase .database() .ref() .on('foo'); return Promise.reject(new Error('Did not throw an Error.')); } catch (error) { error.message.should.containEql("'eventType' must be one of"); return Promise.resolve(); } }); it('throws if callback is not a function', async () => { try { await firebase .database() .ref() .on('value', 'foo'); return Promise.reject(new Error('Did not throw an Error.')); } catch (error) { error.message.should.containEql("'callback' must be a function"); return Promise.resolve(); } }); it('throws if cancel callback is not a function', async () => { try { await firebase .database() .ref() .on('value', () => {}, 'foo'); return Promise.reject(new Error('Did not throw an Error.')); } catch (error) { error.message.should.containEql("'cancelCallbackOrContext' must be a function or object"); return Promise.resolve(); } }); it('throws if context is not an object', async () => { try { await firebase .database() .ref() .on('value', () => {}, () => {}, 'foo'); return Promise.reject(new Error('Did not throw an Error.')); } catch (error) { error.message.should.containEql("'context' must be an object."); return Promise.resolve(); } }); it('should callback with an initial value', async () => { const callback = sinon.spy(); const ref = firebase.database().ref(`${TEST_PATH}/init`); const value = Date.now(); await ref.set(value); ref.on('value', $ => { callback($.val()); }); await Utils.sleep(500); callback.should.be.calledOnce(); callback.should.be.calledWith(value); ref.off('value'); }); it('should callback multiple times when the value changes', async () => { const callback = sinon.spy(); const ref = firebase.database().ref(`${TEST_PATH}/changes`); await ref.set('foo'); ref.on('value', $ => { callback($.val()); }); await Utils.sleep(500); await ref.set('bar'); await Utils.sleep(500); ref.off('value'); callback.should.be.calledTwice(); callback.getCall(0).args[0].should.equal('foo'); callback.getCall(1).args[0].should.equal('bar'); }); it('should cancel when something goes wrong', async () => { const successCallback = sinon.spy(); const cancelCallback = sinon.spy(); const ref = firebase.database().ref('nope'); ref.on( 'value', $ => { successCallback($.val()); }, error => { error.message.should.containEql( "Client doesn't have permission to access the desired data", ); cancelCallback(); }, ); await Utils.sleep(1200); // takes a while to call at times ref.off('value'); successCallback.should.be.callCount(0); cancelCallback.should.be.calledOnce(); }); it('subscribe to child added events', async () => { const successCallback = sinon.spy(); const cancelCallback = sinon.spy(); const ref = firebase.database().ref(`${TEST_PATH}/childAdded`); ref.on( 'child_added', $ => { successCallback($.val()); }, () => { cancelCallback(); }, ); await Utils.sleep(500); await ref.child('child1').set('foo'); await ref.child('child2').set('bar'); await Utils.sleep(500); ref.off('child_added'); successCallback.should.be.callCount(2); successCallback.getCall(0).args[0].should.equal('foo'); successCallback.getCall(1).args[0].should.equal('bar'); cancelCallback.should.be.callCount(0); }); it('subscribe to child changed events', async () => { const successCallback = sinon.spy(); const cancelCallback = sinon.spy(); const ref = firebase.database().ref(`${TEST_PATH}/childChanged`); const child = ref.child('changeme'); await child.set('foo'); ref.on( 'child_changed', $ => { successCallback($.val()); }, () => { cancelCallback(); }, ); const value1 = Date.now(); const value2 = Date.now() + 123; await Utils.sleep(500); await child.set(value1); await child.set(value2); await Utils.sleep(500); ref.off('child_changed'); successCallback.should.be.callCount(2); successCallback.getCall(0).args[0].should.equal(value1); successCallback.getCall(1).args[0].should.equal(value2); cancelCallback.should.be.callCount(0); }); it('subscribe to child removed events', async () => { const successCallback = sinon.spy(); const cancelCallback = sinon.spy(); const ref = firebase.database().ref(`${TEST_PATH}/childRemoved`); const child = ref.child('removeme'); await child.set('foo'); ref.on( 'child_removed', $ => { successCallback($.val()); }, () => { cancelCallback(); }, ); await Utils.sleep(500); await child.remove(); await Utils.sleep(500); ref.off('child_removed'); successCallback.should.be.callCount(1); successCallback.getCall(0).args[0].should.equal('foo'); cancelCallback.should.be.callCount(0); }); it('subscribe to child moved events', async () => { const callback = sinon.spy(); const ref = firebase.database().ref(`${TEST_PATH}/childMoved`); const orderedRef = ref.orderByChild('nuggets'); const initial = { alex: { nuggets: 60 }, rob: { nuggets: 56 }, vassili: { nuggets: 55.5 }, tony: { nuggets: 52 }, greg: { nuggets: 52 }, }; orderedRef.on('child_moved', $ => { // console.log($); callback($.val()); }); await ref.set(initial); await ref.child('greg/nuggets').set(57); await ref.child('rob/nuggets').set(61); await Utils.sleep(500); ref.off('child_moved'); callback.should.be.calledTwice(); callback.getCall(0).args[0].should.be.eql(jet.contextify({ nuggets: 57 })); callback.getCall(1).args[0].should.be.eql(jet.contextify({ nuggets: 61 })); }); });