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

chore(crashlytics): deprecation warning for v8 API ahead of future major release #8107

Merged
merged 9 commits into from
Nov 22, 2024
11 changes: 11 additions & 0 deletions packages/app/lib/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,14 @@ export function tryJSONStringify(data) {
return null;
}
}

export const MODULAR_DEPRECATION_ARG = 'react-native-firebase-modular-method-call';

export function isNotModularCall(args) {
for (let i = 0; i < args.length; i++) {
if (args[i] === MODULAR_DEPRECATION_ARG) {
return false;
}
}
return true;
}
213 changes: 212 additions & 1 deletion packages/crashlytics/__tests__/crashlytics.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it, jest } from '@jest/globals';

import {
firebase,
Expand Down Expand Up @@ -79,4 +79,215 @@ describe('Crashlytics', function () {
expect(setCrashlyticsCollectionEnabled).toBeDefined();
});
});

describe('test `console.warn` is called for RNFB v8 API & not called for v9 API', function () {
it('checkForUnsentReports', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { checkForUnsentReports: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

checkForUnsentReports(crashlytics);
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.checkForUnsentReports();
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});

it('crash', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { crash: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

crash(crashlytics);
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.crash();
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});

it('deleteUnsentReports', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { deleteUnsentReports: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

deleteUnsentReports(crashlytics);
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.deleteUnsentReports();
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});

it('didCrashOnPreviousExecution', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { didCrashOnPreviousExecution: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

didCrashOnPreviousExecution(crashlytics);
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.didCrashOnPreviousExecution();
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});

it('log', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { log: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

log(crashlytics, 'message');
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.log('message');
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});

it('setAttribute', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { setAttribute: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

setAttribute(crashlytics, 'name', 'value');
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.setAttribute('name', 'value');
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});

it('setAttributes', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { setAttributes: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

setAttributes(crashlytics, {});
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.setAttributes({});
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});

it('setUserId', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { setUserId: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

setUserId(crashlytics, 'id');
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.setUserId('id');
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});

it('recordError', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { recordError: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

recordError(crashlytics, new Error(), 'name');
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.recordError(new Error(), 'name');
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});

it('sendUnsentReports', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { sendUnsentReports: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

sendUnsentReports(crashlytics);
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.sendUnsentReports();
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});

it('setCrashlyticsCollectionEnabled', function () {
const crashlytics = getCrashlytics();
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// @ts-ignore test
const nativeMock = { setCrashlyticsCollectionEnabled: jest.fn() };
// @ts-ignore test
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);

setCrashlyticsCollectionEnabled(crashlytics, true);
// Check that console.warn was not called for v9 method call
expect(consoleWarnSpy).not.toHaveBeenCalled();

crashlytics.setCrashlyticsCollectionEnabled(true);
// Check that console.warn was called for v8 method call
expect(consoleWarnSpy).toHaveBeenCalled();
// Restore the original console.warn
consoleWarnSpy.mockRestore();
});
});
});
11 changes: 7 additions & 4 deletions packages/crashlytics/e2e/crashlytics.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
68 changes: 68 additions & 0 deletions packages/crashlytics/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
isObject,
isString,
isOther,
isNotModularCall,
} from '@react-native-firebase/app/lib/common';
import {
createModuleNamespace,
Expand Down Expand Up @@ -51,10 +52,17 @@ class FirebaseCrashlyticsModule extends FirebaseModule {
}

get isCrashlyticsCollectionEnabled() {
// Purposefully did not deprecate this as I think it should remain a property rather than a method.
return this._isCrashlyticsCollectionEnabled;
}

checkForUnsentReports() {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `checkForUnsentReports()` instead.',
);
}
if (this.isCrashlyticsCollectionEnabled) {
throw new Error(
"firebase.crashlytics().setCrashlyticsCollectionEnabled(*) has been set to 'true', all reports are automatically sent.",
Expand All @@ -64,22 +72,52 @@ class FirebaseCrashlyticsModule extends FirebaseModule {
}

crash() {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `crash()` instead.',
);
}
this.native.crash();
}

async deleteUnsentReports() {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `deleteUnsentReports()` instead.',
);
}
await this.native.deleteUnsentReports();
}

didCrashOnPreviousExecution() {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `didCrashOnPreviousExecution()` instead.',
);
}
return this.native.didCrashOnPreviousExecution();
}

log(message) {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `log()` instead.',
);
}
this.native.log(`${message}`);
}

setAttribute(name, value) {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `setAttribute()` instead.',
);
}
if (!isString(name)) {
throw new Error(
'firebase.crashlytics().setAttribute(*, _): The supplied property name must be a string.',
Expand All @@ -96,6 +134,12 @@ class FirebaseCrashlyticsModule extends FirebaseModule {
}

setAttributes(object) {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `setAttributes()` instead.',
);
}
if (!isObject(object)) {
throw new Error(
'firebase.crashlytics().setAttributes(*): The supplied arg must be an object of key value strings.',
Expand All @@ -106,6 +150,12 @@ class FirebaseCrashlyticsModule extends FirebaseModule {
}

setUserId(userId) {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `setUserId()` instead.',
);
}
if (!isString(userId)) {
throw new Error(
'firebase.crashlytics().setUserId(*): The supplied userId must be a string value.',
Expand All @@ -116,6 +166,12 @@ class FirebaseCrashlyticsModule extends FirebaseModule {
}

recordError(error, jsErrorName) {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `recordError()` instead.',
);
}
if (isError(error)) {
StackTrace.fromError(error, { offline: true }).then(stackFrames => {
this.native.recordError(createNativeErrorObj(error, stackFrames, false, jsErrorName));
Expand All @@ -128,12 +184,24 @@ class FirebaseCrashlyticsModule extends FirebaseModule {
}

sendUnsentReports() {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `sendUnsentReports()` instead.',
);
}
if (this.isCrashlyticsCollectionEnabled) {
this.native.sendUnsentReports();
}
}

setCrashlyticsCollectionEnabled(enabled) {
if (isNotModularCall(arguments)) {
// eslint-disable-next-line no-console
console.warn(
'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. Please use `setCrashlyticsCollectionEnabled()` instead.',
);
}
if (!isBoolean(enabled)) {
throw new Error(
"firebase.crashlytics().setCrashlyticsCollectionEnabled(*) 'enabled' must be a boolean.",
Expand Down
Loading
Loading