Skip to content

Commit ce3d81c

Browse files
committed
Merge branch 'next' into ft-fix-tests
2 parents 898cb14 + b424dd1 commit ce3d81c

19 files changed

+2934
-2545
lines changed

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
},
1010
"devDependencies": {
1111
"@tanstack/react-query": "^5.55.4",
12+
"@types/jsonwebtoken": "^9.0.7",
13+
"@vitest/coverage-istanbul": "^2.0.5",
1214
"firebase": "^10.13.1",
15+
"happy-dom": "^15.7.3",
16+
"jsonwebtoken": "^9.0.2",
1317
"react": "^18.3.1",
1418
"tsup": "^8.2.4",
1519
"typescript": "^5.6.2",
16-
"vitest": "^2.0.5",
17-
"happy-dom": "^15.7.3",
18-
"@vitest/coverage-istanbul": "^2.0.5"
20+
"vitest": "^2.0.5"
1921
}
2022
}

packages/react/src/auth/index.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,22 @@
1919
// useSendPasswordResetEmailMutation
2020
export { useSendSignInLinkToEmailMutation } from "./useSendSignInLinkToEmailMutation";
2121
export { useSignInAnonymouslyMutation } from "./useSignInAnonymouslyMutation";
22-
// useSignInWithCredentialMutation
22+
export { useSignInWithCredentialMutation } from "./useSignInWithCredentialMutation";
2323
// useSignInWithCustomTokenMutation
24-
// useSignInWithEmailAndPasswordMutation
24+
export { useSignInWithEmailAndPasswordMutation } from "./useSignInWithEmailAndPasswordMutation";
2525
// useSignInWithEmailLinkMutation
2626
// useSignInWithPhoneNumberMutation
2727
// useSignInWithPopupMutation
2828
// useSignInWithRedirectMutation
29-
// useSignOutMutation
29+
export { useSignOutMutation } from "./useSignOutMutation";
3030
// useUpdateCurrentUserMutation
31+
// useSignOutMutation
32+
export { useUpdateCurrentUserMutation } from "./useUpdateCurrentUserMutation";
3133
// useValidatePasswordMutation
3234
// useVerifyPasswordResetCodeMutation
35+
export { useVerifyPasswordResetCodeMutation } from "./useVerifyPasswordResetCodeMutation";
3336
// useDeleteUserMutation
37+
export { useDeleteUserMutation } from "./useDeleteUserMutation";
3438
// useLinkWithCredentialMutation
3539
// useLinkWithPhoneNumberMutation
3640
// useLinkWithPopupMutation
@@ -39,7 +43,7 @@ export { useSignInAnonymouslyMutation } from "./useSignInAnonymouslyMutation";
3943
// useReauthenticateWithCredentialMutation
4044
// useReauthenticateWithPopupMutation
4145
// useReauthenticateWithRedirectMutation
42-
// useReloadMutation
46+
export { useReloadMutation } from "./useReloadMutation";
4347
// useSendEmailVerificationMutation
4448
// useUnlinkMutation
4549
// useUpdateEmailMutation
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import React from "react";
2+
import { describe, expect, test, beforeEach, afterEach, vi } from "vitest";
3+
import { renderHook, act, waitFor } from "@testing-library/react";
4+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
5+
import { auth, wipeAuth } from "~/testing-utils";
6+
import { createUserWithEmailAndPassword, type User } from "firebase/auth";
7+
import { useDeleteUserMutation } from "./useDeleteUserMutation";
8+
9+
const queryClient = new QueryClient({
10+
defaultOptions: {
11+
queries: { retry: false },
12+
mutations: { retry: false },
13+
},
14+
});
15+
16+
const wrapper = ({ children }: { children: React.ReactNode }) => (
17+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
18+
);
19+
20+
describe("useVerifyPasswordResetCodeMutation", () => {
21+
const email = "[email protected]";
22+
const password = "TanstackQueryFirebase#123";
23+
let user: User;
24+
25+
beforeEach(async () => {
26+
queryClient.clear();
27+
await wipeAuth();
28+
const userCredential = await createUserWithEmailAndPassword(
29+
auth,
30+
email,
31+
password
32+
);
33+
user = userCredential.user;
34+
});
35+
36+
afterEach(async () => {
37+
vi.clearAllMocks();
38+
await auth.signOut();
39+
});
40+
41+
test("successfully verifies the reset code", async () => {
42+
const { result } = renderHook(() => useDeleteUserMutation(auth), {
43+
wrapper,
44+
});
45+
46+
await act(async () => {
47+
result.current.mutate(user);
48+
});
49+
50+
await waitFor(() => expect(result.current.isSuccess).toBe(true));
51+
52+
expect(result.current.data).toBeUndefined();
53+
});
54+
55+
test("resets mutation state correctly", async () => {
56+
const { result } = renderHook(() => useDeleteUserMutation(auth), {
57+
wrapper,
58+
});
59+
60+
act(() => {
61+
result.current.mutate(user);
62+
});
63+
64+
await waitFor(() => {
65+
expect(result.current.isSuccess).toBe(true);
66+
});
67+
68+
act(() => {
69+
result.current.reset();
70+
});
71+
72+
await waitFor(() => {
73+
expect(result.current.isIdle).toBe(true);
74+
expect(result.current.data).toBeUndefined();
75+
expect(result.current.error).toBeNull();
76+
});
77+
});
78+
79+
test("should call onSuccess when the user is successfully deleted", async () => {
80+
const onSuccess = vi.fn();
81+
82+
const { result } = renderHook(
83+
() =>
84+
useDeleteUserMutation(auth, {
85+
onSuccess,
86+
}),
87+
{
88+
wrapper,
89+
}
90+
);
91+
92+
act(() => {
93+
result.current.mutate(user);
94+
});
95+
96+
await waitFor(() => expect(result.current.isSuccess).toBe(true));
97+
98+
expect(onSuccess).toHaveBeenCalledTimes(1);
99+
expect(result.current.data).toBeUndefined();
100+
});
101+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useMutation, type UseMutationOptions } from "@tanstack/react-query";
2+
import { Auth, type AuthError, deleteUser, type User } from "firebase/auth";
3+
4+
type AuthUMutationOptions<
5+
TData = unknown,
6+
TError = Error,
7+
TVariables = void
8+
> = Omit<UseMutationOptions<TData, TError, TVariables>, "mutationFn">;
9+
10+
export function useDeleteUserMutation(
11+
auth: Auth,
12+
options?: AuthUMutationOptions<void, AuthError, User>
13+
) {
14+
return useMutation<void, AuthError, User>({
15+
...options,
16+
mutationFn: (user: User) => deleteUser(user),
17+
});
18+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React from "react";
2+
import { describe, expect, test, beforeEach, afterEach, vi } from "vitest";
3+
import { renderHook, act, waitFor } from "@testing-library/react";
4+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
5+
import { auth, wipeAuth } from "~/testing-utils";
6+
import {
7+
createUserWithEmailAndPassword,
8+
signInWithEmailAndPassword,
9+
type User,
10+
} from "firebase/auth";
11+
import { useReloadMutation } from "./useReloadMutation";
12+
13+
const queryClient = new QueryClient({
14+
defaultOptions: {
15+
queries: { retry: false },
16+
mutations: { retry: false },
17+
},
18+
});
19+
20+
const wrapper = ({ children }: { children: React.ReactNode }) => (
21+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
22+
);
23+
24+
describe("useReloadMutation", () => {
25+
const email = "[email protected]";
26+
const password = "TanstackQueryFirebase#123";
27+
let user: User;
28+
beforeEach(async () => {
29+
queryClient.clear();
30+
await wipeAuth();
31+
const userCredential = await createUserWithEmailAndPassword(
32+
auth,
33+
email,
34+
password
35+
);
36+
user = userCredential.user;
37+
});
38+
39+
afterEach(async () => {
40+
vi.clearAllMocks();
41+
await auth.signOut();
42+
});
43+
44+
test.sequential("should successfully reloads user data", async () => {
45+
await signInWithEmailAndPassword(auth, email, password);
46+
47+
const { result } = renderHook(() => useReloadMutation(), { wrapper });
48+
49+
act(() => result.current.mutate(user));
50+
51+
await waitFor(() => expect(result.current.isSuccess).toBe(true));
52+
});
53+
54+
test("should handle onSuccess callback", async () => {
55+
await signInWithEmailAndPassword(auth, email, password);
56+
57+
const onSuccess = vi.fn();
58+
const { result } = renderHook(() => useReloadMutation({ onSuccess }), {
59+
wrapper,
60+
});
61+
62+
act(() => {
63+
result.current.mutate(user);
64+
});
65+
66+
await waitFor(() => expect(result.current.isSuccess).toBe(true));
67+
68+
expect(onSuccess).toHaveBeenCalled();
69+
});
70+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useMutation, type UseMutationOptions } from "@tanstack/react-query";
2+
import { AuthError, reload, type User } from "firebase/auth";
3+
4+
type AuthMutationOptions<
5+
TData = unknown,
6+
TError = Error,
7+
TVariables = void
8+
> = Omit<UseMutationOptions<TData, TError, TVariables>, "mutationFn">;
9+
10+
export function useReloadMutation(
11+
options?: AuthMutationOptions<void, AuthError, User>
12+
) {
13+
return useMutation<void, AuthError, User>({
14+
...options,
15+
mutationFn: (user: User) => reload(user),
16+
});
17+
}

0 commit comments

Comments
 (0)