Skip to content

Commit 3c1b7d0

Browse files
committed
feat(react): Support display name field when behavior is enabled
1 parent b774d89 commit 3c1b7d0

File tree

6 files changed

+261
-20
lines changed

6 files changed

+261
-20
lines changed

packages/react/src/auth/forms/sign-up-auth-form.test.tsx

Lines changed: 234 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
1818
import { render, screen, fireEvent, renderHook, cleanup } from "@testing-library/react";
19-
import { SignUpAuthForm, useSignUpAuthForm, useSignUpAuthFormAction } from "./sign-up-auth-form";
19+
import { SignUpAuthForm, useSignUpAuthForm, useSignUpAuthFormAction, useRequireDisplayName } from "./sign-up-auth-form";
2020
import { act } from "react";
2121
import { createUserWithEmailAndPassword } from "@firebase-ui/core";
2222
import { createFirebaseUIProvider, createMockUI } from "~/tests/utils";
@@ -60,7 +60,7 @@ describe("useSignUpAuthFormAction", () => {
6060
await result.current({ email: "[email protected]", password: "password123" });
6161
});
6262

63-
expect(createUserWithEmailAndPasswordMock).toHaveBeenCalledWith(expect.any(Object), "[email protected]", "password123");
63+
expect(createUserWithEmailAndPasswordMock).toHaveBeenCalledWith(expect.any(Object), "[email protected]", "password123", undefined);
6464
});
6565

6666
it("should return a credential on success", async () => {
@@ -79,7 +79,7 @@ describe("useSignUpAuthFormAction", () => {
7979
expect(credential).toBe(mockCredential);
8080
});
8181

82-
expect(createUserWithEmailAndPasswordMock).toHaveBeenCalledWith(expect.any(Object), "[email protected]", "password123");
82+
expect(createUserWithEmailAndPasswordMock).toHaveBeenCalledWith(expect.any(Object), "[email protected]", "password123", undefined);
8383
});
8484

8585
it("should throw an unknown error when its not a FirebaseUIError", async () => {
@@ -105,7 +105,23 @@ describe("useSignUpAuthFormAction", () => {
105105
});
106106
}).rejects.toThrow("unknownError");
107107

108-
expect(createUserWithEmailAndPasswordMock).toHaveBeenCalledWith(mockUI.get(), "[email protected]", "password123");
108+
expect(createUserWithEmailAndPasswordMock).toHaveBeenCalledWith(mockUI.get(), "[email protected]", "password123", undefined);
109+
});
110+
111+
it("should return a callback which accepts email, password, and displayName", async () => {
112+
const mockCredential = { credential: true } as unknown as UserCredential;
113+
const createUserWithEmailAndPasswordMock = vi.mocked(createUserWithEmailAndPassword).mockResolvedValue(mockCredential);
114+
const mockUI = createMockUI();
115+
116+
const { result } = renderHook(() => useSignUpAuthFormAction(), {
117+
wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }),
118+
});
119+
120+
await act(async () => {
121+
await result.current({ email: "[email protected]", password: "password123", displayName: "John Doe" });
122+
});
123+
124+
expect(createUserWithEmailAndPasswordMock).toHaveBeenCalledWith(expect.any(Object), "[email protected]", "password123", "John Doe");
109125
});
110126
});
111127

@@ -119,8 +135,9 @@ describe("useSignUpAuthForm", () => {
119135
});
120136

121137
it("should allow the form to be submitted", async () => {
138+
const mockCredential = { credential: true } as unknown as UserCredential;
122139
const mockUI = createMockUI();
123-
const createUserWithEmailAndPasswordMock = vi.mocked(createUserWithEmailAndPassword);
140+
const createUserWithEmailAndPasswordMock = vi.mocked(createUserWithEmailAndPassword).mockResolvedValue(mockCredential);
124141

125142
const { result } = renderHook(() => useSignUpAuthForm(), {
126143
wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }),
@@ -129,13 +146,14 @@ describe("useSignUpAuthForm", () => {
129146
act(() => {
130147
result.current.setFieldValue("email", "[email protected]");
131148
result.current.setFieldValue("password", "password123");
149+
// Don't set displayName - let it be undefined (optional)
132150
});
133151

134152
await act(async () => {
135153
await result.current.handleSubmit();
136154
});
137155

138-
expect(createUserWithEmailAndPasswordMock).toHaveBeenCalledWith(mockUI.get(), "[email protected]", "password123");
156+
expect(createUserWithEmailAndPasswordMock).toHaveBeenCalledWith(mockUI.get(), "[email protected]", "password123", undefined);
139157
});
140158

141159
it("should not allow the form to be submitted if the form is invalid", async () => {
@@ -157,18 +175,45 @@ describe("useSignUpAuthForm", () => {
157175
expect(result.current.getFieldMeta("email")!.errors[0].length).toBeGreaterThan(0);
158176
expect(createUserWithEmailAndPasswordMock).not.toHaveBeenCalled();
159177
});
178+
179+
it("should allow the form to be submitted with displayName", async () => {
180+
const mockUI = createMockUI();
181+
const createUserWithEmailAndPasswordMock = vi.mocked(createUserWithEmailAndPassword);
182+
183+
const { result } = renderHook(() => useSignUpAuthForm(), {
184+
wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }),
185+
});
186+
187+
act(() => {
188+
result.current.setFieldValue("email", "[email protected]");
189+
result.current.setFieldValue("password", "password123");
190+
result.current.setFieldValue("displayName", "John Doe");
191+
});
192+
193+
await act(async () => {
194+
await result.current.handleSubmit();
195+
});
196+
197+
expect(createUserWithEmailAndPasswordMock).toHaveBeenCalledWith(mockUI.get(), "[email protected]", "password123", "John Doe");
198+
});
160199
});
161200

162201
describe("<SignUpAuthForm />", () => {
163202
beforeEach(() => {
164203
vi.clearAllMocks();
165204
});
166205

206+
afterEach(() => {
207+
cleanup();
208+
});
209+
167210
it("should render the form correctly", () => {
168211
const mockUI = createMockUI({
169212
locale: registerLocale("test", {
170213
labels: {
171214
createAccount: "createAccount",
215+
emailAddress: "emailAddress",
216+
password: "password",
172217
},
173218
}),
174219
});
@@ -183,9 +228,9 @@ describe("<SignUpAuthForm />", () => {
183228
const form = container.querySelectorAll("form.fui-form");
184229
expect(form.length).toBe(1);
185230

186-
// Make sure we have an email and password input
187-
expect(screen.getByRole("textbox", { name: /email/i })).toBeInTheDocument();
188-
expect(screen.getByRole("textbox", { name: /password/i })).toBeInTheDocument();
231+
// Make sure we have an email and password input with translated labels
232+
expect(screen.getByRole("textbox", { name: /emailAddress/ })).toBeInTheDocument();
233+
expect(screen.getByRole("textbox", { name: /password/ })).toBeInTheDocument();
189234

190235
// Ensure the "Create Account" button is present and is a submit button
191236
const createAccountButton = screen.getByRole("button", { name: "createAccount" });
@@ -246,4 +291,184 @@ describe("<SignUpAuthForm />", () => {
246291

247292
expect(screen.getByText("Please enter a valid email address")).toBeInTheDocument();
248293
});
294+
295+
it("should render displayName field when requireDisplayName behavior is enabled", () => {
296+
const mockUI = createMockUI({
297+
locale: registerLocale("test", {
298+
labels: {
299+
createAccount: "createAccount",
300+
emailAddress: "emailAddress",
301+
password: "password",
302+
displayName: "displayName",
303+
},
304+
}),
305+
behaviors: [
306+
{
307+
requireDisplayName: { type: "callable" as const, handler: vi.fn() },
308+
}
309+
],
310+
});
311+
312+
const { container } = render(
313+
<FirebaseUIProvider ui={mockUI}>
314+
<SignUpAuthForm />
315+
</FirebaseUIProvider>
316+
);
317+
318+
// There should be only one form
319+
const form = container.querySelectorAll("form.fui-form");
320+
expect(form.length).toBe(1);
321+
322+
// Make sure we have all three inputs with translated labels
323+
expect(screen.getByRole("textbox", { name: /emailAddress/ })).toBeInTheDocument();
324+
expect(screen.getByRole("textbox", { name: /password/ })).toBeInTheDocument();
325+
expect(screen.getByRole("textbox", { name: /displayName/ })).toBeInTheDocument();
326+
327+
// Ensure the "Create Account" button is present and is a submit button
328+
const createAccountButton = screen.getByRole("button", { name: "createAccount" });
329+
expect(createAccountButton).toBeInTheDocument();
330+
expect(createAccountButton).toHaveAttribute("type", "submit");
331+
});
332+
333+
it("should not render displayName field when requireDisplayName behavior is not enabled", () => {
334+
const mockUI = createMockUI({
335+
locale: registerLocale("test", {
336+
labels: {
337+
createAccount: "createAccount",
338+
emailAddress: "emailAddress",
339+
password: "password",
340+
displayName: "displayName",
341+
},
342+
}),
343+
behaviors: [], // Explicitly set empty behaviors array
344+
});
345+
346+
const { container } = render(
347+
<FirebaseUIProvider ui={mockUI}>
348+
<SignUpAuthForm />
349+
</FirebaseUIProvider>
350+
);
351+
352+
// There should be only one form
353+
const form = container.querySelectorAll("form.fui-form");
354+
expect(form.length).toBe(1);
355+
356+
// Make sure we have email and password inputs but not displayName
357+
expect(screen.getByRole("textbox", { name: /emailAddress/ })).toBeInTheDocument();
358+
expect(screen.getByRole("textbox", { name: /password/ })).toBeInTheDocument();
359+
expect(screen.queryByRole("textbox", { name: /displayName/ })).not.toBeInTheDocument();
360+
361+
// Ensure the "Create Account" button is present and is a submit button
362+
const createAccountButton = screen.getByRole("button", { name: "createAccount" });
363+
expect(createAccountButton).toBeInTheDocument();
364+
expect(createAccountButton).toHaveAttribute("type", "submit");
365+
});
366+
367+
it('should trigger displayName validation errors when the form is blurred and requireDisplayName is enabled', () => {
368+
const mockUI = createMockUI({
369+
locale: registerLocale("test", {
370+
errors: {
371+
displayNameRequired: "Please provide a display name",
372+
},
373+
labels: {
374+
displayName: "displayName",
375+
},
376+
}),
377+
behaviors: [
378+
{
379+
requireDisplayName: { type: "callable" as const, handler: vi.fn() },
380+
}
381+
],
382+
});
383+
384+
const { container } = render(
385+
<FirebaseUIProvider ui={mockUI}>
386+
<SignUpAuthForm />
387+
</FirebaseUIProvider>
388+
);
389+
390+
const form = container.querySelector("form.fui-form");
391+
expect(form).toBeInTheDocument();
392+
393+
const displayNameInput = screen.getByRole("textbox", { name: /displayName/ });
394+
395+
act(() => {
396+
fireEvent.blur(displayNameInput);
397+
});
398+
399+
expect(screen.getByText("Please provide a display name")).toBeInTheDocument();
400+
});
401+
402+
it('should not trigger displayName validation when requireDisplayName is not enabled', () => {
403+
const mockUI = createMockUI({
404+
locale: registerLocale("test", {
405+
errors: {
406+
displayNameRequired: "Please provide a display name",
407+
},
408+
labels: {
409+
displayName: "displayName",
410+
},
411+
}),
412+
});
413+
414+
const { container } = render(
415+
<FirebaseUIProvider ui={mockUI}>
416+
<SignUpAuthForm />
417+
</FirebaseUIProvider>
418+
);
419+
420+
const form = container.querySelector("form.fui-form");
421+
expect(form).toBeInTheDocument();
422+
423+
// Display name field should not be present
424+
expect(screen.queryByRole("textbox", { name: "displayName" })).not.toBeInTheDocument();
425+
});
426+
});
427+
428+
describe("useRequireDisplayName", () => {
429+
beforeEach(() => {
430+
vi.clearAllMocks();
431+
});
432+
433+
afterEach(() => {
434+
cleanup();
435+
});
436+
437+
it("should return true when requireDisplayName behavior is enabled", () => {
438+
const mockUI = createMockUI({
439+
behaviors: [
440+
{
441+
requireDisplayName: { type: "callable" as const, handler: vi.fn() },
442+
}
443+
],
444+
});
445+
446+
const { result } = renderHook(() => useRequireDisplayName(), {
447+
wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }),
448+
});
449+
450+
expect(result.current).toBe(true);
451+
});
452+
453+
it("should return false when requireDisplayName behavior is not enabled", () => {
454+
const mockUI = createMockUI({
455+
behaviors: [],
456+
});
457+
458+
const { result } = renderHook(() => useRequireDisplayName(), {
459+
wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }),
460+
});
461+
462+
expect(result.current).toBe(false);
463+
});
464+
465+
it("should return false when behaviors array is empty", () => {
466+
const mockUI = createMockUI();
467+
468+
const { result } = renderHook(() => useRequireDisplayName(), {
469+
wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }),
470+
});
471+
472+
expect(result.current).toBe(false);
473+
});
249474
});

0 commit comments

Comments
 (0)