I'm writing an app with React Native. I use Firebase Cloud Messaging for real time communication. I'm currently writing the unit tests for the FCM code using jest. The problem is that I'm struggling to make it work, since it consists of void functions that contain promises. Let me give you the code:
fcm.js:
import { Alert } from "react-native";
import firebase from "react-native-firebase";
export const checkNotificationsPermission = () => {
firebase
.messaging()
.hasPermission()
.then(enabled => {
if (enabled) {
// User has permissions.
} else {
// User doesn't have permission.
Alert.alert(
alertMessages.firebasePrepareForPermissionTitle,
alertMessages.firebasePrepareForPermissionMessage,
[{ text: buttonTexts.ok, onPress: () => requestNotificationsPermission() }]
);
}
});
};
export const requestNotificationsPermission = () => {
firebase
.messaging()
.requestPermission()
.then(() => {
// User has authorised.
})
.catch(() => {
// User has rejected permissions.
Alert.alert(
alertMessages.firebasePrepareForPermissionTitle,
alertMessages.firebasePermissionDeniedMessage,
[{ text: buttonTexts.ok, onPress: () => {} }]
);
});
};
fcm.test.js:
import firebase from "react-native-firebase";
describe("checkNotificationsPermission", () => {
beforeEach(() => {
return checkNotificationsPermission();
});
afterEach(() => {
jest.clearAllMocks();
});
it("should call firebase's hasPermission", async () => {
expect(firebase.messaging().requestPermission).toHaveBeenCalledTimes(1);
});
});
Here is how I mocked firebase (__mocks__/react-native-firebase.js):
const firebase = {
messaging: jest.fn(() => ({
hasPermission: jest.fn(() => new Promise(resolve => resolve(true))),
requestPermission: jest.fn(() => new Promise(resolve => resolve(true)))
}))
};
export default firebase;
The test fails with Expected mock function to have been called one time, but it was called zero times..Since this wouldn't work and I had a similar question about promises which got answered I tried to apply what I learned there which resulted in the following code.
fcm.js:
import { Alert } from "react-native";
import firebase from "react-native-firebase";
export const checkNotificationsPermission = () =>
new Promise((resolve, reject) => {
firebase
.messaging()
.hasPermission()
.then(enabled => {
if (enabled) {
// User has permissions.
resolve(true);
} else {
// User doesn't have permission.
Alert.alert(
alertMessages.firebasePrepareForPermissionTitle,
alertMessages.firebasePrepareForPermissionMessage,
[
{
text: buttonTexts.ok,
onPress: () =>
requestNotificationsPermission()
.then(() => resolve(true))
.catch(() => reject(false))
}
]
);
}
});
});
export const requestNotificationsPermission = () =>
new Promise((resolve, reject) => {
firebase
.messaging()
.requestPermission()
.then(() => {
// User has authorised.
resolve(true);
})
.catch(() => {
// User has rejected permissions.
reject(true);
Alert.alert(
alertMessages.firebasePrepareForPermissionTitle,
alertMessages.firebasePermissionDeniedMessage,
[{ text: buttonTexts.ok, onPress: () => {} }]
);
});
});
fcm.test.js:
import firebase from "react-native-firebase";
import { requestNotifcationsPermission } from "./fcm";
describe("checkNotificationsPermission", () => {
afterEach(() => {
jest.clearAllMocks();
});
it("should call firebase's hasPermission", () => {
expect.assertions(1);
return checkNotificationsPermission().then(() => {
expect(firebase.messaging().requestPermission).toHaveBeenCalledTimes(1);
});
});
});
But for some reason these tests still fail. I empirically tested and ensured the code works. Just the unit tests won't pass.
Edit
I accidentally left out that both fcm.js also have the following imports:
import alertMessages from "../../config/constants/alertMessages";
import buttonTexts from "../../config/constants/buttonTexts";