16
16
17
17
import { describe , it , expect , vi , beforeEach , afterEach } from "vitest" ;
18
18
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" ;
20
20
import { act } from "react" ;
21
21
import { createUserWithEmailAndPassword } from "@firebase-ui/core" ;
22
22
import { createFirebaseUIProvider , createMockUI } from "~/tests/utils" ;
@@ -60,7 +60,7 @@ describe("useSignUpAuthFormAction", () => {
60
60
await result . current ( { email :
"[email protected] " , password :
"password123" } ) ;
61
61
} ) ;
62
62
63
- expect ( createUserWithEmailAndPasswordMock ) . toHaveBeenCalledWith ( expect . any ( Object ) , "[email protected] " , "password123" ) ;
63
+ expect ( createUserWithEmailAndPasswordMock ) . toHaveBeenCalledWith ( expect . any ( Object ) , "[email protected] " , "password123" , undefined ) ;
64
64
} ) ;
65
65
66
66
it ( "should return a credential on success" , async ( ) => {
@@ -79,7 +79,7 @@ describe("useSignUpAuthFormAction", () => {
79
79
expect ( credential ) . toBe ( mockCredential ) ;
80
80
} ) ;
81
81
82
- expect ( createUserWithEmailAndPasswordMock ) . toHaveBeenCalledWith ( expect . any ( Object ) , "[email protected] " , "password123" ) ;
82
+ expect ( createUserWithEmailAndPasswordMock ) . toHaveBeenCalledWith ( expect . any ( Object ) , "[email protected] " , "password123" , undefined ) ;
83
83
} ) ;
84
84
85
85
it ( "should throw an unknown error when its not a FirebaseUIError" , async ( ) => {
@@ -105,7 +105,23 @@ describe("useSignUpAuthFormAction", () => {
105
105
} ) ;
106
106
} ) . rejects . toThrow ( "unknownError" ) ;
107
107
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" ) ;
109
125
} ) ;
110
126
} ) ;
111
127
@@ -119,8 +135,9 @@ describe("useSignUpAuthForm", () => {
119
135
} ) ;
120
136
121
137
it ( "should allow the form to be submitted" , async ( ) => {
138
+ const mockCredential = { credential : true } as unknown as UserCredential ;
122
139
const mockUI = createMockUI ( ) ;
123
- const createUserWithEmailAndPasswordMock = vi . mocked ( createUserWithEmailAndPassword ) ;
140
+ const createUserWithEmailAndPasswordMock = vi . mocked ( createUserWithEmailAndPassword ) . mockResolvedValue ( mockCredential ) ;
124
141
125
142
const { result } = renderHook ( ( ) => useSignUpAuthForm ( ) , {
126
143
wrapper : ( { children } ) => createFirebaseUIProvider ( { children, ui : mockUI } ) ,
@@ -129,13 +146,14 @@ describe("useSignUpAuthForm", () => {
129
146
act ( ( ) => {
130
147
result . current . setFieldValue ( "email" , "[email protected] " ) ;
131
148
result . current . setFieldValue ( "password" , "password123" ) ;
149
+ // Don't set displayName - let it be undefined (optional)
132
150
} ) ;
133
151
134
152
await act ( async ( ) => {
135
153
await result . current . handleSubmit ( ) ;
136
154
} ) ;
137
155
138
- expect ( createUserWithEmailAndPasswordMock ) . toHaveBeenCalledWith ( mockUI . get ( ) , "[email protected] " , "password123" ) ;
156
+ expect ( createUserWithEmailAndPasswordMock ) . toHaveBeenCalledWith ( mockUI . get ( ) , "[email protected] " , "password123" , undefined ) ;
139
157
} ) ;
140
158
141
159
it ( "should not allow the form to be submitted if the form is invalid" , async ( ) => {
@@ -157,18 +175,45 @@ describe("useSignUpAuthForm", () => {
157
175
expect ( result . current . getFieldMeta ( "email" ) ! . errors [ 0 ] . length ) . toBeGreaterThan ( 0 ) ;
158
176
expect ( createUserWithEmailAndPasswordMock ) . not . toHaveBeenCalled ( ) ;
159
177
} ) ;
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
+ } ) ;
160
199
} ) ;
161
200
162
201
describe ( "<SignUpAuthForm />" , ( ) => {
163
202
beforeEach ( ( ) => {
164
203
vi . clearAllMocks ( ) ;
165
204
} ) ;
166
205
206
+ afterEach ( ( ) => {
207
+ cleanup ( ) ;
208
+ } ) ;
209
+
167
210
it ( "should render the form correctly" , ( ) => {
168
211
const mockUI = createMockUI ( {
169
212
locale : registerLocale ( "test" , {
170
213
labels : {
171
214
createAccount : "createAccount" ,
215
+ emailAddress : "emailAddress" ,
216
+ password : "password" ,
172
217
} ,
173
218
} ) ,
174
219
} ) ;
@@ -183,9 +228,9 @@ describe("<SignUpAuthForm />", () => {
183
228
const form = container . querySelectorAll ( "form.fui-form" ) ;
184
229
expect ( form . length ) . toBe ( 1 ) ;
185
230
186
- // Make sure we have an email and password input
187
- expect ( screen . getByRole ( "textbox" , { name : / e m a i l / i } ) ) . toBeInTheDocument ( ) ;
188
- expect ( screen . getByRole ( "textbox" , { name : / p a s s w o r d / i } ) ) . toBeInTheDocument ( ) ;
231
+ // Make sure we have an email and password input with translated labels
232
+ expect ( screen . getByRole ( "textbox" , { name : / e m a i l A d d r e s s / } ) ) . toBeInTheDocument ( ) ;
233
+ expect ( screen . getByRole ( "textbox" , { name : / p a s s w o r d / } ) ) . toBeInTheDocument ( ) ;
189
234
190
235
// Ensure the "Create Account" button is present and is a submit button
191
236
const createAccountButton = screen . getByRole ( "button" , { name : "createAccount" } ) ;
@@ -246,4 +291,184 @@ describe("<SignUpAuthForm />", () => {
246
291
247
292
expect ( screen . getByText ( "Please enter a valid email address" ) ) . toBeInTheDocument ( ) ;
248
293
} ) ;
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 : / e m a i l A d d r e s s / } ) ) . toBeInTheDocument ( ) ;
324
+ expect ( screen . getByRole ( "textbox" , { name : / p a s s w o r d / } ) ) . toBeInTheDocument ( ) ;
325
+ expect ( screen . getByRole ( "textbox" , { name : / d i s p l a y N a m e / } ) ) . 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 : / e m a i l A d d r e s s / } ) ) . toBeInTheDocument ( ) ;
358
+ expect ( screen . getByRole ( "textbox" , { name : / p a s s w o r d / } ) ) . toBeInTheDocument ( ) ;
359
+ expect ( screen . queryByRole ( "textbox" , { name : / d i s p l a y N a m e / } ) ) . 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 : / d i s p l a y N a m e / } ) ;
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
+ } ) ;
249
474
} ) ;
0 commit comments