Skip to content

Commit f5d2152

Browse files
chore(crashlytics): refactor deprecation implementation (#8155)
1 parent cd09fea commit f5d2152

File tree

6 files changed

+164
-81
lines changed

6 files changed

+164
-81
lines changed

packages/app/lib/common/index.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,64 @@ export function tryJSONStringify(data) {
103103
}
104104
}
105105

106+
// Used to indicate if there is no corresponding modular function
107+
const NO_REPLACEMENT = true;
108+
109+
const mapOfDeprecationReplacements = {
110+
crashlytics: {
111+
checkForUnsentReports: 'checkForUnsentReports()',
112+
crash: 'crash()',
113+
deleteUnsentReports: 'deleteUnsentReports()',
114+
didCrashOnPreviousExecution: 'didCrashOnPreviousExecution()',
115+
log: 'log()',
116+
setAttribute: 'setAttribute()',
117+
setAttributes: 'setAttributes()',
118+
setUserId: 'setUserId()',
119+
recordError: 'recordError()',
120+
sendUnsentReports: 'sendUnsentReports()',
121+
setCrashlyticsCollectionEnabled: 'setCrashlyticsCollectionEnabled()',
122+
},
123+
};
124+
125+
const v8deprecationMessage =
126+
'This v8 method is deprecated and will be removed in the next major release ' +
127+
'as part of move to match Firebase Web modular v9 SDK API.';
128+
129+
export function deprecationConsoleWarning(moduleName, methodName, isModularMethod) {
130+
if (!isModularMethod) {
131+
const moduleMap = mapOfDeprecationReplacements[moduleName];
132+
if (moduleMap) {
133+
const replacementMethodName = moduleMap[methodName];
134+
// only warn if it is mapped and purposefully deprecated
135+
if (replacementMethodName) {
136+
const message = createMessage(moduleName, methodName);
137+
138+
// eslint-disable-next-line no-console
139+
console.warn(message);
140+
}
141+
}
142+
}
143+
}
144+
145+
export function createMessage(moduleName, methodName, uniqueMessage = '') {
146+
if (uniqueMessage.length > 0) {
147+
// Unique deprecation message used for testing
148+
return uniqueMessage;
149+
}
150+
151+
const moduleMap = mapOfDeprecationReplacements[moduleName];
152+
if (moduleMap) {
153+
const replacementMethodName = moduleMap[methodName];
154+
if (replacementMethodName) {
155+
let message;
156+
if (replacementMethodName !== NO_REPLACEMENT) {
157+
message = v8deprecationMessage + ` Please use \`${replacementMethodName}\` instead.`;
158+
}
159+
return message;
160+
}
161+
}
162+
}
163+
106164
export const MODULAR_DEPRECATION_ARG = 'react-native-firebase-modular-method-call';
107165

108166
export function warnIfNotModularCall(args, replacementMethodName, noAlternative) {
Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,46 @@
1+
// @ts-nocheck
12
import { expect, jest } from '@jest/globals';
3+
import { createMessage } from './index';
24

35
export const checkV9Deprecation = (modularFunction: () => void, nonModularFunction: () => void) => {
46
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
7+
consoleWarnSpy.mockRestore();
58
modularFunction();
69
expect(consoleWarnSpy).not.toHaveBeenCalled();
10+
consoleWarnSpy.mockClear();
11+
const consoleWarnSpy2 = jest.spyOn(console, 'warn').mockImplementation(() => {});
712
nonModularFunction();
8-
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
9-
consoleWarnSpy.mockRestore();
13+
14+
expect(consoleWarnSpy2).toHaveBeenCalledTimes(1);
15+
consoleWarnSpy2.mockClear();
16+
};
17+
18+
export type CheckV9DeprecationFunction = (
19+
modularFunction: () => void,
20+
nonModularFunction: () => void,
21+
methodName: string,
22+
uniqueMessage: string = '',
23+
) => void;
24+
25+
export const createCheckV9Deprecation = (moduleName: string): CheckV9DeprecationFunction => {
26+
return (
27+
modularFunction: () => void,
28+
nonModularFunction: () => void,
29+
methodName: string,
30+
uniqueMessage = '',
31+
) => {
32+
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
33+
consoleWarnSpy.mockReset();
34+
modularFunction();
35+
expect(consoleWarnSpy).not.toHaveBeenCalled();
36+
consoleWarnSpy.mockReset();
37+
const consoleWarnSpy2 = jest.spyOn(console, 'warn').mockImplementation(warnMessage => {
38+
const message = createMessage(moduleName, methodName, uniqueMessage);
39+
expect(message).toMatch(warnMessage);
40+
});
41+
nonModularFunction();
42+
43+
expect(consoleWarnSpy2).toHaveBeenCalledTimes(1);
44+
consoleWarnSpy2.mockReset();
45+
};
1046
};

packages/app/lib/internal/registry/namespace.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
*/
1717

18-
import { isString } from '../../common';
18+
import { isString, MODULAR_DEPRECATION_ARG, deprecationConsoleWarning } from '../../common';
1919
import FirebaseApp from '../../FirebaseApp';
2020
import SDK_VERSION from '../../version';
2121
import { DEFAULT_APP_NAME, KNOWN_NAMESPACES } from '../constants';
@@ -170,10 +170,10 @@ function getOrCreateModuleForRoot(moduleNamespace) {
170170
}
171171

172172
if (!APP_MODULE_INSTANCE[_app.name][moduleNamespace]) {
173-
APP_MODULE_INSTANCE[_app.name][moduleNamespace] = new ModuleClass(
174-
_app,
175-
NAMESPACE_REGISTRY[moduleNamespace],
173+
const module = createDeprecationProxy(
174+
new ModuleClass(_app, NAMESPACE_REGISTRY[moduleNamespace]),
176175
);
176+
APP_MODULE_INSTANCE[_app.name][moduleNamespace] = module;
177177
}
178178

179179
return APP_MODULE_INSTANCE[_app.name][moduleNamespace];
@@ -277,6 +277,28 @@ export function getFirebaseRoot() {
277277
return createFirebaseRoot();
278278
}
279279

280+
function createDeprecationProxy(instance) {
281+
return new Proxy(instance, {
282+
get(target, prop, receiver) {
283+
const originalMethod = target[prop];
284+
if (prop === 'constructor') {
285+
return target.constructor;
286+
}
287+
if (typeof originalMethod === 'function') {
288+
return function (...args) {
289+
const isModularMethod = args.includes(MODULAR_DEPRECATION_ARG);
290+
const moduleName = receiver._config.namespace;
291+
292+
deprecationConsoleWarning(moduleName, prop, isModularMethod);
293+
294+
return originalMethod.apply(target, args);
295+
};
296+
}
297+
return Reflect.get(target, prop, receiver);
298+
},
299+
});
300+
}
301+
280302
/**
281303
*
282304
* @param options

packages/crashlytics/__tests__/crashlytics.test.ts

Lines changed: 36 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import { describe, expect, it, jest } from '@jest/globals';
2-
import { checkV9Deprecation } from '../../app/lib/common/unitTestUtils';
1+
import { describe, expect, it, jest, beforeEach } from '@jest/globals';
2+
// @ts-ignore test
3+
import FirebaseModule from '../../app/lib/internal/FirebaseModule';
4+
import {
5+
createCheckV9Deprecation,
6+
CheckV9DeprecationFunction,
7+
} from '../../app/lib/common/unitTestUtils';
38
import {
49
firebase,
510
getCrashlytics,
@@ -81,146 +86,118 @@ describe('Crashlytics', function () {
8186
});
8287

8388
describe('test `console.warn` is called for RNFB v8 API & not called for v9 API', function () {
84-
it('checkForUnsentReports', function () {
85-
const crashlytics = getCrashlytics();
86-
// @ts-ignore test
87-
const nativeMock = { checkForUnsentReports: jest.fn() };
89+
let checkV9Deprecation: CheckV9DeprecationFunction;
90+
91+
beforeEach(function () {
92+
checkV9Deprecation = createCheckV9Deprecation('crashlytics');
93+
8894
// @ts-ignore test
89-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
95+
jest.spyOn(FirebaseModule.prototype, 'native', 'get').mockImplementation(() => {
96+
return new Proxy(
97+
{},
98+
{
99+
get: () => jest.fn(),
100+
},
101+
);
102+
});
103+
});
90104

105+
it('checkForUnsentReports', function () {
106+
const crashlytics = getCrashlytics();
91107
checkV9Deprecation(
92108
() => checkForUnsentReports(crashlytics),
93109
() => crashlytics.checkForUnsentReports(),
110+
'checkForUnsentReports',
94111
);
95112
});
96113

97114
it('crash', function () {
98115
const crashlytics = getCrashlytics();
99-
// @ts-ignore test
100-
const nativeMock = { crash: jest.fn() };
101-
// @ts-ignore test
102-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
103-
104116
checkV9Deprecation(
105117
() => crash(crashlytics),
106118
() => crashlytics.crash(),
119+
'crash',
107120
);
108121
});
109122

110123
it('deleteUnsentReports', function () {
111124
const crashlytics = getCrashlytics();
112-
// @ts-ignore test
113-
const nativeMock = { deleteUnsentReports: jest.fn() };
114-
// @ts-ignore test
115-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
116-
117125
checkV9Deprecation(
118126
() => deleteUnsentReports(crashlytics),
119127
() => crashlytics.deleteUnsentReports(),
128+
'deleteUnsentReports',
120129
);
121130
});
122131

123132
it('didCrashOnPreviousExecution', function () {
124133
const crashlytics = getCrashlytics();
125-
// @ts-ignore test
126-
const nativeMock = { didCrashOnPreviousExecution: jest.fn() };
127-
// @ts-ignore test
128-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
129-
130134
checkV9Deprecation(
131135
() => didCrashOnPreviousExecution(crashlytics),
132136
() => crashlytics.didCrashOnPreviousExecution(),
137+
'didCrashOnPreviousExecution',
133138
);
134139
});
135140

136141
it('log', function () {
137142
const crashlytics = getCrashlytics();
138-
// @ts-ignore test
139-
const nativeMock = { log: jest.fn() };
140-
// @ts-ignore test
141-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
142-
143143
checkV9Deprecation(
144144
() => log(crashlytics, 'message'),
145145
() => crashlytics.log('message'),
146+
'log',
146147
);
147148
});
148149

149150
it('setAttribute', function () {
150151
const crashlytics = getCrashlytics();
151-
// @ts-ignore test
152-
const nativeMock = { setAttribute: jest.fn() };
153-
// @ts-ignore test
154-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
155-
156152
checkV9Deprecation(
157153
() => setAttribute(crashlytics, 'name', 'value'),
158154
() => crashlytics.setAttribute('name', 'value'),
155+
'setAttribute',
159156
);
160157
});
161158

162159
it('setAttributes', function () {
163160
const crashlytics = getCrashlytics();
164-
// @ts-ignore test
165-
const nativeMock = { setAttributes: jest.fn() };
166-
// @ts-ignore test
167-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
168-
169161
checkV9Deprecation(
170162
() => setAttributes(crashlytics, {}),
171163
() => crashlytics.setAttributes({}),
164+
'setAttributes',
172165
);
173166
});
174167

175168
it('setUserId', function () {
176169
const crashlytics = getCrashlytics();
177-
// @ts-ignore test
178-
const nativeMock = { setUserId: jest.fn() };
179-
// @ts-ignore test
180-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
181-
182170
checkV9Deprecation(
183171
() => setUserId(crashlytics, 'id'),
184172
() => crashlytics.setUserId('id'),
173+
'setUserId',
185174
);
186175
});
187176

188177
it('recordError', function () {
189178
const crashlytics = getCrashlytics();
190-
// @ts-ignore test
191-
const nativeMock = { recordError: jest.fn() };
192-
// @ts-ignore test
193-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
194-
195179
checkV9Deprecation(
196180
() => recordError(crashlytics, new Error(), 'name'),
197181
() => crashlytics.recordError(new Error(), 'name'),
182+
'recordError',
198183
);
199184
});
200185

201186
it('sendUnsentReports', function () {
202187
const crashlytics = getCrashlytics();
203-
// @ts-ignore test
204-
const nativeMock = { sendUnsentReports: jest.fn() };
205-
// @ts-ignore test
206-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
207-
208188
checkV9Deprecation(
209189
() => sendUnsentReports(crashlytics),
210190
() => crashlytics.sendUnsentReports(),
191+
'sendUnsentReports',
211192
);
212193
});
213194

214195
it('setCrashlyticsCollectionEnabled', function () {
215196
const crashlytics = getCrashlytics();
216-
// @ts-ignore test
217-
const nativeMock = { setCrashlyticsCollectionEnabled: jest.fn() };
218-
// @ts-ignore test
219-
jest.spyOn(crashlytics, 'native', 'get').mockReturnValue(nativeMock);
220-
221197
checkV9Deprecation(
222198
() => setCrashlyticsCollectionEnabled(crashlytics, true),
223199
() => crashlytics.setCrashlyticsCollectionEnabled(true),
200+
'setCrashlyticsCollectionEnabled',
224201
);
225202
});
226203

@@ -230,6 +207,8 @@ describe('Crashlytics', function () {
230207
// swapped order here because we're deprecating the modular method and keeping the property on Crashlytics instance
231208
() => crashlytics.isCrashlyticsCollectionEnabled,
232209
() => isCrashlyticsCollectionEnabled(crashlytics),
210+
'',
211+
'`isCrashlyticsCollectionEnabled()` is deprecated, please use `Crashlytics.isCrashlyticsCollectionEnabled` property instead',
233212
);
234213
});
235214
});

0 commit comments

Comments
 (0)