11import { describe , it , expect , vi , beforeEach } from "vitest" ;
22import { FirebaseError } from "firebase/app" ;
3- import { AuthCredential } from "firebase/auth" ;
3+ import { Auth , AuthCredential , MultiFactorResolver } from "firebase/auth" ;
44import { FirebaseUIError , handleFirebaseError } from "./errors" ;
55import { createMockUI } from "~/tests/utils" ;
66import { ERROR_CODE_MAP } from "@firebase-ui/translations" ;
77
8- // Mock the translations module
98vi . mock ( "./translations" , ( ) => ( {
109 getTranslation : vi . fn ( ) ,
1110} ) ) ;
1211
12+ vi . mock ( "firebase/auth" , ( ) => ( {
13+ getMultiFactorResolver : vi . fn ( ) ,
14+ } ) ) ;
15+
1316import { getTranslation } from "./translations" ;
17+ import { getMultiFactorResolver } from "firebase/auth" ;
1418
1519let mockSessionStorage : { [ key : string ] : string } ;
1620
1721beforeEach ( ( ) => {
1822 vi . clearAllMocks ( ) ;
1923
20- // Mock sessionStorage
2124 mockSessionStorage = { } ;
2225 Object . defineProperty ( window , 'sessionStorage' , {
2326 value : {
@@ -112,7 +115,6 @@ describe("handleFirebaseError", () => {
112115 try {
113116 handleFirebaseError ( mockUI , mockFirebaseError ) ;
114117 } catch ( error ) {
115- // Should be an instance of both FirebaseUIError and FirebaseError
116118 expect ( error ) . toBeInstanceOf ( FirebaseUIError ) ;
117119 expect ( error ) . toBeInstanceOf ( FirebaseError ) ;
118120 expect ( ( error as FirebaseUIError ) . code ) . toBe ( "auth/user-not-found" ) ;
@@ -168,17 +170,75 @@ describe("handleFirebaseError", () => {
168170 vi . mocked ( getTranslation ) . mockReturnValue ( expectedTranslation ) ;
169171
170172 expect ( ( ) => handleFirebaseError ( mockUI , mockFirebaseError ) ) . toThrow ( FirebaseUIError ) ;
171-
172- // Should not try to store credential if it doesn't exist
173173 expect ( window . sessionStorage . setItem ) . not . toHaveBeenCalled ( ) ;
174174 } ) ;
175+
176+ it ( "should call setMultiFactorResolver when auth/multi-factor-auth-required error is thrown" , ( ) => {
177+ const mockUI = createMockUI ( ) ;
178+ const mockResolver = {
179+ auth : { } as Auth ,
180+ session : null ,
181+ hints : [ ] ,
182+ } as unknown as MultiFactorResolver ;
183+
184+ const error = new FirebaseError ( "auth/multi-factor-auth-required" , "Multi-factor authentication required" ) ;
185+ const expectedTranslation = "Multi-factor authentication required (translated)" ;
186+
187+ vi . mocked ( getTranslation ) . mockReturnValue ( expectedTranslation ) ;
188+ vi . mocked ( getMultiFactorResolver ) . mockReturnValue ( mockResolver ) ;
189+
190+ expect ( ( ) => handleFirebaseError ( mockUI , error ) ) . toThrow ( FirebaseUIError ) ;
191+ expect ( getMultiFactorResolver ) . toHaveBeenCalledWith ( mockUI . auth , error ) ;
192+ expect ( mockUI . setMultiFactorResolver ) . toHaveBeenCalledWith ( mockResolver ) ;
193+ } ) ;
194+
195+ it ( "should still throw FirebaseUIError after setting multi-factor resolver" , ( ) => {
196+ const mockUI = createMockUI ( ) ;
197+ const mockResolver = {
198+ auth : { } as Auth ,
199+ session : null ,
200+ hints : [ ] ,
201+ } as unknown as MultiFactorResolver ;
202+
203+ const error = new FirebaseError ( "auth/multi-factor-auth-required" , "Multi-factor authentication required" ) ;
204+ const expectedTranslation = "Multi-factor authentication required (translated)" ;
205+
206+ vi . mocked ( getTranslation ) . mockReturnValue ( expectedTranslation ) ;
207+ vi . mocked ( getMultiFactorResolver ) . mockReturnValue ( mockResolver ) ;
208+
209+ expect ( ( ) => handleFirebaseError ( mockUI , error ) ) . toThrow ( FirebaseUIError ) ;
210+
211+ expect ( getMultiFactorResolver ) . toHaveBeenCalledWith ( mockUI . auth , error ) ;
212+ expect ( mockUI . setMultiFactorResolver ) . toHaveBeenCalledWith ( mockResolver ) ;
213+
214+ try {
215+ handleFirebaseError ( mockUI , error ) ;
216+ } catch ( error ) {
217+ expect ( error ) . toBeInstanceOf ( FirebaseUIError ) ;
218+ expect ( error ) . toBeInstanceOf ( FirebaseError ) ;
219+ expect ( ( error as FirebaseUIError ) . code ) . toBe ( "auth/multi-factor-auth-required" ) ;
220+ expect ( ( error as FirebaseUIError ) . message ) . toBe ( expectedTranslation ) ;
221+ }
222+ } ) ;
223+
224+ it ( "should not call setMultiFactorResolver for other error types" , ( ) => {
225+ const mockUI = createMockUI ( ) ;
226+ const mockFirebaseError = new FirebaseError ( "auth/user-not-found" , "User not found" ) ;
227+ const expectedTranslation = "User not found (translated)" ;
228+
229+ vi . mocked ( getTranslation ) . mockReturnValue ( expectedTranslation ) ;
230+
231+ expect ( ( ) => handleFirebaseError ( mockUI , mockFirebaseError ) ) . toThrow ( FirebaseUIError ) ;
232+
233+ expect ( getMultiFactorResolver ) . not . toHaveBeenCalled ( ) ;
234+ expect ( mockUI . setMultiFactorResolver ) . not . toHaveBeenCalled ( ) ;
235+ } ) ;
175236} ) ;
176237
177238describe ( "isFirebaseError utility" , ( ) => {
178239 it ( "should identify FirebaseError objects" , ( ) => {
179240 const firebaseError = new FirebaseError ( "auth/user-not-found" , "User not found" ) ;
180241
181- // We can't directly test the private function, but we can test it through handleFirebaseError
182242 const mockUI = createMockUI ( ) ;
183243 vi . mocked ( getTranslation ) . mockReturnValue ( "translated message" ) ;
184244
@@ -187,7 +247,7 @@ describe("isFirebaseError utility", () => {
187247
188248 it ( "should reject non-FirebaseError objects" , ( ) => {
189249 const mockUI = createMockUI ( ) ;
190- const nonFirebaseError = { code : "test" , message : "test" } ; // Missing proper structure
250+ const nonFirebaseError = { code : "test" , message : "test" } ;
191251
192252 expect ( ( ) => handleFirebaseError ( mockUI , nonFirebaseError ) ) . toThrow ( ) ;
193253 } ) ;
@@ -218,7 +278,6 @@ describe("errorContainsCredential utility", () => {
218278
219279 expect ( ( ) => handleFirebaseError ( mockUI , firebaseErrorWithCredential ) ) . toThrowError ( FirebaseUIError ) ;
220280
221- // Should have stored the credential
222281 expect ( window . sessionStorage . setItem ) . toHaveBeenCalledWith (
223282 "pendingCred" ,
224283 JSON . stringify ( mockCredential . toJSON ( ) )
@@ -236,8 +295,6 @@ describe("errorContainsCredential utility", () => {
236295
237296 expect ( ( ) => handleFirebaseError ( mockUI , firebaseErrorWithoutCredential ) ) . toThrowError ( FirebaseUIError ) ;
238297
239- // Should not have stored any credential
240298 expect ( window . sessionStorage . setItem ) . not . toHaveBeenCalled ( ) ;
241299 } ) ;
242300} ) ;
243-
0 commit comments