Skip to content

Commit 62a99e6

Browse files
committed
feat(react/auth): add useFetchSignInMethodsForEmailQuery
1 parent 2f0ca8b commit 62a99e6

File tree

3 files changed

+154
-1
lines changed

3 files changed

+154
-1
lines changed

packages/react/src/auth/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export { useApplyActionCodeMutation } from "./useApplyActionCodeMutation";
1515
// useCheckActionCodeMutation
1616
// useConfirmPasswordResetMutation
1717
export { useCreateUserWithEmailAndPasswordMutation } from "./useCreateUserWithEmailAndPasswordMutation";
18-
// useFetchSignInMethodsForEmailQuery
18+
export { useFetchSignInMethodsForEmailQuery } from "./useFetchSignInMethodsForEmailQuery";
1919
export { useConfirmPasswordResetMutation } from "./useConfirmPasswordResetMutation";
2020
// useCreateUserWithEmailAndPasswordMutation
2121
// useGetRedirectResultQuery
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { renderHook, waitFor } from "@testing-library/react";
2+
import { createUserWithEmailAndPassword } from "firebase/auth";
3+
import { useFetchSignInMethodsForEmailQuery } from "./useFetchSignInMethodsForEmailQuery";
4+
import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
5+
import { auth, expectFirebaseError, wipeAuth } from "~/testing-utils";
6+
import { queryClient, wrapper } from "../../utils";
7+
8+
describe("useFetchSignInMethodsForEmailQuery", () => {
9+
const email = "[email protected]";
10+
const password = "TanstackQueryFirebase#123";
11+
12+
beforeEach(async () => {
13+
queryClient.clear();
14+
await wipeAuth();
15+
const userCredential = await createUserWithEmailAndPassword(
16+
auth,
17+
email,
18+
password
19+
);
20+
});
21+
22+
afterEach(async () => {
23+
vi.clearAllMocks();
24+
await auth.signOut();
25+
});
26+
27+
test("should fetch sign in methods for existing user", async () => {
28+
const { result } = renderHook(
29+
() =>
30+
useFetchSignInMethodsForEmailQuery(auth, email, {
31+
queryKey: ["signInMethods", email],
32+
}),
33+
{ wrapper }
34+
);
35+
36+
expect(result.current.isLoading).toBe(true);
37+
38+
await waitFor(() => {
39+
expect(result.current.isSuccess).toBe(true);
40+
});
41+
42+
expect(result.current.data).toContain("password");
43+
});
44+
45+
test("should return empty array for non-existent user", async () => {
46+
const nonExistentEmail = "[email protected]";
47+
48+
const { result } = renderHook(
49+
() =>
50+
useFetchSignInMethodsForEmailQuery(auth, nonExistentEmail, {
51+
queryKey: ["signInMethods", nonExistentEmail],
52+
}),
53+
{ wrapper }
54+
);
55+
56+
await waitFor(() => {
57+
expect(result.current.isSuccess).toBe(true);
58+
});
59+
60+
expect(result.current.data).toEqual([]);
61+
});
62+
63+
test("should not fetch when enabled is false", () => {
64+
const { result } = renderHook(
65+
() =>
66+
useFetchSignInMethodsForEmailQuery(auth, email, {
67+
queryKey: ["signInMethods", email],
68+
enabled: false,
69+
}),
70+
{ wrapper }
71+
);
72+
73+
expect(result.current.isLoading).toBe(false);
74+
expect(result.current.isSuccess).toBe(false);
75+
expect(result.current.isError).toBe(false);
76+
expect(result.current.data).toBeUndefined();
77+
});
78+
79+
test("should refetch when email changes", async () => {
80+
const newEmail = "[email protected]";
81+
82+
await createUserWithEmailAndPassword(auth, newEmail, email);
83+
84+
const { result, rerender } = renderHook(
85+
({ email }) =>
86+
useFetchSignInMethodsForEmailQuery(auth, email, {
87+
queryKey: ["signInMethods", email],
88+
}),
89+
{
90+
wrapper,
91+
initialProps: { email: email },
92+
}
93+
);
94+
95+
expect(result.current.isLoading).toBe(true);
96+
97+
await waitFor(() => {
98+
expect(result.current.isSuccess).toBe(true);
99+
expect(result.current.isLoading).toBe(false);
100+
});
101+
expect(result.current.data).toContain("password");
102+
103+
rerender({ email: newEmail });
104+
105+
expect(result.current.isLoading).toBe(true);
106+
107+
await waitFor(() => {
108+
expect(result.current.data).toContain("password");
109+
expect(result.current.isLoading).toBe(false);
110+
});
111+
});
112+
113+
test("should handle invalid email error", async () => {
114+
const invalidEmail = "not-an-email";
115+
116+
const { result } = renderHook(
117+
() =>
118+
useFetchSignInMethodsForEmailQuery(auth, invalidEmail, {
119+
queryKey: ["signInMethods", invalidEmail],
120+
}),
121+
{ wrapper }
122+
);
123+
124+
await waitFor(() => {
125+
expect(result.current.isError).toBe(true);
126+
});
127+
128+
expectFirebaseError(result.current.error, "auth/invalid-email");
129+
});
130+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
2+
import {
3+
fetchSignInMethodsForEmail,
4+
type Auth,
5+
type AuthError,
6+
} from "firebase/auth";
7+
8+
type AuthUseQueryOptions<
9+
TData = unknown,
10+
TError = Error,
11+
TVariables = void
12+
> = Omit<UseQueryOptions<TData, TError, TVariables>, "queryFn">;
13+
14+
export function useFetchSignInMethodsForEmailQuery(
15+
auth: Auth,
16+
email: string,
17+
options: AuthUseQueryOptions<string[], AuthError, void>
18+
) {
19+
return useQuery<string[], AuthError, void>({
20+
...options,
21+
queryFn: async () => fetchSignInMethodsForEmail(auth, email),
22+
});
23+
}

0 commit comments

Comments
 (0)