Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react/src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export { useApplyActionCodeMutation } from "./useApplyActionCodeMutation";
// useCheckActionCodeMutation
// useConfirmPasswordResetMutation
export { useCreateUserWithEmailAndPasswordMutation } from "./useCreateUserWithEmailAndPasswordMutation";
// useFetchSignInMethodsForEmailQuery
export { useFetchSignInMethodsForEmailQuery } from "./useFetchSignInMethodsForEmailQuery";
export { useConfirmPasswordResetMutation } from "./useConfirmPasswordResetMutation";
// useCreateUserWithEmailAndPasswordMutation
// useGetRedirectResultQuery
Expand Down
130 changes: 130 additions & 0 deletions packages/react/src/auth/useFetchSignInMethodsForEmailQuery.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { renderHook, waitFor } from "@testing-library/react";
import { createUserWithEmailAndPassword } from "firebase/auth";
import { useFetchSignInMethodsForEmailQuery } from "./useFetchSignInMethodsForEmailQuery";
import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
import { auth, expectFirebaseError, wipeAuth } from "~/testing-utils";
import { queryClient, wrapper } from "../../utils";

describe("useFetchSignInMethodsForEmailQuery", () => {
const email = "tqf@invertase.io";
const password = "TanstackQueryFirebase#123";

beforeEach(async () => {
queryClient.clear();
await wipeAuth();
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
);
});

afterEach(async () => {
vi.clearAllMocks();
await auth.signOut();
});

test("should fetch sign in methods for existing user", async () => {
const { result } = renderHook(
() =>
useFetchSignInMethodsForEmailQuery(auth, email, {
queryKey: ["signInMethods", email],
}),
{ wrapper }
);

expect(result.current.isLoading).toBe(true);

await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
});

expect(result.current.data).toContain("password");
});

test("should return empty array for non-existent user", async () => {
const nonExistentEmail = "nonexistent@example.com";

const { result } = renderHook(
() =>
useFetchSignInMethodsForEmailQuery(auth, nonExistentEmail, {
queryKey: ["signInMethods", nonExistentEmail],
}),
{ wrapper }
);

await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
});

expect(result.current.data).toEqual([]);
});

test("should not fetch when enabled is false", () => {
const { result } = renderHook(
() =>
useFetchSignInMethodsForEmailQuery(auth, email, {
queryKey: ["signInMethods", email],
enabled: false,
}),
{ wrapper }
);

expect(result.current.isLoading).toBe(false);
expect(result.current.isSuccess).toBe(false);
expect(result.current.isError).toBe(false);
expect(result.current.data).toBeUndefined();
});

test("should refetch when email changes", async () => {
const newEmail = "another@example.com";

await createUserWithEmailAndPassword(auth, newEmail, email);

const { result, rerender } = renderHook(
({ email }) =>
useFetchSignInMethodsForEmailQuery(auth, email, {
queryKey: ["signInMethods", email],
}),
{
wrapper,
initialProps: { email: email },
}
);

expect(result.current.isLoading).toBe(true);

await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
expect(result.current.isLoading).toBe(false);
});
expect(result.current.data).toContain("password");

rerender({ email: newEmail });

expect(result.current.isLoading).toBe(true);

await waitFor(() => {
expect(result.current.data).toContain("password");
expect(result.current.isLoading).toBe(false);
});
});

test("should handle invalid email error", async () => {
const invalidEmail = "not-an-email";

const { result } = renderHook(
() =>
useFetchSignInMethodsForEmailQuery(auth, invalidEmail, {
queryKey: ["signInMethods", invalidEmail],
}),
{ wrapper }
);

await waitFor(() => {
expect(result.current.isError).toBe(true);
});

expectFirebaseError(result.current.error, "auth/invalid-email");
});
});
23 changes: 23 additions & 0 deletions packages/react/src/auth/useFetchSignInMethodsForEmailQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
import {
fetchSignInMethodsForEmail,
type Auth,
type AuthError,
} from "firebase/auth";

type AuthUseQueryOptions<
TData = unknown,
TError = Error,
TVariables = void
> = Omit<UseQueryOptions<TData, TError, TVariables>, "queryFn">;

export function useFetchSignInMethodsForEmailQuery(
auth: Auth,
email: string,
options: AuthUseQueryOptions<string[], AuthError, void>
) {
return useQuery<string[], AuthError, void>({
...options,
queryFn: () => fetchSignInMethodsForEmail(auth, email),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The email should be passed as a mutation value

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this have change to be a mutation?

Currently, this is a query versus mutations for which a user can call a mutate() or a mutateAsync method.

});
}