[v6] Implement Realtime Database (#2195)

* [database] recreate database branch based off of #2185

* [database] cleanup linting issues

* [database] enable tests

* [database] add to tests deps
This commit is contained in:
Mike Diarmid
2019-06-04 15:25:35 +01:00
committed by Elliot Hesp
parent da9c32000d
commit fe959bb842
126 changed files with 12045 additions and 46 deletions

View File

@@ -0,0 +1,27 @@
/*
* 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.
*
*/
describe('database.X', () => {
describe('ServerValue.TIMESTAMP', () => {
it('returns a valid object', () => {
const { TIMESTAMP } = firebase.database.ServerValue;
should.equal(Object.keys(TIMESTAMP).length, 1);
TIMESTAMP.should.have.property('.sv');
TIMESTAMP['.sv'].should.eql('timestamp');
});
});
});

View File

@@ -0,0 +1,196 @@
/*
* 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.
*
*/
describe('database()', () => {
describe('namespace', () => {
it('accessible from firebase.app()', () => {
const app = firebase.app();
should.exist(app.database);
app.database().app.should.eql(app);
});
it('supports multiple apps', async () => {
firebase.database().app.name.should.eql('[DEFAULT]');
firebase
.database(firebase.app('secondaryFromNative'))
.app.name.should.eql('secondaryFromNative');
firebase
.app('secondaryFromNative')
.database()
.app.name.should.eql('secondaryFromNative');
});
});
it('supports custom database URL', async () => {
firebase.database().app.name.should.eql('[DEFAULT]');
firebase
.database(firebase.app('secondaryFromNative'))
.app.name.should.eql('secondaryFromNative');
firebase
.app('secondaryFromNative')
.database()
.app.name.should.eql('secondaryFromNative');
});
describe('ref()', () => {
it('throws if path is not a string', async () => {
try {
firebase.database().ref({ foo: 'bar' });
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'path' must be a string value`);
return Promise.resolve();
}
});
it('throws if path is not a valid string', async () => {
try {
firebase.database().ref('$$$$$');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`Paths must be non-empty strings and can't contain ".", "#", "$", "[", or "]"`,
);
return Promise.resolve();
}
});
});
describe('refFromURL()', () => {
it('throws if url is not a url', async () => {
try {
firebase.database().refFromURL('foobar');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'url' must be a valid database URL`);
return Promise.resolve();
}
});
it('throws if url from a different domain', async () => {
try {
firebase.database().refFromURL('https://foobar.firebaseio.com');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'url' must be the same domain as the current instance`);
return Promise.resolve();
}
});
it('returns a reference', async () => {
const ref1 = firebase.database().refFromURL(firebase.database()._customUrlOrRegion);
const ref2 = firebase
.database()
.refFromURL(`${firebase.database()._customUrlOrRegion}/foo/bar`);
const ref3 = firebase
.database()
.refFromURL(`${firebase.database()._customUrlOrRegion}/foo/bar?baz=foo`);
should.equal(ref1.path, '/');
should.equal(ref2.path, 'foo/bar');
should.equal(ref3.path, 'foo/bar');
});
});
describe('goOnline()', () => {
it('calls goOnline successfully', async () => {
await firebase.database().goOnline();
});
});
describe('goOffline()', () => {
it('calls goOffline successfully', async () => {
// await Utils.sleep(5000);
await firebase.database().goOffline();
await firebase.database().goOnline();
});
});
describe('setPersistenceEnabled()', () => {
it('throws if enabled is not a boolean', async () => {
try {
firebase.database().setPersistenceEnabled('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'enabled' must be a boolean value`);
return Promise.resolve();
}
});
it('calls setPersistenceEnabled successfully', async () => {
firebase.database().setPersistenceEnabled(true);
firebase.database().setPersistenceEnabled(false);
});
});
describe('setLoggingEnabled()', () => {
it('throws if enabled is not a boolean', async () => {
try {
firebase.database().setLoggingEnabled('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'enabled' must be a boolean value`);
return Promise.resolve();
}
});
it('calls setLoggingEnabled successfully', async () => {
firebase.database().setLoggingEnabled(true);
firebase.database().setLoggingEnabled(false);
});
});
describe('setPersistenceCacheSizeBytes()', () => {
it('throws if bytes is not a number', async () => {
try {
firebase.database().setPersistenceCacheSizeBytes('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'bytes' must be a number value`);
return Promise.resolve();
}
});
it('throws if bytes is less than 1MB', async () => {
try {
firebase.database().setPersistenceCacheSizeBytes(1234);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'bytes' must be greater than 1000000 (1MB)`);
return Promise.resolve();
}
});
it('throws if bytes is greater than 10MB', async () => {
try {
firebase.database().setPersistenceCacheSizeBytes(100000000000000);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'bytes' must be less than 100000000 (10MB)`);
return Promise.resolve();
}
});
it('calls setPersistenceCacheSizeBytes successfully', async () => {
firebase.database().setPersistenceCacheSizeBytes(1000000);
});
});
});

View File

@@ -0,0 +1,54 @@
// TODO make more unique?
const ID = Date.now();
const PATH = `tests/${ID}`;
const CONTENT = {
TYPES: {
array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
boolean: true,
string: 'foobar',
number: 123567890,
object: {
foo: 'bar',
bar: 'baz',
},
},
QUERY: {
a: {
string: 'foo',
number: 10,
},
b: {
string: 'bar',
number: 5,
},
c: {
string: 'baz',
number: 8,
},
},
};
exports.seed = function seed(path) {
return Promise.all([
firebase
.database()
.ref(`${path}/types`)
.set(CONTENT.TYPES),
firebase
.database()
.ref(`${path}/query`)
.set(CONTENT.QUERY),
]);
};
exports.wipe = function wipe(path) {
return firebase
.database()
.ref(path)
.remove();
};
exports.PATH = PATH;
exports.CONTENT = CONTENT;

View File

@@ -0,0 +1,62 @@
/*
* 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.
*
*/
describe(`database().ref('.info/connected')`, () => {
after(() => firebase.database().goOnline());
it('returns false when used with once', async () => {
const snapshot = await firebase
.database()
.ref('.info/connected')
.once('value');
snapshot.val().should.equal(false);
});
it('returns true when used with once with a previous call', async () => {
await firebase
.database()
.ref('tests')
.once('value');
const snapshot = await firebase
.database()
.ref('.info/connected')
.once('value');
snapshot.val().should.equal(true);
});
it('subscribes to online state', async () => {
const callback = sinon.spy();
await firebase.database().goOffline();
const ref = firebase.database().ref('.info/connected');
const handler = $ => {
callback($.val());
};
ref.on('value', handler);
await Utils.sleep(1000);
await firebase.database().goOnline();
await Utils.sleep(1000);
ref.off('value', handler);
callback.should.be.calledTwice();
callback.getCall(0).args[0].should.equal(false);
callback.getCall(1).args[0].should.equal(true);
});
});

View File

@@ -0,0 +1,27 @@
/*
* 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.
*
*/
describe(`database().ref('.info/serverTimeOffset')`, () => {
it('returns a valid number value', async () => {
const snapshot = await firebase
.database()
.ref('.info/serverTimeOffset')
.once('value');
should.equal(typeof snapshot.val(), 'number');
});
});

View File

@@ -0,0 +1,73 @@
/*
* 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, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/onDisconnectCancel`;
describe('database().ref().onDisconnect().cancel()', () => {
after(() => wipe(TEST_PATH));
afterEach(() => {
// Ensures the db is online before running each test
firebase.database().goOnline();
});
it('throws if onComplete is not a function', () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.onDisconnect();
try {
ref.cancel('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('cancels all previously queued events', async () => {
const ref = firebase.database().ref(TEST_PATH);
await ref.set('foobar');
const value = Date.now();
await ref.onDisconnect().set(value);
await ref.onDisconnect().cancel();
await firebase.database().goOffline();
await firebase.database().goOnline();
const snapshot = await ref.once('value');
snapshot.val().should.eql('foobar');
});
it('calls back to the onComplete function', async () => {
const callback = sinon.spy();
const ref = firebase.database().ref(TEST_PATH);
// Set an initial value
await ref.set('foo');
await ref.onDisconnect().set('bar');
await ref.onDisconnect().cancel(callback);
await firebase.database().goOffline();
await firebase.database().goOnline();
callback.should.be.calledOnce();
});
});

View File

@@ -0,0 +1,76 @@
/*
* 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, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/onDisconnectRemove`;
describe('database().ref().onDisconnect().remove()', () => {
after(() => wipe(TEST_PATH));
afterEach(() => {
// Ensures the db is online before running each test
firebase.database().goOnline();
});
it('throws if onComplete is not a function', () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.onDisconnect();
try {
ref.remove('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('removes a node whilst offline', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('removeMe');
await ref.set('foobar');
await ref.onDisconnect().remove();
await firebase.database().goOffline();
await firebase.database().goOnline();
const snapshot = await ref.once('value');
snapshot.exists().should.eql(false);
});
it('calls back to the onComplete function', async () => {
const callback = sinon.spy();
const ref = firebase
.database()
.ref(TEST_PATH)
.child('removeMe');
// Set an initial value
await ref.set('foo');
await ref.onDisconnect().remove(callback);
await firebase.database().goOffline();
await firebase.database().goOnline();
callback.should.be.calledOnce();
});
});

View File

@@ -0,0 +1,84 @@
/*
* 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, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/onDisconnectSet`;
describe('database().ref().onDisconnect().set()', () => {
after(() => wipe(TEST_PATH));
afterEach(() => {
// Ensures the db is online before running each test
firebase.database().goOnline();
});
it('throws if value is not a defined', () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.onDisconnect();
try {
ref.set();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'value' must be defined`);
return Promise.resolve();
}
});
it('throws if onComplete is not a function', () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.onDisconnect();
try {
ref.set(null, 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('sets value when disconnected', async () => {
const ref = firebase.database().ref(TEST_PATH);
const value = Date.now();
await ref.onDisconnect().set(value);
await firebase.database().goOffline();
await firebase.database().goOnline();
const snapshot = await ref.once('value');
snapshot.val().should.eql(value);
});
it('calls back to the onComplete function', async () => {
const callback = sinon.spy();
const ref = firebase.database().ref(TEST_PATH);
// Set an initial value
await ref.set('foo');
await ref.onDisconnect().set('bar', callback);
await firebase.database().goOffline();
await firebase.database().goOnline();
callback.should.be.calledOnce();
});
});

View File

@@ -0,0 +1,99 @@
/*
* 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, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/onDisconnectSetWithPriority`;
describe('database().ref().onDisconnect().setWithPriority()', () => {
after(() => wipe(TEST_PATH));
afterEach(() => {
// Ensures the db is online before running each test
firebase.database().goOnline();
});
it('throws if value is not a defined', () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.onDisconnect();
try {
ref.setWithPriority();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'value' must be defined`);
return Promise.resolve();
}
});
it('throws if priority is not a valid type', () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.onDisconnect();
try {
ref.setWithPriority(null, { foo: 'bar' });
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'priority' must be a number, string or null value`);
return Promise.resolve();
}
});
it('throws if onComplete is not a function', () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.onDisconnect();
try {
ref.setWithPriority(null, 1, 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('sets value with priority when disconnected', async () => {
const ref = firebase.database().ref(TEST_PATH);
const value = Date.now();
await ref.onDisconnect().setWithPriority(value, 3);
await firebase.database().goOffline();
await firebase.database().goOnline();
const snapshot = await ref.once('value');
snapshot.exportVal()['.value'].should.eql(value);
snapshot.exportVal()['.priority'].should.eql(3);
});
it('calls back to the onComplete function', async () => {
const callback = sinon.spy();
const ref = firebase.database().ref(TEST_PATH);
// Set an initial value
await ref.set('foo');
await ref.onDisconnect().setWithPriority('bar', 2, callback);
await firebase.database().goOffline();
await firebase.database().goOnline();
callback.should.be.calledOnce();
});
});

View File

@@ -0,0 +1,111 @@
/*
* 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, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/onDisconnectUpdate`;
describe('database().ref().onDisconnect().update()', () => {
after(() => wipe(TEST_PATH));
afterEach(() => {
// Ensures the db is online before running each test
firebase.database().goOnline();
});
it('throws if values is not an object', async () => {
try {
await firebase
.database()
.ref(TEST_PATH)
.onDisconnect()
.update('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'values' must be an object`);
return Promise.resolve();
}
});
it('throws if values does not contain any values', async () => {
try {
await firebase
.database()
.ref(TEST_PATH)
.onDisconnect()
.update({});
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'values' must be an object containing multiple values`);
return Promise.resolve();
}
});
it('throws if onComplete is not a function', () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.onDisconnect();
try {
ref.update({ foo: 'bar' }, 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('updates value when disconnected', async () => {
const ref = firebase.database().ref(TEST_PATH);
const value = Date.now();
await ref.set({
foo: {
bar: 'baz',
},
});
await ref
.child('foo')
.onDisconnect()
.update({
bar: value,
});
await firebase.database().goOffline();
await firebase.database().goOnline();
const snapshot = await ref.child('foo').once('value');
snapshot.val().should.eql(
jet.contextify({
bar: value,
}),
);
});
it('calls back to the onComplete function', async () => {
const callback = sinon.spy();
const ref = firebase.database().ref(TEST_PATH);
// Set an initial value
await ref.set('foo');
await ref.onDisconnect().update({ foo: 'bar' }, callback);
await firebase.database().goOffline();
await firebase.database().goOnline();
callback.should.be.calledOnce();
});
});

View File

@@ -0,0 +1,156 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/endAt`;
describe('database().ref().endAt()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if an value is undefined', async () => {
try {
await firebase
.database()
.ref()
.endAt();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'value' must be a number, string, boolean or null value`);
return Promise.resolve();
}
});
it('throws if an key is not a string', async () => {
try {
await firebase
.database()
.ref()
.endAt('foo', 1234);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'key' must be a string value if defined`);
return Promise.resolve();
}
});
it('throws if a ending point has already been set', async () => {
try {
await firebase
.database()
.ref()
.equalTo('foo')
.endAt('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`Ending point was already set (by another call to endAt or equalTo)`,
);
return Promise.resolve();
}
});
it('throws if ordering by key and the key param is set', async () => {
try {
await firebase
.database()
.ref()
.orderByKey('foo')
.endAt('foo', 'bar');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`When ordering by key, you may only pass a value argument to startAt(), endAt(), or equalTo()`,
);
return Promise.resolve();
}
});
it('throws if ordering by key and the value param is not a string', async () => {
try {
await firebase
.database()
.ref()
.orderByKey('foo')
.endAt(123);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`When ordering by key, the value of startAt(), endAt(), or equalTo() must be a string`,
);
return Promise.resolve();
}
});
it('throws if ordering by priority and the value param is not priority type', async () => {
try {
await firebase
.database()
.ref()
.orderByPriority()
.endAt(true);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`When ordering by priority, the first value of startAt(), endAt(), or equalTo() must be a valid priority value (null, a number, or a string)`,
);
return Promise.resolve();
}
});
it('snapshot value returns all when no ordering modifier is applied', async () => {
const ref = firebase.database().ref(TEST_PATH);
await ref.set({
a: 1,
b: 2,
c: 3,
d: 4,
});
const expected = ['a', 'b', 'c', 'd'];
const snapshot = await ref.endAt(2).once('value');
snapshot.forEach((childSnapshot, i) => {
childSnapshot.key.should.eql(expected[i]);
});
});
it('ends at the correct value', async () => {
const ref = firebase.database().ref(TEST_PATH);
await ref.set({
a: 1,
b: 2,
c: 3,
d: 4,
});
const snapshot = await ref
.orderByValue()
.endAt(2)
.once('value');
const expected = ['a', 'b'];
snapshot.forEach((childSnapshot, i) => {
childSnapshot.key.should.eql(expected[i]);
});
});
});

View File

@@ -0,0 +1,120 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/equalTo`;
describe('database().ref().equalTo()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if value is not a valid type', async () => {
try {
await firebase
.database()
.ref()
.equalTo({ foo: 'bar' });
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'value' must be a number, string, boolean or null value`);
return Promise.resolve();
}
});
it('throws if key is not a string', async () => {
try {
await firebase
.database()
.ref()
.equalTo('bar', 123);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'key' must be a string value if defined`);
return Promise.resolve();
}
});
it('throws if a starting point has already been set', async () => {
try {
await firebase
.database()
.ref()
.startAt('foo')
.equalTo('bar');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`Starting point was already set (by another call to startAt or equalTo)`,
);
return Promise.resolve();
}
});
it('throws if a ending point has already been set', async () => {
try {
await firebase
.database()
.ref()
.endAt('foo')
.equalTo('bar');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`Ending point was already set (by another call to endAt or equalTo)`,
);
return Promise.resolve();
}
});
it('snapshot value is null when no ordering modifier is applied', async () => {
const ref = firebase.database().ref(TEST_PATH);
await ref.set({
a: 1,
b: 2,
c: 3,
d: 4,
});
const snapshot = await ref.equalTo(2).once('value');
should.equal(snapshot.val(), null);
});
it('returns the correct equal to values', async () => {
const ref = firebase.database().ref(TEST_PATH);
await ref.set({
a: 1,
b: 2,
c: 3,
d: 4,
e: 2,
});
const snapshot = await ref
.orderByValue()
.equalTo(2)
.once('value');
const expected = ['b', 'e'];
snapshot.forEach((childSnapshot, i) => {
childSnapshot.key.should.eql(expected[i]);
});
});
});

View File

@@ -0,0 +1,62 @@
/*
* 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.
*
*/
describe('database().ref().isEqual()', () => {
it('throws if limit other param is not a query instance', async () => {
try {
await firebase
.database()
.ref()
.isEqual('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'other' must be an instance of Query.`);
return Promise.resolve();
}
});
it('returns true if the query is the same instance', async () => {
const query = await firebase.database().ref();
const same = query.isEqual(query);
same.should.eql(true);
});
it('returns false if the query is different', async () => {
const query = await firebase.database().ref();
const other = await firebase
.database()
.ref()
.limitToLast(2);
const same = query.isEqual(other);
same.should.eql(false);
});
it('returns true if the query is created differently', async () => {
const query = await firebase
.database()
.ref()
.limitToFirst(1)
.orderByChild('foo');
const other = await firebase
.database()
.ref()
.orderByChild('foo')
.limitToFirst(1);
const same = query.isEqual(other);
same.should.eql(true);
});
});

View File

@@ -0,0 +1,40 @@
/*
* 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.
*
*/
describe('database().ref().keepSynced()', () => {
it('throws if bool is not a valid type', async () => {
try {
await firebase
.database()
.ref()
.keepSynced('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'bool' value must be a boolean value.`);
return Promise.resolve();
}
});
it('toggles keepSynced on and off without throwing', async () => {
const ref = firebase
.database()
.ref('noop')
.orderByValue();
await ref.keepSynced(true);
await ref.keepSynced(false);
});
});

View File

@@ -0,0 +1,115 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/limitToFirst`;
describe('database().ref().limitToFirst()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if limit is invalid', async () => {
try {
await firebase
.database()
.ref()
.limitToFirst('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'limit' must be a positive integer value`);
return Promise.resolve();
}
});
it('throws if limit has already been set', async () => {
try {
await firebase
.database()
.ref()
.limitToLast(2)
.limitToFirst(3);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`Limit was already set (by another call to limitToFirst, or limitToLast)`,
);
return Promise.resolve();
}
});
it('returns a limited array data set', async () => {
const ref = firebase.database().ref(`${TEST_PATH}`);
const initial = {
0: 'foo',
1: 'bar',
2: 'baz',
};
await ref.set(initial);
return ref
.limitToFirst(2)
.once('value')
.then(snapshot => {
snapshot.val().should.eql(jet.contextify(['foo', 'bar']));
return Promise.resolve();
});
});
it('returns a limited object data set', async () => {
const ref = firebase.database().ref(`${TEST_PATH}`);
const initial = {
a: 'foo',
b: 'bar',
c: 'baz',
};
await ref.set(initial);
return ref
.limitToFirst(2)
.once('value')
.then(snapshot => {
snapshot.val().should.eql(
jet.contextify({
a: 'foo',
b: 'bar',
}),
);
return Promise.resolve();
});
});
it('returns a null value when not possible to limit', async () => {
const ref = firebase.database().ref(`${TEST_PATH}`);
const initial = 'foo';
await ref.set(initial);
return ref
.limitToFirst(2)
.once('value')
.then(snapshot => {
should.equal(snapshot.val(), null);
return Promise.resolve();
});
});
});

View File

@@ -0,0 +1,115 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/limitToLast`;
describe('database().ref().limitToLast()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if limit is invalid', async () => {
try {
await firebase
.database()
.ref()
.limitToLast('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'limit' must be a positive integer value`);
return Promise.resolve();
}
});
it('throws if limit has already been set', async () => {
try {
await firebase
.database()
.ref()
.limitToFirst(3)
.limitToLast(2);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`Limit was already set (by another call to limitToFirst, or limitToLast)`,
);
return Promise.resolve();
}
});
it('returns a limited array data set', async () => {
const ref = firebase.database().ref(`${TEST_PATH}`);
const initial = {
0: 'foo',
1: 'bar',
2: 'baz',
};
await ref.set(initial);
return ref
.limitToLast(2)
.once('value')
.then(snapshot => {
snapshot.val().should.eql(jet.contextify([null, 'bar', 'baz']));
return Promise.resolve();
});
});
it('returns a limited object data set', async () => {
const ref = firebase.database().ref(`${TEST_PATH}`);
const initial = {
a: 'foo',
b: 'bar',
c: 'baz',
};
await ref.set(initial);
return ref
.limitToLast(2)
.once('value')
.then(snapshot => {
snapshot.val().should.eql(
jet.contextify({
b: 'bar',
c: 'baz',
}),
);
return Promise.resolve();
});
});
it('returns a null value when not possible to limit', async () => {
const ref = firebase.database().ref(`${TEST_PATH}`);
const initial = 'foo';
await ref.set(initial);
return ref
.limitToFirst(2)
.once('value')
.then(snapshot => {
should.equal(snapshot.val(), null);
return Promise.resolve();
});
});
});

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1,244 @@
/*
* 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 }));
});
});

View File

@@ -0,0 +1,178 @@
/*
* 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, CONTENT, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/once`;
describe('database().ref().once()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if event type is invalid', async () => {
try {
await firebase
.database()
.ref()
.once('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 success callback is not a function', async () => {
try {
await firebase
.database()
.ref()
.once('value', 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'successCallBack' must be a function`);
return Promise.resolve();
}
});
it('throws if failure callback is not a function', async () => {
try {
await firebase
.database()
.ref()
.once('value', () => {}, 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'failureCallbackOrContext' must be a function or context`);
return Promise.resolve();
}
});
it('throws if context is not an object', async () => {
try {
await firebase
.database()
.ref()
.once('value', () => {}, () => {}, 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'context' must be a context object.`);
return Promise.resolve();
}
});
it('returns a promise', async () => {
const ref = firebase.database().ref('tests/types/number');
const returnValue = ref.once('value');
returnValue.should.be.Promise();
});
it('resolves with the correct values', async () => {
const ref = firebase.database().ref(`${TEST_PATH}/types`);
await Promise.all(
Object.keys(CONTENT.TYPES).map(async key => {
const value = CONTENT.TYPES[key];
const snapsnot = await ref.child(key).once('value');
snapsnot.val().should.eql(jet.contextify(value));
}),
);
});
it('is is called when the value is changed', async () => {
const callback = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/types/number`);
ref.once('value').then(callback);
await ref.set(1337);
callback.should.be.calledOnce();
});
it('errors if permission denied', async () => {
const ref = firebase.database().ref('nope');
try {
await ref.once('value');
return Promise.reject(new Error('No permission denied error'));
} catch (error) {
error.code.includes('database/permission-denied').should.be.true();
return Promise.resolve();
}
});
it('it calls when a child is added', async () => {
const value = Date.now();
const callback = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/childAdded`);
ref.once('child_added').then($ => callback($.val()));
await ref.child('foo').set(value);
await Utils.sleep(100);
callback.should.be.calledOnce();
callback.should.be.calledWith(value);
});
it('resolves when a child is changed', async () => {
const callback = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/childChanged`);
await ref.child('foo').set(1);
ref.once('child_changed').then($ => callback($.val()));
await Utils.sleep(300);
await ref.child('foo').set(2);
await Utils.sleep(300);
callback.should.be.calledOnce();
callback.should.be.calledWith(2);
});
it('resolves when a child is removed', async () => {
const callback = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/childRemoved`);
const child = ref.child('removeme');
await child.set('foo');
ref.once('child_removed').then($ => callback($.val()));
await Utils.sleep(300);
await child.remove();
await Utils.sleep(300);
callback.should.be.calledOnce();
callback.should.be.calledWith('foo');
});
// https://github.com/firebase/firebase-js-sdk/blob/6b53e0058483c9002d2fe56119f86fc9fb96b56c/packages/database/test/order_by.test.ts#L104
it('resolves when a child is moved', 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.once('child_moved').then($ => callback($.val()));
await ref.set(initial);
await ref.child('greg/nuggets').set(57);
await Utils.sleep(100);
callback.should.be.calledOnce();
callback.should.be.calledWith({ nuggets: 57 });
});
});

View File

@@ -0,0 +1,86 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/orderByChild`;
describe('database().ref().orderByChild()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if path is not a string value', async () => {
try {
await firebase
.database()
.ref()
.orderByChild({ foo: 'bar' });
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'path' must be a string value`);
return Promise.resolve();
}
});
it('throws if path is an empty path', async () => {
try {
await firebase
.database()
.ref()
.orderByChild('/');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'path' cannot be empty. Use orderByValue instead`);
return Promise.resolve();
}
});
it('throws if an orderBy call has already been set', async () => {
try {
await firebase
.database()
.ref()
.orderByKey()
.orderByChild('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`You can't combine multiple orderBy calls`);
return Promise.resolve();
}
});
it('order by a child value', async () => {
const ref = firebase.database().ref(TEST_PATH);
try {
const snapshot = await ref
.child('query')
.orderByChild('number')
.once('value');
const expected = ['b', 'c', 'a'];
snapshot.forEach((childSnapshot, i) => {
childSnapshot.key.should.eql(expected[i]);
});
return Promise.resolve();
} catch (error) {
throw error;
}
});
});

View File

@@ -0,0 +1,60 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/orderByKey`;
describe('database().ref().orderByKey()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if an orderBy call has already been set', async () => {
try {
await firebase
.database()
.ref()
.orderByChild('foo')
.orderByKey();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`You can't combine multiple orderBy calls`);
return Promise.resolve();
}
});
it('order by a key', async () => {
const ref = firebase.database().ref(TEST_PATH);
try {
const snapshot = await ref
.child('query')
.orderByKey()
.once('value');
const expected = ['a', 'b', 'c'];
snapshot.forEach((childSnapshot, i) => {
childSnapshot.key.should.eql(expected[i]);
});
return Promise.resolve();
} catch (error) {
throw error;
}
});
});

View File

@@ -0,0 +1,66 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/orderByPriority`;
describe('database().ref().orderByPriority()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if an orderBy call has already been set', async () => {
try {
await firebase
.database()
.ref()
.orderByChild('foo')
.orderByPriority();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`You can't combine multiple orderBy calls`);
return Promise.resolve();
}
});
it('order by priority', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('query');
await Promise.all([
ref.child('a').setPriority(2),
ref.child('b').setPriority(3),
ref.child('c').setPriority(1),
]);
try {
const snapshot = await ref.orderByPriority().once('value');
const expected = ['c', 'a', 'b'];
snapshot.forEach((childSnapshot, i) => {
childSnapshot.key.should.eql(expected[i]);
});
return Promise.resolve();
} catch (error) {
throw error;
}
});
});

View File

@@ -0,0 +1,66 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/orderByValue`;
describe('database().ref().orderByValue()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if an orderBy call has already been set', async () => {
try {
await firebase
.database()
.ref()
.orderByChild('foo')
.orderByValue();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`You can't combine multiple orderBy calls`);
return Promise.resolve();
}
});
it('order by value', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('query');
await ref.set({
a: 2,
b: 3,
c: 1,
});
try {
const snapshot = await ref.orderByValue().once('value');
const expected = ['c', 'a', 'b'];
snapshot.forEach((childSnapshot, i) => {
childSnapshot.key.should.eql(expected[i]);
});
return Promise.resolve();
} catch (error) {
throw error;
}
});
});

View File

@@ -0,0 +1,151 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/startAt`;
describe('database().ref().startAt()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if an value is undefined', async () => {
try {
await firebase
.database()
.ref()
.startAt();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'value' must be a number, string, boolean or null value`);
return Promise.resolve();
}
});
it('throws if an key is not a string', async () => {
try {
await firebase
.database()
.ref()
.startAt('foo', 1234);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'key' must be a string value if defined`);
return Promise.resolve();
}
});
it('throws if a starting point has already been set', async () => {
try {
await firebase
.database()
.ref()
.equalTo('foo')
.startAt('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`Starting point was already set (by another call to startAt or equalTo)`,
);
return Promise.resolve();
}
});
it('throws if ordering by key and the key param is set', async () => {
try {
await firebase
.database()
.ref()
.orderByKey('foo')
.startAt('foo', 'bar');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`When ordering by key, you may only pass a value argument to startAt(), endAt(), or equalTo()`,
);
return Promise.resolve();
}
});
it('throws if ordering by key and the value param is not a string', async () => {
try {
await firebase
.database()
.ref()
.orderByKey('foo')
.startAt(123);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`When ordering by key, the value of startAt(), endAt(), or equalTo() must be a string`,
);
return Promise.resolve();
}
});
it('throws if ordering by priority and the value param is not priority type', async () => {
try {
await firebase
.database()
.ref()
.orderByPriority()
.startAt(true);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
`When ordering by priority, the first value of startAt(), endAt(), or equalTo() must be a valid priority value (null, a number, or a string)`,
);
return Promise.resolve();
}
});
it('snapshot value is null when no ordering modifier is applied', async () => {
const ref = firebase.database().ref(TEST_PATH);
await ref.set({
a: 1,
b: 2,
c: 3,
d: 4,
});
const snapshot = await ref.startAt(2).once('value');
should.equal(snapshot.val(), null);
});
it('starts at the correct value', async () => {
const ref = firebase.database().ref(TEST_PATH);
await ref.set({
a: 1,
b: 2,
c: 3,
d: 4,
});
const snapshot = await ref
.orderByValue()
.startAt(2)
.once('value');
const expected = ['b', 'c', 'd'];
snapshot.forEach((childSnapshot, i) => {
childSnapshot.key.should.eql(expected[i]);
});
});
});

View File

@@ -0,0 +1,27 @@
/*
* 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.
*
*/
describe('database().ref().toJSON()', () => {
it('returns a string version of the current query path', async () => {
const res = firebase
.database()
.ref('foo/bar/baz')
.toJSON();
const expected = `${firebase.database()._customUrlOrRegion}/foo/bar/baz`;
should.equal(res, expected);
});
});

View File

@@ -0,0 +1,44 @@
/*
* 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.
*
*/
describe('database().ref().child()', () => {
it('throws if path is not a string', async () => {
try {
firebase
.database()
.ref()
.child({ foo: 'bar' });
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'path' must be a string value`);
return Promise.resolve();
}
});
it('throws if path is not a valid string', async () => {
try {
firebase
.database()
.ref()
.child('$$$$$');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`Paths must be non-empty strings and can't contain`);
return Promise.resolve();
}
});
});

View File

@@ -0,0 +1,30 @@
/*
* 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.
*
*/
describe('database().ref().key', () => {
it('returns null when no reference path is provides', () => {
const ref = firebase.database().ref();
should.equal(ref.key, null);
});
it('return last token in reference path', () => {
const ref1 = firebase.database().ref('foo');
const ref2 = firebase.database().ref('foo/bar/baz');
ref1.key.should.equal('foo');
ref2.key.should.equal('baz');
});
});

View File

@@ -0,0 +1,28 @@
/*
* 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.
*
*/
// See onDisconnect directory for specific tests
describe('database().ref().onDisconnect()', () => {
it('returns a new DatabaseOnDisconnect instance', () => {
const instance = firebase
.database()
.ref()
.onDisconnect();
instance.constructor.name.should.eql('DatabaseOnDisconnect');
});
});

View File

@@ -0,0 +1,30 @@
/*
* 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.
*
*/
describe('database().ref().parent', () => {
it('returns null when no reference path is provides', () => {
const ref = firebase.database().ref();
should.equal(ref.parent, null);
});
it('return last token in reference path', () => {
const ref1 = firebase.database().ref('/foo').parent;
const ref2 = firebase.database().ref('/foo/bar/baz').parent;
should.equal(ref1, null);
ref2.key.should.equal('bar');
});
});

View File

@@ -0,0 +1,26 @@
/*
* 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, seed, wipe } = require('../helpers');
// const TEST_PATH = `${PATH}/push`;
xdescribe('database().ref().push()', () => {
// before(() => seed(TEST_PATH));
// after(() => wipe(TEST_PATH));
// TODO
});

View File

@@ -0,0 +1,43 @@
/*
* 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}/remove`;
describe('database().ref().remove()', () => {
it('throws if onComplete is not a function', async () => {
try {
await firebase
.database()
.ref(TEST_PATH)
.remove('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('removes a value at the path', async () => {
const ref = firebase.database().ref(TEST_PATH);
await ref.set('foo');
await ref.remove();
const snapshot = await ref.once('value');
snapshot.exists().should.equal(false);
});
});

View File

@@ -0,0 +1,23 @@
/*
* 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.
*
*/
describe('database().ref().root', () => {
it('returns a root reference', () => {
const ref = firebase.database().ref('foo/bar/baz');
should.equal(ref.root.key, null);
});
});

View File

@@ -0,0 +1,83 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/set`;
describe('database().ref().set()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if no value is provided', async () => {
try {
await firebase
.database()
.ref(TEST_PATH)
.set();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'value' must be defined`);
return Promise.resolve();
}
});
it('throws if onComplete is not a function', async () => {
try {
await firebase
.database()
.ref(TEST_PATH)
.set(null, 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('sets a new value', async () => {
const value = Date.now();
const ref = firebase.database().ref(TEST_PATH);
await ref.set(value);
const snapshot = await ref.once('value');
snapshot.val().should.eql(value);
});
it('callback if function is passed', async () => {
const value = Date.now();
return new Promise(async resolve => {
await firebase
.database()
.ref(TEST_PATH)
.set(value, resolve);
});
});
it('throws if permission defined', async () => {
const value = Date.now();
try {
await firebase
.database()
.ref('nope/foo')
.set(value);
return Promise.reject(new Error('Did not throw error.'));
} catch (error) {
error.code.includes('database/permission-denied').should.be.true();
return Promise.resolve();
}
});
});

View File

@@ -0,0 +1,101 @@
/*
* 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, CONTENT, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/priority`;
describe('database().ref().setPriority()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if priority is not a valid type', async () => {
try {
await firebase
.database()
.ref()
.setPriority({});
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'priority' must be a number, string or null value`);
return Promise.resolve();
}
});
it('throws if onComplete is not a function', async () => {
try {
await firebase
.database()
.ref()
.setPriority(null, 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('should correctly set a priority for all non-null values', async () => {
await Promise.all(
Object.keys(CONTENT.TYPES).map(async dataRef => {
const ref = firebase.database().ref(`${TEST_PATH}/types/${dataRef}`);
await ref.setPriority(1);
const snapshot = await ref.once('value');
if (snapshot.val() !== null) {
snapshot.getPriority().should.eql(1);
}
}),
);
});
it('callback if function is passed', async () => {
const value = Date.now();
return new Promise(async resolve => {
await firebase
.database()
.ref(`${TEST_PATH}/types/string`)
.set(value, resolve);
});
});
it('throws if setting priority on non-existent node', async () => {
try {
await firebase
.database()
.ref('tests/siudfhsuidfj')
.setPriority(1);
return Promise.reject(new Error('Did not throw error.'));
} catch (error) {
// WEB SDK: INVALID_PARAMETERS: could not set priority on non-existent node
// TODO Get this error? Native code = -999 Unknown
return Promise.resolve();
}
});
it('throws if permission defined', async () => {
try {
await firebase
.database()
.ref('nope/foo')
.setPriority(1);
return Promise.reject(new Error('Did not throw error.'));
} catch (error) {
error.code.includes('database/permission-denied').should.be.true();
return Promise.resolve();
}
});
});

View File

@@ -0,0 +1,83 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/setWithPriority`;
describe('database().ref().setWithPriority()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if newVal is not defined', async () => {
try {
await firebase
.database()
.ref()
.setWithPriority();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'newVal' must be defined`);
return Promise.resolve();
}
});
it('throws if newPriority is incorrect type', async () => {
try {
await firebase
.database()
.ref()
.setWithPriority(null, { foo: 'bar' });
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'newPriority' must be a number, string or null value`);
return Promise.resolve();
}
});
it('throws if onComplete is not a function', async () => {
try {
await firebase
.database()
.ref()
.setWithPriority(null, null, 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('callback if function is passed', async () => {
const value = Date.now();
return new Promise(async resolve => {
await firebase
.database()
.ref(`${TEST_PATH}/setValueWithCallback`)
.setWithPriority(value, 2, resolve);
});
});
it('sets with a new value and priority', async () => {
const value = Date.now();
const ref = firebase.database().ref(`${TEST_PATH}/setValue`);
await ref.setWithPriority(value, 2);
const snapshot = await ref.once('value');
snapshot.val().should.eql(value);
snapshot.getPriority().should.eql(2);
});
});

View File

@@ -0,0 +1,217 @@
/*
* 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, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/transaction`;
const NOOP = () => {};
describe('database().ref().transaction()', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('throws if no transactionUpdate is provided', async () => {
try {
await firebase
.database()
.ref(TEST_PATH)
.transaction();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'transactionUpdate' must be a function`);
return Promise.resolve();
}
});
it('throws if onComplete is not a function', async () => {
try {
await firebase
.database()
.ref()
.transaction(NOOP, 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('throws if applyLocally is not a boolean', async () => {
try {
await firebase
.database()
.ref()
.transaction(NOOP, NOOP, 'foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'applyLocally' must be a boolean value if provided`);
return Promise.resolve();
}
});
it('updates the value via a transaction', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('transaction');
await ref.set(1);
const { committed, snapshot } = await ref.transaction(value => {
return value + 1;
});
should.equal(committed, true, 'Transaction did not commit.');
snapshot.val().should.equal(2);
});
it('aborts transaction if undefined returned', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('transaction');
await ref.set(1);
return new Promise((resolve, reject) => {
ref.transaction(
() => {
return undefined;
},
(error, committed) => {
if (error) {
return reject(error);
}
if (!committed) {
return resolve();
}
return reject(new Error('Transaction did not abort commit.'));
},
);
});
});
it('passes valid data through the callback', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('transaction');
await ref.set(1);
return new Promise((resolve, reject) => {
ref.transaction(
$ => {
return $ + 1;
},
(error, committed, snapshot) => {
if (error) {
return reject(error);
}
if (!committed) {
return reject(new Error('Transaction aborted when it should not have done'));
}
should.equal(snapshot.val(), 2);
return resolve();
},
);
});
});
it('throws when an error occurs', async () => {
const ref = firebase.database().ref('nope');
try {
await ref.transaction($ => {
return $ + 1;
});
return Promise.reject(new Error('Did not throw error.'));
} catch (error) {
error.message.should.containEql(`Client doesn't have permission to access the desired data`);
return Promise.resolve();
}
});
it('passes error back to the callback', async () => {
const ref = firebase.database().ref('nope');
return new Promise((resolve, reject) => {
ref
.transaction(
$ => {
return $ + 1;
},
(error, committed, snapshot) => {
if (snapshot !== null) {
return reject(new Error('Snapshot should not be available'));
}
if (committed === true) {
return reject(new Error('Transaction should not have committed'));
}
error.message.should.containEql(
`Client doesn't have permission to access the desired data`,
);
return resolve();
},
)
.catch(() => {
// catch unhandled rejection
});
});
});
it('sets a value if one does not exist', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('create');
await ref.remove(); // Ensure it's clear
const value = Date.now();
return new Promise((resolve, reject) => {
ref.transaction(
$ => {
if ($ === null) {
return { foo: value };
}
throw new Error('Value should not exist');
},
(error, committed, snapshot) => {
if (error) {
return reject(error);
}
if (!committed) {
return reject(new Error('Transaction should have committed'));
}
snapshot.val().should.eql(
jet.contextify({
foo: value,
}),
);
return resolve();
},
);
});
});
});

View File

@@ -0,0 +1,100 @@
/*
* 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');
describe('database().ref().update()', () => {
after(async () => {
await firebase
.database()
.ref(`${PATH}/update`)
.remove();
});
it('throws if values is not an object', async () => {
try {
await firebase
.database()
.ref(`${PATH}/update`)
.update('foo');
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'values' must be an object`);
return Promise.resolve();
}
});
it('throws if values does not contain any values', async () => {
try {
await firebase
.database()
.ref(`${PATH}/update`)
.update({});
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'values' must be an object containing multiple values`);
return Promise.resolve();
}
});
it('throws if onComplete is not a function', async () => {
try {
await firebase
.database()
.ref(`${PATH}/update`)
.update(
{
foo: 'bar',
},
'foo',
);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(`'onComplete' must be a function if provided`);
return Promise.resolve();
}
});
it('updates values', async () => {
const value = Date.now();
const ref = firebase.database().ref(`${PATH}/update`);
await ref.update({
foo: value,
});
const snapshot = await ref.once('value');
snapshot.val().should.eql(
jet.contextify({
foo: value,
}),
);
});
it('callback if function is passed', async () => {
const value = Date.now();
return new Promise(async resolve => {
await firebase
.database()
.ref(`${PATH}/update`)
.update(
{
foo: value,
},
resolve,
);
});
});
});

View File

@@ -0,0 +1,302 @@
/*
* 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, CONTENT, seed, wipe } = require('../helpers');
const TEST_PATH = `${PATH}/snapshot`;
describe('database()...snapshot', () => {
before(() => seed(TEST_PATH));
after(() => wipe(TEST_PATH));
it('returns the snapshot key', async () => {
const snapshot = await firebase
.database()
.ref(TEST_PATH)
.child('types/boolean')
.once('value');
snapshot.key.should.equal('boolean');
});
it('returns the snapshot reference', async () => {
const snapshot = await firebase
.database()
.ref(TEST_PATH)
.child('types/boolean')
.once('value');
snapshot.ref.key.should.equal('boolean');
});
it('returns the correct boolean for exists', async () => {
const snapshot1 = await firebase
.database()
.ref(TEST_PATH)
.child('types/boolean')
.once('value');
const snapshot2 = await firebase
.database()
.ref(TEST_PATH)
.child('types/nope')
.once('value');
snapshot1.exists().should.equal(true);
snapshot2.exists().should.equal(false);
});
it('exports a valid object', async () => {
const snapshot = await firebase
.database()
.ref(TEST_PATH)
.child('types/string')
.once('value');
const exported = snapshot.exportVal();
exported.should.have.property('.value');
exported.should.have.property('.priority');
exported['.value'].should.equal('foobar');
should.equal(exported['.priority'], null);
});
it('exports a valid object with a object value', async () => {
const snapshot = await firebase
.database()
.ref(TEST_PATH)
.child('types/object')
.once('value');
const exported = snapshot.exportVal();
exported.should.have.property('.value');
exported.should.have.property('.priority');
exported['.value'].should.eql(jet.contextify(CONTENT.TYPES.object));
should.equal(exported['.priority'], null);
});
it('forEach throws if action param is not a function', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('unorderedList');
await ref.set({
a: 3,
b: 1,
c: 2,
});
const snapshot = await ref.orderByValue().once('value');
try {
snapshot.forEach('foo');
} catch (error) {
error.message.should.containEql(`'action' must be a function`);
}
});
it('forEach returns an ordered list of snapshots', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('unorderedList');
await ref.set({
a: 3,
b: 1,
c: 2,
});
const snapshot = await ref.orderByValue().once('value');
const expected = ['b', 'c', 'a'];
snapshot.forEach((childSnap, i) => {
childSnap.val().should.equal(i + 1);
childSnap.key.should.equal(expected[i]);
});
});
it('forEach works with arrays', async () => {
const callback = sinon.spy();
const ref = firebase
.database()
.ref(TEST_PATH)
.child('types');
const snapshot = await ref.once('value');
snapshot.child('array').forEach((childSnap, i) => {
callback();
childSnap.val().should.equal(i);
childSnap.key.should.equal(i.toString());
});
callback.should.be.callCount(snapshot.child('array').numChildren());
});
it('forEach works with objects and cancels when returning true', async () => {
const callback = sinon.spy();
const ref = firebase
.database()
.ref(TEST_PATH)
.child('types/object')
.orderByKey();
const snapshot = await ref.once('value');
snapshot.forEach(childSnap => {
callback();
childSnap.key.should.equal('bar');
childSnap.val().should.equal('baz');
return true;
});
callback.should.be.calledOnce();
});
it('forEach works with arrays and cancels when returning true', async () => {
const callback = sinon.spy();
const ref = firebase
.database()
.ref(TEST_PATH)
.child('types');
const snapshot = await ref.once('value');
snapshot.child('array').forEach(childSnap => {
callback();
childSnap.val().should.equal(0);
childSnap.key.should.equal('0');
return true;
});
callback.should.be.calledOnce();
});
it('forEach returns false when no child keys', async () => {
const callback = sinon.spy();
const ref = firebase
.database()
.ref(TEST_PATH)
.child('types/boolean');
const snapshot = await ref.once('value');
const bool = snapshot.forEach(() => {
callback();
});
bool.should.equal(false);
callback.should.be.callCount(0);
});
it('forEach cancels iteration when returning true', async () => {
const callback = sinon.spy();
const ref = firebase
.database()
.ref(TEST_PATH)
.child('types/array');
const snapshot = await ref.orderByValue().once('value');
const cancelled = snapshot.forEach(childSnap => {
callback(childSnap.val());
return true;
});
cancelled.should.equal(true);
callback.should.be.callCount(1);
callback.getCall(0).args[0].should.equal(0);
});
it('getPriority returns the correct value', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('getPriority');
await ref.setWithPriority('foo', 'bar');
const snapshot = await ref.once('value');
snapshot.getPriority().should.equal('bar');
});
it('hasChild throws if path is not a string value', async () => {
const ref = firebase
.database()
.ref(TEST_PATH)
.child('types/boolean');
const snapshot = await ref.once('value');
try {
snapshot.hasChild({ foo: 'bar' });
} catch (error) {
error.message.should.containEql(`'path' must be a string value`);
}
});
it('hasChild returns the correct boolean value', async () => {
const ref = firebase.database().ref(TEST_PATH);
const snapshot1 = await ref.child('types/boolean').once('value');
const snapshot2 = await ref.child('types').once('value');
snapshot1.hasChild('foo').should.equal(false);
snapshot2.hasChild('boolean').should.equal(true);
});
it('hasChildren returns the correct boolean value', async () => {
const ref = firebase.database().ref(TEST_PATH);
const snapshot = await ref.child('types/object').once('value');
snapshot.hasChildren().should.equal(true);
});
it('numChildren returns the correct number value', async () => {
const ref = firebase.database().ref(TEST_PATH);
const snapshot1 = await ref.child('types/boolean').once('value');
const snapshot2 = await ref.child('types/array').once('value');
const snapshot3 = await ref.child('types/object').once('value');
snapshot1.numChildren().should.equal(0);
snapshot2.numChildren().should.equal(CONTENT.TYPES.array.length);
snapshot3.numChildren().should.equal(Object.keys(CONTENT.TYPES.object).length);
});
it('toJSON returns the value of the snapshot', async () => {
const ref = firebase.database().ref(TEST_PATH);
const snapshot1 = await ref.child('types/string').once('value');
const snapshot2 = await ref.child('types/object').once('value');
snapshot1.toJSON().should.equal('foobar');
snapshot2.toJSON().should.eql(jet.contextify(CONTENT.TYPES.object));
});
it('val returns the value of the snapshot', async () => {
const ref = firebase.database().ref(TEST_PATH);
const snapshot1 = await ref.child('types/string').once('value');
const snapshot2 = await ref.child('types/object').once('value');
snapshot1.val().should.equal('foobar');
snapshot2.val().should.eql(jet.contextify(CONTENT.TYPES.object));
});
});