diff --git a/packages/app/__tests__/app.test.ts b/packages/app/__tests__/app.test.ts index 96aa57dc16..ad607e530a 100644 --- a/packages/app/__tests__/app.test.ts +++ b/packages/app/__tests__/app.test.ts @@ -1,6 +1,6 @@ -import { describe, expect, it } from '@jest/globals'; - -import { +import { describe, expect, it, jest } from '@jest/globals'; +import { checkV9Deprecation } from '../lib/common/unitTestUtils'; +import firebase, { deleteApp, registerVersion, onLog, @@ -40,4 +40,57 @@ describe('App', function () { expect(setLogLevel).toBeDefined(); }); }); + + describe('`console.warn` only called for non-modular API', function () { + it('deleteApp', function () { + // this test has a slightly special setup + // @ts-ignore test + jest.spyOn(getApp(), '_deleteApp').mockImplementation(() => Promise.resolve(null)); + checkV9Deprecation( + () => {}, // no modular replacement + () => getApp().delete(), // modular getApp(), then non-modular to check + ); + }); + + it('getApps', function () { + checkV9Deprecation( + () => getApps(), + () => firebase.apps, + ); + }); + + it('getApp', function () { + checkV9Deprecation( + () => getApp(), + () => firebase.app(), + ); + }); + + it('setLogLevel', function () { + checkV9Deprecation( + () => setLogLevel('debug'), + () => firebase.setLogLevel('debug'), + ); + }); + + it('FirebaseApp.toString()', function () { + checkV9Deprecation( + () => {}, // no modular replacement + () => getApp().toString(), // modular getApp(), then non-modular to check + ); + }); + + it('FirebaseApp.extendApp()', function () { + checkV9Deprecation( + // no modular replacement for this one so no modular func to send in + () => {}, + // modular getApp(), then non-modular to check + () => { + const app = getApp(); + (app as any).extendApp({ some: 'property' }); + return; + }, + ); + }); + }); }); diff --git a/packages/app/lib/FirebaseApp.js b/packages/app/lib/FirebaseApp.js index af6c30e86f..be7ea00120 100644 --- a/packages/app/lib/FirebaseApp.js +++ b/packages/app/lib/FirebaseApp.js @@ -14,7 +14,7 @@ * limitations under the License. * */ - +import { warnIfNotModularCall } from '@react-native-firebase/app/lib/common'; import { getAppModule } from './internal/registry/nativeModule'; export default class FirebaseApp { @@ -61,16 +61,21 @@ export default class FirebaseApp { } extendApp(extendedProps) { + // this method has no modular alternative, send true for param 'noAlternative' + warnIfNotModularCall(arguments, '', true); this._checkDestroyed(); Object.assign(this, extendedProps); } delete() { + warnIfNotModularCall(arguments, 'deleteApp()'); this._checkDestroyed(); return this._deleteApp(); } toString() { + // this method has no modular alternative, send true for param 'noAlternative' + warnIfNotModularCall(arguments, '', true); return this.name; } } diff --git a/packages/app/lib/common/index.js b/packages/app/lib/common/index.js index 8eba4403c2..68dcc755ed 100644 --- a/packages/app/lib/common/index.js +++ b/packages/app/lib/common/index.js @@ -102,3 +102,23 @@ export function tryJSONStringify(data) { return null; } } + +export const MODULAR_DEPRECATION_ARG = 'react-native-firebase-modular-method-call'; + +export function warnIfNotModularCall(args, replacementMethodName, noAlternative) { + for (let i = 0; i < args.length; i++) { + if (args[i] === MODULAR_DEPRECATION_ARG) { + return; + } + } + let message = + 'This v8 method is deprecated and will be removed in the next major release ' + + 'as part of move to match Firebase Web modular v9 SDK API.'; + + if (!noAlternative) { + message += ` Please use \`${replacementMethodName}\` instead.`; + } + + // eslint-disable-next-line no-console + console.warn(message); +} diff --git a/packages/app/lib/common/unitTestUtils.ts b/packages/app/lib/common/unitTestUtils.ts new file mode 100644 index 0000000000..ce19cba3f6 --- /dev/null +++ b/packages/app/lib/common/unitTestUtils.ts @@ -0,0 +1,10 @@ +import { expect, jest } from '@jest/globals'; + +export const checkV9Deprecation = (modularFunction: () => void, nonModularFunction: () => void) => { + const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + modularFunction(); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + nonModularFunction(); + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); + consoleWarnSpy.mockRestore(); +}; diff --git a/packages/app/lib/internal/registry/app.js b/packages/app/lib/internal/registry/app.js index b5da4fbbbd..e62da70e76 100644 --- a/packages/app/lib/internal/registry/app.js +++ b/packages/app/lib/internal/registry/app.js @@ -19,6 +19,7 @@ import { isIOS, isOther, isNull, + warnIfNotModularCall, isObject, isFunction, isString, @@ -84,6 +85,7 @@ export function initializeNativeApps() { * @param name */ export function getApp(name = DEFAULT_APP_NAME) { + warnIfNotModularCall(arguments, 'getApp()'); if (!initializedNativeApps) { initializeNativeApps(); } @@ -100,6 +102,7 @@ export function getApp(name = DEFAULT_APP_NAME) { * Gets all app instances, used for `firebase.apps` */ export function getApps() { + warnIfNotModularCall(arguments, 'getApps()'); if (!initializedNativeApps) { initializeNativeApps(); } @@ -112,6 +115,7 @@ export function getApps() { * @param configOrName */ export function initializeApp(options = {}, configOrName) { + warnIfNotModularCall(arguments, 'initializeApp()'); let appConfig = configOrName; if (!isObject(configOrName) || isNull(configOrName)) { @@ -200,6 +204,7 @@ export function initializeApp(options = {}, configOrName) { } export function setLogLevel(logLevel) { + warnIfNotModularCall(arguments, 'setLogLevel()'); if (!['error', 'warn', 'info', 'debug', 'verbose'].includes(logLevel)) { throw new Error('LogLevel must be one of "error", "warn", "info", "debug", "verbose"'); } diff --git a/packages/app/lib/modular/index.js b/packages/app/lib/modular/index.js index dd72166309..a5d5c59bfa 100644 --- a/packages/app/lib/modular/index.js +++ b/packages/app/lib/modular/index.js @@ -1,3 +1,4 @@ +import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/lib/common'; /* eslint-disable @typescript-eslint/no-unused-vars */ import { deleteApp as deleteAppCompat, @@ -6,6 +7,7 @@ import { initializeApp as initializeAppCompat, setLogLevel as setLogLevelCompat, } from '../internal'; +import sdkVersion from '../version'; /** * @typedef {import('..').ReactNativeFirebase.FirebaseApp} FirebaseApp @@ -19,7 +21,7 @@ import { * @returns {Promise} */ export function deleteApp(app) { - return deleteAppCompat(app.name, app._nativeInitialized); + return deleteAppCompat.call(null, app.name, app._nativeInitialized, MODULAR_DEPRECATION_ARG); } /** @@ -48,7 +50,7 @@ export function onLog(logCallback, options) { * @returns {FirebaseApp[]} - An array of all initialized Firebase apps. */ export function getApps() { - return getAppsCompat(); + return getAppsCompat.call(null, MODULAR_DEPRECATION_ARG); } /** @@ -58,7 +60,7 @@ export function getApps() { * @returns {FirebaseApp} - The initialized Firebase app. */ export function initializeApp(options, name) { - return initializeAppCompat(options, name); + return initializeAppCompat.call(null, options, name, MODULAR_DEPRECATION_ARG); } /** @@ -67,7 +69,7 @@ export function initializeApp(options, name) { * @returns {FirebaseApp} - The requested Firebase app instance. */ export function getApp(name) { - return getAppCompat(name); + return getAppCompat.call(null, name, MODULAR_DEPRECATION_ARG); } /** @@ -76,5 +78,7 @@ export function getApp(name) { * @returns {void} */ export function setLogLevel(logLevel) { - return setLogLevelCompat(logLevel); + return setLogLevelCompat.call(null, logLevel, MODULAR_DEPRECATION_ARG); } + +export const SDK_VERSION = sdkVersion; diff --git a/packages/crashlytics/e2e/crashlytics.e2e.js b/packages/crashlytics/e2e/crashlytics.e2e.js index 9ec30f2aa5..64d8b1e868 100644 --- a/packages/crashlytics/e2e/crashlytics.e2e.js +++ b/packages/crashlytics/e2e/crashlytics.e2e.js @@ -90,10 +90,13 @@ describe('crashlytics()', function () { let logged = false; // eslint-disable-next-line no-console console.warn = msg => { - msg.should.containEql('expects an instance of Error'); - logged = true; - // eslint-disable-next-line no-console - console.warn = orig; + // we console.warn for deprecated API, can be removed when we move to v9 + if (!msg.includes('v8 method is deprecated')) { + msg.should.containEql('expects an instance of Error'); + logged = true; + // eslint-disable-next-line no-console + console.warn = orig; + } }; firebase.crashlytics().recordError(1337); @@ -261,10 +264,13 @@ describe('crashlytics()', function () { let logged = false; // eslint-disable-next-line no-console console.warn = msg => { - msg.should.containEql('expects an instance of Error'); - logged = true; - // eslint-disable-next-line no-console - console.warn = orig; + // we console.warn for deprecated API, can be removed when we move to v9 + if (!msg.includes('v8 method is deprecated')) { + msg.should.containEql('expects an instance of Error'); + logged = true; + // eslint-disable-next-line no-console + console.warn = orig; + } }; recordError(getCrashlytics(), 1337);