I need help resolving error when running unit tests on a hook being used to retrieve user data.
TypeError: _app.default.auth is not a function 10 | setUserId(storedUserId); 11 | } else { > 12 | const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
I'm only getting the above error when running unit tests for a hook I created to retrieve user info from storage or Firebase. This hook looks in storage and then Firebase if storage === null.
import { useEffect, useState } from "react";
import useGetUserId from "./useGetUserId";
import { app } from "../environments/environment";
import { doc, getDoc, getFirestore } from "firebase/firestore";
const useGetUserProfile = () => {
type profile = {
email: string;
username: string;
userBio: string;
dob: Date;
gender: string;
sexo: string;
education: string;
drinkingHabits: string;
smokingHabits: string;
};
const db = getFirestore(app);
const userId: string | null = useGetUserId();
const [isLoading, setIsLoading] = useState(true);
const [userProfile, setUserProfile] = useState<any | null>(null);
useEffect(() => {
const userProfile = async () => {
setIsLoading(true);
try {
const userRef = localStorage.getItem("PROFILE_INFO");
if (userRef) {
const profile: profile = JSON.parse(userRef);
setUserProfile(profile);
} else {
if (userId) {
const id = JSON.parse(userId);
const userRef = await getDoc(doc(db, "users", id.user.uid));
if (userRef.exists()) {
const profile = userRef.data();
setUserProfile(profile);
}
}
}
} catch (error) {
console.log("error", error);
} finally {
setIsLoading(false);
}
};
userProfile();
}, [setUserProfile]);
return {
isLoading,
userProfile, setUserProfile
};
};
export default useGetUserProfile;
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const firebaseConfig = {
//config properties
};
// Initialize Firebase
export const app = initializeApp(firebaseConfig);
// Initialize Firebase Authentication and get a reference to the service
export const auth = getAuth(app);
And unit test:
// Mocking Firebase Firestore methods
jest.mock('firebase/firestore', () => ({
getFirestore: jest.fn(),
doc: jest.fn(),
getDoc: jest.fn(),
}));
// Mocking localStorage
beforeEach(() => {
jest.spyOn(Storage.prototype, 'getItem').mockImplementation(() => null); // Reset localStorage
});
describe('useGetUserProfile', () => {
it.only('should get user profile from localStorage if available', async () => {
// Mock localStorage with a mock profile
const mockProfile = {
email: '[email protected]',
username: 'testuser',
userBio: 'Bio info',
dob: new Date('1990-01-01'),
gender: 'Male',
sexo: 'M',
education: 'Bachelor',
drinkingHabits: 'Occasionally',
smokingHabits: 'Non-smoker',
};
jest.spyOn(Storage.prototype, 'getItem').mockImplementationOnce(() => JSON.stringify(mockProfile));
const { result } = renderHook(() => useGetUserProfile());
expect(result.current.userProfile).toEqual(mockProfile);
});
I'm only getting the error when running the unit test, I think there must be an error on how I set up my Firebase config, but I'm not sure.
The actual error is it's displaying is in a separte hook that gets the UserId, however the unit tests for that hook is not failing.
Here's the actual line of code that's causing errors in a separate Hook's unit test.
useEffect(() => {
const storedUserId = localStorage.getItem("USER_CREDENTIALS");
if (storedUserId) {
setUserId(storedUserId);
} else {
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if (user) {
const uid = user.uid;
localStorage.setItem("USER_CREDENTIALS", uid);
setUserId(uid);
}
});
return () => unsubscribe();
}
}, [userId]);
return userId;
};
Based on the code you provided, as long as local storage contains an entry for "USER_CREDENTIALS"
, the logic that is throwing the error is skipped entirely.
When you mock localStorage.getItem()
to always return null
, you now cause the faulty logic to execute.
useEffect(() => {
const storedUserId = localStorage.getItem("USER_CREDENTIALS");
if (storedUserId) { // this becomes null
setUserId(storedUserId);
} else { // this block gets executed
const unsubscribe = firebase.auth().onAuthStateChanged((user) => { // this line throws the error
if (user) {
const uid = user.uid;
localStorage.setItem("USER_CREDENTIALS", uid);
setUserId(uid);
}
});
return () => unsubscribe();
}
}, [userId]);
To update this legacy logic to the new syntax, you'd use the following:
import { getAuth, onAuthStateChanged } from "firebase/auth";
useEffect(() => {
const storedUserId = localStorage.getItem("USER_CREDENTIALS");
if (storedUserId) {
setUserId(storedUserId);
} else {
const unsubscribe = onAuthStateChanged(getAuth(), (user) => { // line updated to new syntax
if (user) {
const uid = user.uid;
localStorage.setItem("USER_CREDENTIALS", uid);
setUserId(uid);
}
});
return () => unsubscribe();
}
}, [userId]);