From 10f339502e85fb3e3e7e85d52d23340a99140bd5 Mon Sep 17 00:00:00 2001 From: HassanBahati Date: Wed, 9 Oct 2024 07:07:10 +0300 Subject: [PATCH 1/2] feat(useSendSignInLinkToEmailMutation): add useSendSignInLinkToEmailMutation hook --- packages/react/src/auth/index.ts | 3 +- .../useSendSignInLinkToEmailMutation.test.tsx | 119 ++++++++++++++++++ .../auth/useSendSignInLinkToEmailMutation.ts | 24 ++++ vitest/utils.ts | 1 + 4 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 packages/react/src/auth/useSendSignInLinkToEmailMutation.test.tsx create mode 100644 packages/react/src/auth/useSendSignInLinkToEmailMutation.ts diff --git a/packages/react/src/auth/index.ts b/packages/react/src/auth/index.ts index 632083b7..24520331 100644 --- a/packages/react/src/auth/index.ts +++ b/packages/react/src/auth/index.ts @@ -17,7 +17,7 @@ // useGetRedirectResultQuery // useRevokeAccessTokenMutation // useSendPasswordResetEmailMutation -// useSendSignInLinkToEmailMutation +export { useSendSignInLinkToEmailMutation } from "./useSendSignInLinkToEmailMutation"; export { useSignInAnonymouslyMutation } from "./useSignInAnonymouslyMutation"; // useSignInWithCredentialMutation // useSignInWithCustomTokenMutation @@ -46,4 +46,3 @@ export { useSignInAnonymouslyMutation } from "./useSignInAnonymouslyMutation"; // useUpdatePasswordMutation // useUpdateProfileMutation // useVerifyBeforeUpdateEmailMutation - diff --git a/packages/react/src/auth/useSendSignInLinkToEmailMutation.test.tsx b/packages/react/src/auth/useSendSignInLinkToEmailMutation.test.tsx new file mode 100644 index 00000000..418cbeff --- /dev/null +++ b/packages/react/src/auth/useSendSignInLinkToEmailMutation.test.tsx @@ -0,0 +1,119 @@ +import React from "react"; +import { describe, expect, test, beforeEach, afterEach } from "vitest"; +import { renderHook, act, waitFor } from "@testing-library/react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { useSendSignInLinkToEmailMutation } from "./useSendSignInLinkToEmailMutation"; +import { wipeAuth, auth } from "~/testing-utils"; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false }, + mutations: { retry: false }, + }, +}); + +const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} +); + +describe("useSendSignInLinkToEmailMutation", () => { + const email = "tanstack-query-firebase@invertase.io"; + const actionCodeSettings = { + url: `https://invertase.io/?email=${email}`, + iOS: { + bundleId: "com.example.ios", + }, + android: { + packageName: "com.example.android", + installApp: true, + minimumVersion: "12", + }, + handleCodeInApp: true, + }; + + beforeEach(async () => { + queryClient.clear(); + await wipeAuth(); + }); + + test("resets mutation state correctly", async () => { + const { result } = renderHook( + () => useSendSignInLinkToEmailMutation(auth, email, actionCodeSettings), + { wrapper } + ); + + act(() => { + result.current.mutate(); + }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + act(() => { + result.current.reset(); + }); + + await waitFor(() => { + expect(result.current.isIdle).toBe(true); + expect(result.current.data).toBeUndefined(); + expect(result.current.error).toBeNull(); + }); + }); + + test("successfully sends sign-in link to email", async () => { + const { result } = renderHook( + () => useSendSignInLinkToEmailMutation(auth, email, actionCodeSettings), + { wrapper } + ); + + act(() => { + result.current.mutate(); + }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + expect(result.current.isSuccess).toBe(true); + expect(result.current.error).toBeNull(); + }); + + test("allows multiple sequential send attempts", async () => { + const { result } = renderHook( + () => useSendSignInLinkToEmailMutation(auth, email, actionCodeSettings), + { wrapper } + ); + + // First attempt + act(() => { + result.current.mutate(); + }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + // Reset state + act(() => { + result.current.reset(); + }); + + await waitFor(() => { + expect(result.current.isIdle).toBe(true); + expect(result.current.data).toBeUndefined(); + expect(result.current.error).toBeNull(); + }); + + // Second attempt + act(() => { + result.current.mutate(); + }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + expect(result.current.error).toBeNull(); + }); +}); diff --git a/packages/react/src/auth/useSendSignInLinkToEmailMutation.ts b/packages/react/src/auth/useSendSignInLinkToEmailMutation.ts new file mode 100644 index 00000000..8f5c8e36 --- /dev/null +++ b/packages/react/src/auth/useSendSignInLinkToEmailMutation.ts @@ -0,0 +1,24 @@ +import { useMutation, type UseMutationOptions } from "@tanstack/react-query"; +import { + type ActionCodeSettings, + type Auth, + AuthError, + sendSignInLinkToEmail, +} from "firebase/auth"; + +type AuthUseMutationOptions = Omit< + UseMutationOptions, + "mutationFn" +>; + +export function useSendSignInLinkToEmailMutation( + auth: Auth, + email: string, + actionCodeSettings: ActionCodeSettings, + options?: AuthUseMutationOptions +) { + return useMutation({ + ...options, + mutationFn: () => sendSignInLinkToEmail(auth, email, actionCodeSettings), + }); +} diff --git a/vitest/utils.ts b/vitest/utils.ts index d42d3996..b9745157 100644 --- a/vitest/utils.ts +++ b/vitest/utils.ts @@ -10,6 +10,7 @@ import { expect } from "vitest"; const firebaseTestingOptions = { projectId: "test-project", apiKey: "test-api-key", + authDomain: "test-auth-domain", }; let app: FirebaseApp | undefined; From 064bcf1d7bddf5926a109297527e2029f8459735 Mon Sep 17 00:00:00 2001 From: HassanBahati Date: Wed, 9 Oct 2024 12:36:47 +0300 Subject: [PATCH 2/2] refactor(useSendSignInLinkToEmailMutation): allow passing reactive values to the mutation function --- .../useSendSignInLinkToEmailMutation.test.tsx | 16 ++++++------- .../auth/useSendSignInLinkToEmailMutation.ts | 23 +++++++++++-------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/packages/react/src/auth/useSendSignInLinkToEmailMutation.test.tsx b/packages/react/src/auth/useSendSignInLinkToEmailMutation.test.tsx index 418cbeff..7109d02a 100644 --- a/packages/react/src/auth/useSendSignInLinkToEmailMutation.test.tsx +++ b/packages/react/src/auth/useSendSignInLinkToEmailMutation.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { describe, expect, test, beforeEach, afterEach } from "vitest"; +import { describe, expect, test, beforeEach } from "vitest"; import { renderHook, act, waitFor } from "@testing-library/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { useSendSignInLinkToEmailMutation } from "./useSendSignInLinkToEmailMutation"; @@ -38,12 +38,12 @@ describe("useSendSignInLinkToEmailMutation", () => { test("resets mutation state correctly", async () => { const { result } = renderHook( - () => useSendSignInLinkToEmailMutation(auth, email, actionCodeSettings), + () => useSendSignInLinkToEmailMutation(auth), { wrapper } ); act(() => { - result.current.mutate(); + result.current.mutate({ email, actionCodeSettings }); }); await waitFor(() => { @@ -63,12 +63,12 @@ describe("useSendSignInLinkToEmailMutation", () => { test("successfully sends sign-in link to email", async () => { const { result } = renderHook( - () => useSendSignInLinkToEmailMutation(auth, email, actionCodeSettings), + () => useSendSignInLinkToEmailMutation(auth), { wrapper } ); act(() => { - result.current.mutate(); + result.current.mutate({ email, actionCodeSettings }); }); await waitFor(() => { @@ -81,13 +81,13 @@ describe("useSendSignInLinkToEmailMutation", () => { test("allows multiple sequential send attempts", async () => { const { result } = renderHook( - () => useSendSignInLinkToEmailMutation(auth, email, actionCodeSettings), + () => useSendSignInLinkToEmailMutation(auth), { wrapper } ); // First attempt act(() => { - result.current.mutate(); + result.current.mutate({ email, actionCodeSettings }); }); await waitFor(() => { @@ -107,7 +107,7 @@ describe("useSendSignInLinkToEmailMutation", () => { // Second attempt act(() => { - result.current.mutate(); + result.current.mutate({ email, actionCodeSettings }); }); await waitFor(() => { diff --git a/packages/react/src/auth/useSendSignInLinkToEmailMutation.ts b/packages/react/src/auth/useSendSignInLinkToEmailMutation.ts index 8f5c8e36..90b2289b 100644 --- a/packages/react/src/auth/useSendSignInLinkToEmailMutation.ts +++ b/packages/react/src/auth/useSendSignInLinkToEmailMutation.ts @@ -6,19 +6,24 @@ import { sendSignInLinkToEmail, } from "firebase/auth"; -type AuthUseMutationOptions = Omit< - UseMutationOptions, - "mutationFn" ->; +type SendSignInLinkParams = { + email: string; + actionCodeSettings: ActionCodeSettings; +}; + +type AuthUseMutationOptions< + TData = unknown, + TError = Error, + TVariables = void +> = Omit, "mutationFn">; export function useSendSignInLinkToEmailMutation( auth: Auth, - email: string, - actionCodeSettings: ActionCodeSettings, - options?: AuthUseMutationOptions + options?: AuthUseMutationOptions ) { - return useMutation({ + return useMutation({ ...options, - mutationFn: () => sendSignInLinkToEmail(auth, email, actionCodeSettings), + mutationFn: ({ email, actionCodeSettings }) => + sendSignInLinkToEmail(auth, email, actionCodeSettings), }); }