Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(other): Add Database support #7887

Merged
merged 12 commits into from
Jul 12, 2024
4 changes: 4 additions & 0 deletions packages/app/lib/internal/web/firebaseDatabase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// We need to share firebase imports between modules, otherwise
// apps and instances of the firebase modules are not shared.
export * from 'firebase/app';
export * from 'firebase/database';
8 changes: 8 additions & 0 deletions packages/app/lib/internal/web/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@ export function getWebError(error) {
code: error.code || 'unknown',
message: error.message,
};

// Some modules send codes as PERMISSION_DENIED, which is not
// the same as the Firebase error code format.
obj.code = obj.code.toLowerCase();
// Replace _ with - in code
obj.code = obj.code.replace(/_/g, '-');

// Split out prefix, since we internally prefix all error codes already.
if (obj.code.includes('/')) {
obj.code = obj.code.split('/')[1];
}

return {
...obj,
userInfo: obj,
Expand Down
26 changes: 24 additions & 2 deletions packages/database/e2e/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,23 @@ const ID = Date.now();

const PATH = `tests/${ID}`;
const DB_NAME = getE2eTestProject();
const DB_RULES = `{ "rules": {".read": false, ".write": false, "tests": {".read": true, ".write": true } } }`;
const DB_RULES = {
rules: {
'.read': false,
'.write': false,
tests: {
'.read': true,
'.write': true,
$dynamic: {
once: {
childMoved: {
'.indexOn': ['nuggets'],
},
},
},
},
},
};

const CONTENT = {
TYPES: {
Expand Down Expand Up @@ -40,9 +56,15 @@ exports.seed = function seed(path) {
firebase.database().ref(`${path}/types`).set(CONTENT.TYPES),
firebase.database().ref(`${path}/query`).set(CONTENT.QUERY),
// The database emulator does not load rules correctly. We force them pre-test.
// TODO(ehesp): This is current erroring - however without it, we can't test rules.
testingUtils.initializeTestEnvironment({
projectId: getE2eTestProject(),
database: { databaseName: DB_NAME, rules: DB_RULES, host: getE2eEmulatorHost(), port: 9000 },
database: {
databaseName: DB_NAME,
rules: JSON.stringify(DB_RULES),
host: getE2eEmulatorHost(),
port: 9000,
},
}),
]);
};
Expand Down
2 changes: 2 additions & 0 deletions packages/database/e2e/query/keepSynced.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe('database().ref().keepSynced()', function () {
});

it('toggles keepSynced on and off without throwing', async function () {
if (Platform.other) return;
const ref = firebase.database().ref('noop').orderByValue();
await ref.keepSynced(true);
await ref.keepSynced(false);
Expand All @@ -48,6 +49,7 @@ describe('database().ref().keepSynced()', function () {
});

it('toggles keepSynced on and off without throwing', async function () {
if (Platform.other) return;
const { getDatabase, ref, orderByValue, query, keepSynced } = databaseModular;

const dbRef = query(ref(getDatabase(), 'noop'), orderByValue());
Expand Down
36 changes: 23 additions & 13 deletions packages/database/e2e/query/on.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,19 +94,20 @@ describe('database().ref().on()', function () {
ref.off('value');
});

xit('should callback multiple times when the value changes', async function () {
it('should callback multiple times when the value changes', async function () {
const callback = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/changes`);
const date = Date.now();
const ref = firebase.database().ref(`${TEST_PATH}/multi-change/${date}`);
ref.on('value', $ => {
// console.error('callback with ' + $.val());
callback($.val());
});
await ref.set('foo');
await Utils.sleep(100);
await ref.set('bar');
await Utils.spyToBeCalledTimesAsync(callback, 2);
ref.off('value');
callback.getCall(0).args[0].should.equal('foo'); // FIXME these simply do *not* come back
callback.getCall(1).args[0].should.equal('bar'); // in the right order every time. ??
callback.getCall(0).args[0].should.equal('foo');
callback.getCall(1).args[0].should.equal('bar');
});

// the cancelCallback is never called for ref.on but ref.once works?
Expand Down Expand Up @@ -134,7 +135,7 @@ describe('database().ref().on()', function () {

// FIXME super flaky on android emulator
it('subscribe to child added events', async function () {
if (Platform.ios) {
if (Platform.ios || Platform.other) {
const successCallback = sinon.spy();
const cancelCallback = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/childAdded`);
Expand All @@ -161,9 +162,15 @@ describe('database().ref().on()', function () {
}
});

// FIXME super flaky on Jet
xit('subscribe to child changed events', async function () {
if (Platform.ios) {
// FIXME super flaky on Jet for ios/android
it('subscribe to child changed events', async function () {
if (Platform.other) {
this.skip('Errors on JS SDK about a missing index.');
return;
}
this.skip('Flakey');
return;
if (Platform.other) {
const successCallback = sinon.spy();
const cancelCallback = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/childChanged`);
Expand Down Expand Up @@ -195,14 +202,13 @@ describe('database().ref().on()', function () {
}
});

// FIXME super flaky on jet
xit('subscribe to child removed events', async function () {
it('subscribe to child removed events', async function () {
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');

await Utils.sleep(250);
ref.on(
'child_removed',
$ => {
Expand All @@ -212,7 +218,7 @@ describe('database().ref().on()', function () {
cancelCallback();
},
);

await Utils.sleep(250);
await child.remove();
await Utils.spyToBeCalledOnceAsync(successCallback, 5000);
ref.off('child_removed');
Expand All @@ -221,6 +227,10 @@ describe('database().ref().on()', function () {
});

it('subscribe to child moved events', async function () {
if (Platform.other) {
this.skip('Errors on JS SDK about a missing index.');
return;
}
const callback = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/childMoved`);
const orderedRef = ref.orderByChild('nuggets');
Expand Down
7 changes: 6 additions & 1 deletion packages/database/e2e/query/onChildMoved.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ describe('onChildMoved', function () {
});

// FIXME super flaky on ios simulator
// FIXME errors on 'Other' platforms with a missing index on 'nuggets'
it('should stop listening if ListeningOptions.onlyOnce is true', async function () {
if (Platform.ios) {
if (Platform.ios || Platform.other) {
this.skip();
}

Expand Down Expand Up @@ -63,7 +64,11 @@ describe('onChildMoved', function () {
callback.should.be.calledWith({ nuggets: 57 });
});

// FIXME errors on 'Other' platforms with a missing index on 'nuggets'
it('subscribe to child moved events', async function () {
if (Platform.other) {
this.skip();
}
const { getDatabase, ref, query, orderByChild, onChildMoved, set, child } = databaseModular;

const callback = sinon.spy();
Expand Down
12 changes: 9 additions & 3 deletions packages/database/e2e/query/onValue.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,15 @@ describe('onValue()', function () {
const dbRef = ref(getDatabase(), `${TEST_PATH}/init`);

const callback = sinon.spy();
const unsubscribe = onValue(dbRef, $ => {
callback($.val());
});
const unsubscribe = onValue(
dbRef,
$ => {
callback($.val());
},
error => {
callback(error);
},
);

const value = Date.now();
await set(dbRef, value);
Expand Down
26 changes: 18 additions & 8 deletions packages/database/e2e/query/once.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,10 @@ describe('database().ref().once()', function () {
const value = Date.now();
const callback = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/childAdded`);

ref.once('child_added').then($ => callback($.val()));
ref
.once('child_added')
.then($ => callback($.val()))
.catch(e => callback(e));
await ref.child('foo').set(value);
await Utils.spyToBeCalledOnceAsync(callback, 5000);
callback.should.be.calledWith(value);
Expand All @@ -144,17 +146,19 @@ describe('database().ref().once()', function () {
});

// FIXME too flaky against android in CI
it('resolves when a child is removed', async function () {
if (Platform.ios) {
xit('resolves when a child is removed', async function () {
if (Platform.ios || Platform.other) {
const callbackAdd = sinon.spy();
const callbackRemove = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/childRemoved`);
ref.once('child_added').then($ => callbackAdd($.val()));
const child = ref.child('removeme');
await child.set('foo');
await Utils.spyToBeCalledOnceAsync(callbackAdd, 10000);

ref.once('child_removed').then($ => callbackRemove($.val()));
ref
.once('child_removed')
.then($ => callbackRemove($.val()))
.catch(e => callback(e));
await child.remove();
await Utils.spyToBeCalledOnceAsync(callbackRemove, 10000);
callbackRemove.should.be.calledWith('foo');
Expand All @@ -165,6 +169,10 @@ describe('database().ref().once()', function () {

// 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 function () {
if (Platform.other) {
this.skip('Errors on JS SDK about a missing index.');
return;
}
const callback = sinon.spy();
const ref = firebase.database().ref(`${TEST_PATH}/childMoved`);
const orderedRef = ref.orderByChild('nuggets');
Expand All @@ -176,8 +184,10 @@ describe('database().ref().once()', function () {
tony: { nuggets: 52 },
greg: { nuggets: 52 },
};

orderedRef.once('child_moved').then($ => callback($.val()));
orderedRef
.once('child_moved')
.then($ => callback($.val()))
.catch(e => callback(e));
await ref.set(initial);
await ref.child('greg/nuggets').set(57);
await Utils.spyToBeCalledOnceAsync(callback, 5000);
Expand Down
17 changes: 11 additions & 6 deletions packages/database/e2e/reference/push.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,22 @@ describe('database().ref().push()', function () {

it('throws if push errors', async function () {
const ref = firebase.database().ref('nope');
return ref.push('foo').catch(error => {
error.message.should.containEql("doesn't have permission to access");
return Promise.resolve();
});
return ref
.push('foo')
.then(() => {
throw new Error('Did not error');
})
.catch(error => {
error.code.should.equal('database/permission-denied');
return Promise.resolve();
});
});

it('returns an error to the callback', async function () {
const callback = sinon.spy();
const ref = firebase.database().ref('nope');
ref.push('foo', error => {
error.message.should.containEql("doesn't have permission to access");
error.code.should.equal('database/permission-denied');
callback();
});
await Utils.spyToBeCalledOnceAsync(callback);
Expand Down Expand Up @@ -134,7 +139,7 @@ describe('database().ref().push()', function () {
await push(dbRef, 'foo');
return Promise.reject(new Error('Did not throw Error'));
} catch (error) {
error.message.should.containEql("doesn't have permission to access");
error.code.should.equal('database/permission-denied');
return Promise.resolve();
}
});
Expand Down
Loading
Loading