1515 */
1616
1717import { describe , it , expect , vi , beforeEach } from "vitest" ;
18- import { renderHook , act , cleanup } from "@testing-library/react" ;
18+ import { renderHook , act , cleanup , waitFor } from "@testing-library/react" ;
19+ import React from "react" ;
1920import {
2021 useUI ,
2122 useRedirectError ,
@@ -25,12 +26,29 @@ import {
2526 useEmailLinkAuthFormSchema ,
2627 usePhoneAuthNumberFormSchema ,
2728 usePhoneAuthVerifyFormSchema ,
29+ useRecaptchaVerifier ,
2830} from "./hooks" ;
2931import { createFirebaseUIProvider , createMockUI } from "~/tests/utils" ;
3032import { registerLocale , enUs } from "@firebase-ui/translations" ;
33+ import type { RecaptchaVerifier } from "firebase/auth" ;
34+
35+ // Mock RecaptchaVerifier from firebase/auth
36+ const mockRender = vi . fn ( ) ;
37+ const mockVerifier = {
38+ render : mockRender ,
39+ } as unknown as RecaptchaVerifier ;
40+
41+ vi . mock ( "firebase/auth" , async ( ) => {
42+ const actual = await vi . importActual < typeof import ( "firebase/auth" ) > ( "firebase/auth" ) ;
43+ return {
44+ ...actual ,
45+ RecaptchaVerifier : vi . fn ( ) . mockImplementation ( ( ) => mockVerifier ) ,
46+ } ;
47+ } ) ;
3148
3249beforeEach ( ( ) => {
3350 vi . clearAllMocks ( ) ;
51+ mockRender . mockClear ( ) ;
3452} ) ;
3553
3654describe ( "useUI" , ( ) => {
@@ -825,3 +843,91 @@ describe("useRedirectError", () => {
825843 expect ( result . current ) . toBeUndefined ( ) ;
826844 } ) ;
827845} ) ;
846+
847+ describe ( "useRecaptchaVerifier" , ( ) => {
848+ beforeEach ( ( ) => {
849+ cleanup ( ) ;
850+ } ) ;
851+
852+ it ( "creates verifier when element is available" , async ( ) => {
853+ const mockUI = createMockUI ( ) ;
854+ const element = document . createElement ( "div" ) ;
855+ const ref = { current : element } ;
856+
857+ const { result } = renderHook ( ( ) => useRecaptchaVerifier ( ref ) , {
858+ wrapper : ( { children } ) => createFirebaseUIProvider ( { children, ui : mockUI } ) ,
859+ } ) ;
860+
861+ await waitFor ( ( ) => {
862+ expect ( result . current ) . toBe ( mockVerifier ) ;
863+ } ) ;
864+
865+ expect ( mockRender ) . toHaveBeenCalledTimes ( 1 ) ;
866+ } ) ;
867+
868+ it ( "returns null when element is not available" , ( ) => {
869+ const mockUI = createMockUI ( ) ;
870+ const ref = { current : null } ;
871+
872+ const { result } = renderHook ( ( ) => useRecaptchaVerifier ( ref ) , {
873+ wrapper : ( { children } ) => createFirebaseUIProvider ( { children, ui : mockUI } ) ,
874+ } ) ;
875+
876+ expect ( result . current ) . toBeNull ( ) ;
877+ expect ( mockRender ) . not . toHaveBeenCalled ( ) ;
878+ } ) ;
879+
880+ it ( "does not recreate verifier when ui changes" , async ( ) => {
881+ const mockUI = createMockUI ( ) ;
882+ const element = document . createElement ( "div" ) ;
883+ const ref = { current : element } ;
884+
885+ const { result, rerender } = renderHook ( ( ) => useRecaptchaVerifier ( ref ) , {
886+ wrapper : ( { children } ) => createFirebaseUIProvider ( { children, ui : mockUI } ) ,
887+ } ) ;
888+
889+ await waitFor ( ( ) => {
890+ expect ( result . current ) . toBe ( mockVerifier ) ;
891+ } ) ;
892+
893+ const firstVerifier = result . current ;
894+ expect ( mockRender ) . toHaveBeenCalledTimes ( 1 ) ;
895+
896+ act ( ( ) => {
897+ mockUI . get ( ) . setState ( "pending" ) ;
898+ } ) ;
899+
900+ rerender ( ) ;
901+
902+ expect ( result . current ) . toBe ( firstVerifier ) ;
903+ expect ( mockRender ) . toHaveBeenCalledTimes ( 1 ) ;
904+ } ) ;
905+
906+ it ( "recreates verifier when element changes" , async ( ) => {
907+ const mockUI = createMockUI ( ) ;
908+ const element1 = document . createElement ( "div" ) ;
909+ const element2 = document . createElement ( "div" ) ;
910+
911+ const { result, rerender } = renderHook ( ( props ) => useRecaptchaVerifier ( props . ref ) , {
912+ initialProps : { ref : { current : element1 } } ,
913+ wrapper : ( { children } ) => createFirebaseUIProvider ( { children, ui : mockUI } ) ,
914+ } ) ;
915+
916+ await waitFor ( ( ) => {
917+ expect ( result . current ) . toBe ( mockVerifier ) ;
918+ } ) ;
919+
920+ expect ( mockRender ) . toHaveBeenCalledTimes ( 1 ) ;
921+
922+ act ( ( ) => {
923+ rerender ( { ref : { current : element2 } } ) ;
924+ } ) ;
925+
926+ // Verifier should be recreated - wait for effect to run
927+ await waitFor ( ( ) => {
928+ expect ( mockRender ) . toHaveBeenCalledTimes ( 2 ) ;
929+ } ) ;
930+
931+ expect ( result . current ) . toBe ( mockVerifier ) ;
932+ } ) ;
933+ } ) ;
0 commit comments