From 7284cbd85eaa0555170dea96c8e1ef9132a6e341 Mon Sep 17 00:00:00 2001 From: HassanBahati Date: Mon, 20 Jan 2025 11:12:07 +0300 Subject: [PATCH 1/2] feat(react): add useUserGetIdTokenMutation --- packages/react/src/auth/index.ts | 2 +- .../auth/useUserGetIdTokenMutation.test.tsx | 96 +++++++++++++++++++ .../src/auth/useUserGetIdTokenMutation.ts | 25 +++++ 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 packages/react/src/auth/useUserGetIdTokenMutation.test.tsx create mode 100644 packages/react/src/auth/useUserGetIdTokenMutation.ts diff --git a/packages/react/src/auth/index.ts b/packages/react/src/auth/index.ts index 024d9a34..d10c2603 100644 --- a/packages/react/src/auth/index.ts +++ b/packages/react/src/auth/index.ts @@ -2,7 +2,7 @@ // useConfirmationResultConfirmMutation (ConfirmationResult) // useUserDeleteMutation (User) // userUserGetIdTokenResultMutation (User) -// useUserGetIdTokenMutation (User) +export { useUserGetIdTokenMutation } from "./useUserGetIdTokenMutation"; // useUserReloadMutation (User) // useVerifyPhoneNumberMutation (PhoneAuthProvider) // useMultiFactorUserEnrollMutation (MultiFactorUser) diff --git a/packages/react/src/auth/useUserGetIdTokenMutation.test.tsx b/packages/react/src/auth/useUserGetIdTokenMutation.test.tsx new file mode 100644 index 00000000..05d8dcb4 --- /dev/null +++ b/packages/react/src/auth/useUserGetIdTokenMutation.test.tsx @@ -0,0 +1,96 @@ +import { act, renderHook, waitFor } from "@testing-library/react"; +import { + createUserWithEmailAndPassword, + signInWithEmailAndPassword, +} from "firebase/auth"; +import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; +import { auth, wipeAuth } from "~/testing-utils"; +import { useUserGetIdTokenMutation } from "./useUserGetIdTokenMutation"; +import { queryClient, wrapper } from "../../utils"; + +describe("useUserGetIdTokenMutation", () => { + const email = "tqf@invertase.io"; + const password = "TanstackQueryFirebase#123"; + + beforeEach(async () => { + queryClient.clear(); + await wipeAuth(); + await createUserWithEmailAndPassword(auth, email, password); + }); + + afterEach(async () => { + vi.clearAllMocks(); + await auth.signOut(); + }); + + test("successfully retrieves an ID token", async () => { + const userCredential = await signInWithEmailAndPassword( + auth, + email, + password + ); + const { user } = userCredential; + + const { result } = renderHook(() => useUserGetIdTokenMutation(user), { + wrapper, + }); + + await act(async () => { + await result.current.mutateAsync(); + }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(typeof result.current.data).toBe("string"); + expect(result.current.data?.length).toBeGreaterThan(0); + }); + + test("can get token with forceRefresh option", async () => { + const userCredential = await signInWithEmailAndPassword( + auth, + email, + password + ); + const { user } = userCredential; + + const { result } = renderHook( + () => + useUserGetIdTokenMutation(user, { + auth: { forceRefresh: true }, + }), + { wrapper } + ); + + await act(async () => { + await result.current.mutateAsync(); + }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(typeof result.current.data).toBe("string"); + expect(result.current.data?.length).toBeGreaterThan(0); + }); + + test("executes onSuccess callback", async () => { + const userCredential = await signInWithEmailAndPassword( + auth, + email, + password + ); + const { user } = userCredential; + const onSuccess = vi.fn(); + + const { result } = renderHook( + () => useUserGetIdTokenMutation(user, { onSuccess }), + { wrapper } + ); + + await act(async () => { + await result.current.mutateAsync(); + }); + + await waitFor(() => expect(onSuccess).toHaveBeenCalled()); + expect(typeof onSuccess.mock.calls[0][0]).toBe("string"); + expect(onSuccess.mock.calls[0][0].length).toBeGreaterThan(0); + }); +}); diff --git a/packages/react/src/auth/useUserGetIdTokenMutation.ts b/packages/react/src/auth/useUserGetIdTokenMutation.ts new file mode 100644 index 00000000..d614271f --- /dev/null +++ b/packages/react/src/auth/useUserGetIdTokenMutation.ts @@ -0,0 +1,25 @@ +import { type UseMutationOptions, useMutation } from "@tanstack/react-query"; +import { type User, type AuthError, getIdToken } from "firebase/auth"; + +type AuthUseMutationOptions< + TData = unknown, + TError = Error, + TVariables = void +> = Omit, "mutationFn"> & { + auth?: { + forceRefresh?: boolean; + }; +}; + +export function useUserGetIdTokenMutation( + user: User, + options?: AuthUseMutationOptions +) { + const { auth, ...mutationOptions } = options || {}; + const forceRefresh = auth?.forceRefresh; + + return useMutation({ + ...mutationOptions, + mutationFn: () => getIdToken(user, forceRefresh), + }); +} From 5cff970c6979e9b2d0d39bc2cfddd1ccc5f83ffc Mon Sep 17 00:00:00 2001 From: HassanBahati Date: Tue, 11 Feb 2025 17:34:53 +0300 Subject: [PATCH 2/2] refactor: move forceRefresh to mutation args --- .../auth/useUserGetIdTokenMutation.test.tsx | 22 ++++++++----------- .../src/auth/useUserGetIdTokenMutation.ts | 11 ++++------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/packages/react/src/auth/useUserGetIdTokenMutation.test.tsx b/packages/react/src/auth/useUserGetIdTokenMutation.test.tsx index 05d8dcb4..b79411e7 100644 --- a/packages/react/src/auth/useUserGetIdTokenMutation.test.tsx +++ b/packages/react/src/auth/useUserGetIdTokenMutation.test.tsx @@ -23,7 +23,7 @@ describe("useUserGetIdTokenMutation", () => { await auth.signOut(); }); - test("successfully retrieves an ID token", async () => { + test("successfully retrieves an ID token with forceRefresh true", async () => { const userCredential = await signInWithEmailAndPassword( auth, email, @@ -36,7 +36,7 @@ describe("useUserGetIdTokenMutation", () => { }); await act(async () => { - await result.current.mutateAsync(); + await result.current.mutateAsync(true); }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); @@ -45,7 +45,7 @@ describe("useUserGetIdTokenMutation", () => { expect(result.current.data?.length).toBeGreaterThan(0); }); - test("can get token with forceRefresh option", async () => { + test("successfully retrieves an ID token with forceRefresh false", async () => { const userCredential = await signInWithEmailAndPassword( auth, email, @@ -53,16 +53,12 @@ describe("useUserGetIdTokenMutation", () => { ); const { user } = userCredential; - const { result } = renderHook( - () => - useUserGetIdTokenMutation(user, { - auth: { forceRefresh: true }, - }), - { wrapper } - ); + const { result } = renderHook(() => useUserGetIdTokenMutation(user), { + wrapper, + }); await act(async () => { - await result.current.mutateAsync(); + await result.current.mutateAsync(false); }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); @@ -71,7 +67,7 @@ describe("useUserGetIdTokenMutation", () => { expect(result.current.data?.length).toBeGreaterThan(0); }); - test("executes onSuccess callback", async () => { + test("executes onSuccess callback with token", async () => { const userCredential = await signInWithEmailAndPassword( auth, email, @@ -86,7 +82,7 @@ describe("useUserGetIdTokenMutation", () => { ); await act(async () => { - await result.current.mutateAsync(); + await result.current.mutateAsync(true); }); await waitFor(() => expect(onSuccess).toHaveBeenCalled()); diff --git a/packages/react/src/auth/useUserGetIdTokenMutation.ts b/packages/react/src/auth/useUserGetIdTokenMutation.ts index d614271f..9adf6ec1 100644 --- a/packages/react/src/auth/useUserGetIdTokenMutation.ts +++ b/packages/react/src/auth/useUserGetIdTokenMutation.ts @@ -13,13 +13,10 @@ type AuthUseMutationOptions< export function useUserGetIdTokenMutation( user: User, - options?: AuthUseMutationOptions + options?: AuthUseMutationOptions ) { - const { auth, ...mutationOptions } = options || {}; - const forceRefresh = auth?.forceRefresh; - - return useMutation({ - ...mutationOptions, - mutationFn: () => getIdToken(user, forceRefresh), + return useMutation({ + ...options, + mutationFn: (forceRefresh?: boolean) => getIdToken(user, forceRefresh), }); }