Skip to content

Commit

Permalink
feat(other): add App/Core support
Browse files Browse the repository at this point in the history
  • Loading branch information
Salakar committed Jul 9, 2024
1 parent dba1beb commit 1b2e247
Show file tree
Hide file tree
Showing 21 changed files with 494 additions and 53 deletions.
12 changes: 10 additions & 2 deletions packages/app/e2e/app.constants.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,29 @@
* limitations under the License.
*
*/
import { getAppModule } from '@react-native-firebase/app/lib/internal/registry/nativeModule';

describe('App -> NativeModules -> Constants', function () {
describe('.apps', function () {
it('should be an array', function () {
const { NATIVE_FIREBASE_APPS } = NativeModules.RNFBAppModule;
const { NATIVE_FIREBASE_APPS } = getAppModule();

NATIVE_FIREBASE_APPS.should.be.an.Array();

// There is no native app initialization on non-native platforms.
if (Platform.other) return;
// secondaryFromNative + default
NATIVE_FIREBASE_APPS.length.should.equal(2);
});

it('array items contain name, options & state properties', function () {
const { NATIVE_FIREBASE_APPS } = NativeModules.RNFBAppModule;
const { NATIVE_FIREBASE_APPS } = getAppModule();

NATIVE_FIREBASE_APPS.should.be.an.Array();

// There is no native app initialization on non-native platforms.
if (Platform.other) return;

NATIVE_FIREBASE_APPS.length.should.equal(2);

for (let i = 0; i < NATIVE_FIREBASE_APPS.length; i++) {
Expand Down
26 changes: 16 additions & 10 deletions packages/app/e2e/app.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,22 @@
*/

describe('modular', function () {
describe('firebase v8 compatibility', function () {
describe('firebase v8 compat', function () {
it('it should allow read the default app from native', function () {
if (Platform.other) return; // Not supported on non-native platforms.
// app is created in tests app before all hook
should.equal(firebase.app()._nativeInitialized, true);
should.equal(firebase.app().name, '[DEFAULT]');
});

it('it should create js apps for natively initialized apps', function () {
if (Platform.other) return; // Not supported on non-native platforms.
should.equal(firebase.app('secondaryFromNative')._nativeInitialized, true);
should.equal(firebase.app('secondaryFromNative').name, 'secondaryFromNative');
});

it('natively initialized apps should have options available in js', function () {
if (Platform.other) return; // Not supported on non-native platforms.
const platformAppConfig = FirebaseHelpers.app.config();
should.equal(firebase.app().options.apiKey, platformAppConfig.apiKey);
should.equal(firebase.app().options.appId, platformAppConfig.appId);
Expand All @@ -45,7 +48,6 @@ describe('modular', function () {
it('apps should provide an array of apps', function () {
should.equal(!!firebase.apps.length, true);
should.equal(firebase.apps.includes(firebase.app('[DEFAULT]')), true);
return Promise.resolve();
});

it('apps can get and set data collection', async function () {
Expand Down Expand Up @@ -93,8 +95,8 @@ describe('modular', function () {
});

it('should error if dynamic app initialization values are invalid', async function () {
// firebase-android-sdk will not complain on invalid initialization values, iOS throws
if (device.getPlatform() === 'android') {
// firebase-android-sdk and js-sdk will not complain on invalid initialization values, iOS throws
if (Platform.android || Platform.other) {
return;
}

Expand All @@ -120,7 +122,7 @@ describe('modular', function () {
}
});

it('apps can be deleted, but only once', async function () {
it('apps can be deleted, but only if it exists', async function () {
const name = `testscoreapp${FirebaseHelpers.id}`;
const platformAppConfig = FirebaseHelpers.app.config();
const newApp = await firebase.initializeApp(platformAppConfig, name);
Expand All @@ -145,6 +147,7 @@ describe('modular', function () {
});

it('prevents the default app from being deleted', async function () {
if (Platform.other) return; // We can delete the default app on other platforms.
try {
await firebase.app().delete();
} catch (e) {
Expand All @@ -161,8 +164,9 @@ describe('modular', function () {
});
});

describe('firebase', function () {
describe('firebase v9 modular', function () {
it('it should allow read the default app from native', function () {
if (Platform.other) return; // Not supported on non-native platforms.
const { getApp } = modular;

// app is created in tests app before all hook
Expand All @@ -171,6 +175,7 @@ describe('modular', function () {
});

it('it should create js apps for natively initialized apps', function () {
if (Platform.other) return; // Not supported on non-native platforms.
const { getApp } = modular;

should.equal(getApp('secondaryFromNative')._nativeInitialized, true);
Expand All @@ -189,7 +194,7 @@ describe('modular', function () {

try {
setLogLevel('silent');
throw new Error('did not throw on invalid loglevel');
throw new Error('did not throw on invalid log level');
} catch (e) {
e.message.should.containEql('LogLevel must be one of');
}
Expand Down Expand Up @@ -230,8 +235,8 @@ describe('modular', function () {
it('should error if dynamic app initialization values are invalid', async function () {
const { initializeApp, getApps } = modular;

// firebase-android-sdk will not complain on invalid initialization values, iOS throws
if (device.getPlatform() === 'android') {
// firebase-android-sdk & js-sdk will not complain on invalid initialization values, iOS throws
if (Platform.android || Platform.other) {
return;
}

Expand All @@ -257,7 +262,7 @@ describe('modular', function () {
}
});

it('apps can be deleted, but only once', async function () {
it('apps can be deleted, but only if it exists', async function () {
const { initializeApp, getApp, deleteApp } = modular;

const name = `testscoreapp${FirebaseHelpers.id}`;
Expand Down Expand Up @@ -286,6 +291,7 @@ describe('modular', function () {
});

it('prevents the default app from being deleted', async function () {
if (Platform.other) return; // We can delete the default app on other platforms.
const { getApp, deleteApp } = modular;

try {
Expand Down
3 changes: 3 additions & 0 deletions packages/app/e2e/config.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('config', function () {
describe('meta', function () {
it('should read Info.plist/AndroidManifest.xml meta data', async function () {
const metaData = await NativeModules.RNFBAppModule.metaGetAll();
if (Platform.other) return;
metaData.rnfirebase_meta_testing_string.should.equal('abc');
metaData.rnfirebase_meta_testing_boolean_false.should.equal(false);
metaData.rnfirebase_meta_testing_boolean_true.should.equal(true);
Expand All @@ -28,6 +29,7 @@ describe('config', function () {
describe('json', function () {
it('should read firebase.json data', async function () {
const jsonData = await NativeModules.RNFBAppModule.jsonGetAll();
if (Platform.other) return;
jsonData.rnfirebase_json_testing_string.should.equal('abc');
jsonData.rnfirebase_json_testing_boolean_false.should.equal(false);
jsonData.rnfirebase_json_testing_boolean_true.should.equal(true);
Expand All @@ -41,6 +43,7 @@ describe('config', function () {

// NOTE: "preferencesClearAll" clears Firestore settings. Set DB as emulator again.
after(async function () {
if (Platform.other) return;
await firebase
.firestore()
.settings({ host: 'localhost:8080', ssl: false, persistence: true });
Expand Down
68 changes: 52 additions & 16 deletions packages/app/e2e/events.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,23 @@ describe('Core -> EventEmitter', function () {
const { resolve, reject, promise } = Promise.defer();
const emitter = NativeEventEmitter;

emitter.addListener(eventName, event => {
event.foo.should.equal(eventBody.foo);
if (!readyToResolve) {
return reject(new Error('Event was received before being ready!'));
}

return resolve();
});
emitter.addListener(
eventName,
event => {
try {
should.notEqual(event.foo, null);
event.foo.should.equal(eventBody.foo);
} catch (e) {
return reject(e);
}
if (!readyToResolve) {
return reject(new Error('Event was received before being ready!'));
}
return resolve();
},
undefined,
true,
);

await eventsNotifyReady(false);
await eventsPing(eventName, eventBody);
Expand All @@ -57,24 +66,51 @@ describe('Core -> EventEmitter', function () {
should.equal(nativeListenersAfter.events.pong, undefined);
});

it('can send and receive lots of events', async function () {
const { eventsPing, eventsNotifyReady } = NativeModules.RNFBAppModule;
await eventsNotifyReady(true);
const { resolve, reject, promise } = Promise.defer();
const emitter = NativeEventEmitter;
let eventCount = 0;
emitter.addListener(eventName, event => {
try {
should.notEqual(event.foo, null);
event.foo.should.equal(eventBody.foo);
} catch (e) {
return reject(e);
}

eventCount++;
if (eventCount === 100) {
return resolve();
}
});
await Utils.sleep(100);
for (let i = 0; i < 100; i++) {
eventsPing(eventName, eventBody);
}

await promise;
emitter.removeAllListeners(eventName);
});

it('queues events before a js listener is registered', async function () {
const { eventsPing, eventsNotifyReady, eventsGetListeners, eventsRemoveListener } =
NativeModules.RNFBAppModule;
await eventsNotifyReady(true);
const { resolve, promise } = Promise.defer();
const { resolve, reject, promise } = Promise.defer();
const emitter = NativeEventEmitter;

await eventsPing(eventName2, eventBody);
await Utils.sleep(500);
// const nativeListenersBefore = await eventsGetListeners();
// console.error('we have listeners? ' + JSON.stringify(nativeListenersBefore));
// should.equal(nativeListenersBefore.events.ping, undefined);

const subscription = emitter.addListener(eventName2, event => {
event.foo.should.equal(eventBody.foo);
try {
should.notEqual(event.foo, null);
event.foo.should.equal(eventBody.foo);
} catch (e) {
return reject(e);
}
return resolve();
});

await promise;
subscription.remove();

Expand Down
2 changes: 2 additions & 0 deletions packages/app/e2e/utils.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/

describe('utils()', function () {
if (Platform.other) return; // Not supported on non-native platforms.

describe('namespace', function () {
it('accessible from firebase.app()', function () {
const app = firebase.app();
Expand Down
2 changes: 2 additions & 0 deletions packages/app/e2e/utilsStatics.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/

describe('utils()', function () {
if (Platform.other) return; // Not supported on non-native platforms.

describe('statics', function () {
it('provides native path strings', function () {
firebase.utils.FilePath.should.be.an.Object();
Expand Down
2 changes: 2 additions & 0 deletions packages/app/lib/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ export const isIOS = Platform.OS === 'ios';

export const isAndroid = Platform.OS === 'android';

export const isOther = Platform.OS !== 'ios' && Platform.OS !== 'android';

export function tryJSONParse(string) {
try {
return string && JSON.parse(string);
Expand Down
25 changes: 19 additions & 6 deletions packages/app/lib/internal/RNFBNativeEventEmitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,35 @@
*
*/

import { NativeEventEmitter, NativeModules } from 'react-native';

const { RNFBAppModule } = NativeModules;
import { NativeEventEmitter } from 'react-native';
import { getReactNativeModule } from './nativeModule';

class RNFBNativeEventEmitter extends NativeEventEmitter {
constructor() {
super(RNFBAppModule);
super(getReactNativeModule('RNFBAppModule'));
this.ready = false;
}

addListener(eventType, listener, context) {
const RNFBAppModule = getReactNativeModule('RNFBAppModule');
if (!this.ready) {
RNFBAppModule.eventsNotifyReady(true);
this.ready = true;
}
RNFBAppModule.eventsAddListener(eventType);
if (global.RNFBDebug) {
// eslint-disable-next-line no-console
console.debug(`[RNFB-->Event][👂] ${eventType} -> listening`);
}
const listenerDebugger = (...args) => {
if (global.RNFBDebug) {
// eslint-disable-next-line no-console
console.debug(`[RNFB<--Event][📣] ${eventType} <-`, JSON.stringify(args[0]));
}
return listener(...args);
};

let subscription = super.addListener(`rnfb_${eventType}`, listener, context);
let subscription = super.addListener(`rnfb_${eventType}`, listenerDebugger, context);

// React Native 0.65+ altered EventEmitter:
// - removeSubscription is gone
Expand All @@ -41,7 +52,7 @@ class RNFBNativeEventEmitter extends NativeEventEmitter {
// make sure eventType for backwards compatibility just in case
subscription.eventType = `rnfb_${eventType}`;

// New style is to return a remove function on the object, just in csae people call that,
// New style is to return a remove function on the object, just in case people call that,
// we will modify it to do our native unsubscription then call the original
let originalRemove = subscription.remove;
let newRemove = () => {
Expand All @@ -59,12 +70,14 @@ class RNFBNativeEventEmitter extends NativeEventEmitter {
}

removeAllListeners(eventType) {
const RNFBAppModule = getReactNativeModule('RNFBAppModule');
RNFBAppModule.eventsRemoveListener(eventType, true);
super.removeAllListeners(`rnfb_${eventType}`);
}

// This is likely no longer ever called, but it is here for backwards compatibility with RN <= 0.64
removeSubscription(subscription) {
const RNFBAppModule = getReactNativeModule('RNFBAppModule');
RNFBAppModule.eventsRemoveListener(subscription.eventType.replace('rnfb_'), false);
if (super.removeSubscription) {
super.removeSubscription(subscription);
Expand Down
2 changes: 2 additions & 0 deletions packages/app/lib/internal/nativeModule.android.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { getReactNativeModule } from './nativeModuleAndroidIos';
export { setReactNativeModule } from './nativeModuleAndroidIos';
2 changes: 2 additions & 0 deletions packages/app/lib/internal/nativeModule.ios.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { getReactNativeModule } from './nativeModuleAndroidIos';
export { setReactNativeModule } from './nativeModuleAndroidIos';
4 changes: 4 additions & 0 deletions packages/app/lib/internal/nativeModule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This is fallback for when the platform is not with native SDKs.
// In this case we use the web Firebase JS SDK.
export { getReactNativeModule } from './nativeModuleWeb';
export { setReactNativeModule } from './nativeModuleWeb';
Loading

0 comments on commit 1b2e247

Please sign in to comment.