Skip to content

Commit 8e49414

Browse files
committed
test(react): ForgotPasswordAuthForm tests
1 parent 0d37db4 commit 8e49414

File tree

2 files changed

+207
-255
lines changed

2 files changed

+207
-255
lines changed

packages/react/src/auth/forms/forgot-password-auth-form.test.tsx

Lines changed: 145 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -14,218 +14,198 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { describe, it, expect, vi, beforeEach, Mock } from "vitest";
18-
import { render, screen, fireEvent } from "@testing-library/react";
19-
import { ForgotPasswordAuthForm } from "./forgot-password-auth-form";
17+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
18+
import { render, screen, fireEvent, renderHook, cleanup } from "@testing-library/react";
19+
import { ForgotPasswordAuthForm, useForgotPasswordAuthForm, useForgotPasswordAuthFormAction } from "./forgot-password-auth-form";
2020
import { act } from "react";
21+
import { sendPasswordResetEmail } from "@firebase-ui/core";
22+
import { createFirebaseUIProvider, createMockUI } from "~/tests/utils";
23+
import { registerLocale } from "@firebase-ui/translations";
24+
import { FirebaseUIProvider } from "~/context";
2125

22-
// Mock the dependencies
2326
vi.mock("@firebase-ui/core", async (importOriginal) => {
2427
const mod = await importOriginal<typeof import("@firebase-ui/core")>();
2528
return {
2629
...mod,
27-
sendPasswordResetEmail: vi.fn().mockImplementation(() => {
28-
return Promise.resolve();
29-
}),
30-
// FirebaseUIError: class FirebaseUIError extends Error {
31-
// code: string;
32-
// constructor(error: any) {
33-
// super(error.message || "Unknown error");
34-
// this.name = "FirebaseUIError";
35-
// this.code = error.code || "unknown-error";
36-
// }
37-
// },
38-
// createForgotPasswordFormSchema: vi.fn().mockReturnValue({
39-
// email: { required: "Email is required" },
40-
// }),
30+
sendPasswordResetEmail: vi.fn(),
4131
};
4232
});
4333

44-
// Mock @tanstack/react-form library
45-
vi.mock("@tanstack/react-form", () => {
46-
const handleSubmitMock = vi.fn().mockImplementation((callback) => {
47-
// Store the callback to call it directly in tests
48-
(global as any).formSubmitCallback = callback;
49-
return Promise.resolve();
34+
describe("useForgotPasswordAuthFormAction", () => {
35+
beforeEach(() => {
36+
vi.clearAllMocks();
5037
});
5138

52-
return {
53-
useForm: vi.fn().mockImplementation(({ onSubmit }) => {
54-
// Save the onSubmit function to call it directly in tests
55-
(global as any).formOnSubmit = onSubmit;
56-
57-
return {
58-
handleSubmit: handleSubmitMock,
59-
Field: ({ children, name }: any) => {
60-
const field = {
61-
name,
62-
state: {
63-
value: name === "email" ? "[email protected]" : "",
64-
meta: {
65-
isTouched: false,
66-
errors: [],
67-
},
68-
},
69-
handleBlur: vi.fn(),
70-
handleChange: vi.fn(),
71-
};
72-
return children(field);
73-
},
74-
};
75-
}),
76-
};
77-
});
39+
it("should return a callback which accept an email", async () => {
40+
const sendPasswordResetEmailMock = vi.mocked(sendPasswordResetEmail);
41+
const mockUI = createMockUI();
7842

79-
vi.mock("../../../../src/hooks", () => ({
80-
useAuth: vi.fn().mockReturnValue({}),
81-
useUI: vi.fn().mockReturnValue({
82-
locale: "en-US",
83-
translations: {
84-
"en-US": {
85-
labels: {
86-
backToSignIn: "back button",
43+
const { result } = renderHook(() => useForgotPasswordAuthFormAction(), {
44+
wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }),
45+
});
46+
47+
await act(async () => {
48+
await result.current({ email: "[email protected]" });
49+
});
50+
51+
expect(sendPasswordResetEmailMock).toHaveBeenCalledWith(expect.any(Object), "[email protected]");
52+
});
53+
54+
it("should throw an unknown error when its not a FirebaseUIError", async () => {
55+
const sendPasswordResetEmailMock = vi
56+
.mocked(sendPasswordResetEmail)
57+
.mockRejectedValue(new Error("Unknown error"));
58+
59+
const mockUI = createMockUI({
60+
locale: registerLocale("es-ES", {
61+
errors: {
62+
unknownError: "unknownError",
8763
},
88-
},
89-
},
90-
}),
91-
}));
92-
93-
// Mock the components
94-
vi.mock("../../../../src/components/field-info", () => ({
95-
FieldInfo: vi
96-
.fn()
97-
.mockImplementation(({ field }) => (
98-
<div data-testid="field-info">
99-
{field.state.meta.errors.length > 0 && <span>{field.state.meta.errors[0]}</span>}
100-
</div>
101-
)),
102-
}));
103-
104-
vi.mock("../../../../src/components/policies", () => ({
105-
Policies: vi.fn().mockReturnValue(<div data-testid="policies" />),
106-
}));
107-
108-
vi.mock("../../../../src/components/button", () => ({
109-
Button: vi.fn().mockImplementation(({ children, type, onClick }) => (
110-
<button type={type} onClick={onClick} data-testid="submit-button">
111-
{children}
112-
</button>
113-
)),
114-
}));
115-
116-
// Import the actual functions after mocking
117-
import { sendPasswordResetEmail } from "@firebase-ui/core";
64+
}),
65+
});
66+
67+
const { result } = renderHook(() => useForgotPasswordAuthFormAction(), {
68+
wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }),
69+
});
70+
71+
await expect(async () => {
72+
await act(async () => {
73+
await result.current({ email: "[email protected]" });
74+
});
75+
}).rejects.toThrow("unknownError");
76+
77+
expect(sendPasswordResetEmailMock).toHaveBeenCalledWith(mockUI.get(), "[email protected]");
78+
});
79+
});
11880

119-
describe("ForgotPasswordForm", () => {
81+
describe("useForgotPasswordAuthForm", () => {
12082
beforeEach(() => {
12183
vi.clearAllMocks();
12284
});
12385

124-
it("renders the form correctly", () => {
125-
render(<ForgotPasswordAuthForm />);
126-
127-
expect(screen.getByRole("textbox", { name: /email address/i })).toBeInTheDocument();
128-
expect(screen.getByTestId("submit-button")).toBeInTheDocument();
86+
afterEach(() => {
87+
cleanup();
12988
});
13089

131-
it("submits the form when the button is clicked", async () => {
132-
render(<ForgotPasswordAuthForm />);
90+
it("should allow the form to be submitted", async () => {
91+
const mockUI = createMockUI();
92+
const sendPasswordResetEmailMock = vi.mocked(sendPasswordResetEmail);
13393

134-
// Get the submit button
135-
const submitButton = screen.getByTestId("submit-button");
94+
const { result } = renderHook(() => useForgotPasswordAuthForm(), {
95+
wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }),
96+
});
97+
98+
act(() => {
99+
result.current.setFieldValue("email", "[email protected]");
100+
});
136101

137-
// Trigger form submission
138102
await act(async () => {
139-
fireEvent.click(submitButton);
140-
141-
// Directly call the onSubmit function with form values
142-
if ((global as any).formOnSubmit) {
143-
await (global as any).formOnSubmit({
144-
value: {
145-
146-
},
147-
});
148-
}
103+
await result.current.handleSubmit();
149104
});
150105

151-
// Check that the password reset function was called
152-
expect(sendPasswordResetEmail).toHaveBeenCalledWith(expect.anything(), "[email protected]");
106+
expect(sendPasswordResetEmailMock).toHaveBeenCalledWith(mockUI.get(), "[email protected]");
153107
});
154108

155-
it("displays error message when password reset fails", async () => {
156-
// Mock the reset function to reject with an error
157-
const mockError = new Error("Invalid email");
158-
(sendPasswordResetEmail as Mock).mockRejectedValueOnce(mockError);
109+
it("should not allow the form to be submitted if the form is invalid", async () => {
110+
const mockUI = createMockUI();
111+
const sendPasswordResetEmailMock = vi.mocked(sendPasswordResetEmail);
159112

160-
render(<ForgotPasswordAuthForm />);
113+
const { result } = renderHook(() => useForgotPasswordAuthForm(), {
114+
wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }),
115+
});
161116

162-
// Get the submit button
163-
const submitButton = screen.getByTestId("submit-button");
117+
act(() => {
118+
result.current.setFieldValue("email", "123");
119+
});
164120

165-
// Trigger form submission
166121
await act(async () => {
167-
fireEvent.click(submitButton);
168-
169-
// Directly call the onSubmit function with form values
170-
if ((global as any).formOnSubmit) {
171-
await (global as any)
172-
.formOnSubmit({
173-
value: {
174-
175-
},
176-
})
177-
.catch(() => {
178-
// Catch the error here to prevent test from failing
179-
});
180-
}
122+
await result.current.handleSubmit();
181123
});
182124

183-
// Check that the password reset function was called
184-
expect(sendPasswordResetEmail).toHaveBeenCalled();
125+
expect(result.current.getFieldMeta("email")!.errors[0].length).toBeGreaterThan(0);
126+
expect(sendPasswordResetEmailMock).not.toHaveBeenCalled();
185127
});
128+
});
186129

187-
it("validates on blur for the first time", async () => {
188-
render(<ForgotPasswordAuthForm />);
189-
190-
const emailInput = screen.getByRole("textbox", { name: /email address/i });
130+
describe("<ForgotPasswordAuthForm />", () => {
131+
beforeEach(() => {
132+
vi.clearAllMocks();
133+
});
191134

192-
await act(async () => {
193-
fireEvent.blur(emailInput);
135+
it("should render the form correctly", () => {
136+
const mockUI = createMockUI({
137+
locale: registerLocale("test", {
138+
labels: {
139+
resetPassword: "resetPassword",
140+
},
141+
}),
194142
});
195143

196-
// Check that handleBlur was called
197-
expect((global as any).formOnSubmit).toBeDefined();
198-
});
144+
const { container } = render(
145+
<FirebaseUIProvider ui={mockUI}>
146+
<ForgotPasswordAuthForm />
147+
</FirebaseUIProvider>
148+
);
199149

200-
it("validates on input after first blur", async () => {
201-
render(<ForgotPasswordAuthForm />);
150+
// There should be only one form
151+
const form = container.querySelectorAll("form.fui-form");
152+
expect(form.length).toBe(1);
202153

203-
const emailInput = screen.getByRole("textbox", { name: /email address/i });
154+
// Make sure we have an email input
155+
expect(screen.getByRole("textbox", { name: /email/i })).toBeInTheDocument();
204156

205-
// First validation on blur
206-
await act(async () => {
207-
fireEvent.blur(emailInput);
208-
});
157+
// Ensure the "Reset Password" button is present and is a submit button
158+
const resetPasswordButton = screen.getByRole("button", { name: "resetPassword" });
159+
expect(resetPasswordButton).toBeInTheDocument();
160+
expect(resetPasswordButton).toHaveAttribute("type", "submit");
161+
});
209162

210-
// Then validation should happen on input
211-
await act(async () => {
212-
fireEvent.input(emailInput, { target: { value: "[email protected]" } });
163+
it("should render the back to sign in button callback when onBackToSignInClick is provided", () => {
164+
const mockUI = createMockUI({
165+
locale: registerLocale("test", {
166+
labels: {
167+
backToSignIn: "backToSignIn",
168+
},
169+
}),
213170
});
214171

215-
// Check that handleBlur and form.update were called
216-
expect((global as any).formOnSubmit).toBeDefined();
217-
});
218-
219-
// TODO: Fix this test
220-
it.skip("displays back to sign in button when provided", () => {
221172
const onBackToSignInClickMock = vi.fn();
222-
render(<ForgotPasswordAuthForm onBackToSignInClick={onBackToSignInClickMock} />);
223173

224-
const backButton = screen.getByText(/back button/i);
225-
expect(backButton).toHaveClass("fui-form__action");
226-
expect(backButton).toBeInTheDocument();
174+
render(
175+
<FirebaseUIProvider ui={mockUI}>
176+
<ForgotPasswordAuthForm onBackToSignInClick={onBackToSignInClickMock} />
177+
</FirebaseUIProvider>
178+
);
227179

228-
fireEvent.click(backButton);
180+
const backToSignInButton = screen.getByRole("button", { name: "backToSignIn" });
181+
expect(backToSignInButton).toBeInTheDocument();
182+
expect(backToSignInButton).toHaveTextContent("backToSignIn");
183+
184+
// Make sure it's a button so it doesn't submit the form
185+
expect(backToSignInButton).toHaveAttribute("type", "button");
186+
187+
fireEvent.click(backToSignInButton);
229188
expect(onBackToSignInClickMock).toHaveBeenCalled();
230189
});
190+
191+
it('should trigger validation errors when the form is blurred', () => {
192+
const mockUI = createMockUI();
193+
194+
const { container } = render(
195+
<FirebaseUIProvider ui={mockUI}>
196+
<ForgotPasswordAuthForm />
197+
</FirebaseUIProvider>
198+
);
199+
200+
const form = container.querySelector("form.fui-form");
201+
expect(form).toBeInTheDocument();
202+
203+
const input = screen.getByRole("textbox", { name: /email/i });
204+
205+
act(() => {
206+
fireEvent.blur(input);
207+
});
208+
209+
expect(screen.getByText("Please enter a valid email address")).toBeInTheDocument();
210+
});
231211
});

0 commit comments

Comments
 (0)