@@ -185,9 +185,10 @@ class ErrorRecoveryDialogLogicTest {
185185 }
186186
187187 @Test
188- fun `getRecoveryActionText returns continue for AuthCancelledException` () {
189- // Arrange
190- val error = AuthException .AuthCancelledException (" Auth cancelled" )
188+ fun `getRecoveryActionText returns continue for AuthCancelledException, ignoring the raw exception message` () {
189+ // Arrange - message mirrors the raw GetCredentialCancellationException message from GH #2422;
190+ // the action label must never surface this raw string to the user
191+ val error = AuthException .AuthCancelledException (" User cancelled the selector" )
191192
192193 // Act
193194 val actionText = getRecoveryActionText(error, mockStringProvider)
@@ -209,15 +210,15 @@ class ErrorRecoveryDialogLogicTest {
209210 }
210211
211212 @Test
212- fun `getRecoveryActionText returns continue for AccountLinkingRequiredException` () {
213- // Arrange
213+ fun `getRecoveryActionText returns sign in for AccountLinkingRequiredException` () {
214+ // Arrange - user needs to sign in with the existing method to link accounts
214215 val error = AuthException .AccountLinkingRequiredException (" Account linking required" )
215216
216217 // Act
217218 val actionText = getRecoveryActionText(error, mockStringProvider)
218219
219220 // Assert
220- Truth .assertThat(actionText).isEqualTo(" Continue " )
221+ Truth .assertThat(actionText).isEqualTo(" Sign in " )
221222 }
222223
223224 @Test
@@ -308,75 +309,4 @@ class ErrorRecoveryDialogLogicTest {
308309 // Act & Assert
309310 Truth .assertThat(isRecoverable(error)).isTrue()
310311 }
311-
312- // Helper functions to test the private functions - we need to make them internal for testing
313- private fun getRecoveryMessage (error : AuthException , stringProvider : AuthUIStringProvider ): String {
314- return when (error) {
315- is AuthException .NetworkException -> stringProvider.networkErrorRecoveryMessage
316- is AuthException .InvalidCredentialsException -> {
317- // Use the actual error message from Firebase if available, otherwise fallback to generic message
318- error.message?.takeIf { it.isNotBlank() && it != " Invalid credentials provided" }
319- ? : stringProvider.invalidCredentialsRecoveryMessage
320- }
321- is AuthException .UserNotFoundException -> stringProvider.userNotFoundRecoveryMessage
322- is AuthException .WeakPasswordException -> {
323- val baseMessage = stringProvider.weakPasswordRecoveryMessage
324- error.reason?.let { reason ->
325- " $baseMessage \n\n Reason: $reason "
326- } ? : baseMessage
327- }
328- is AuthException .EmailAlreadyInUseException -> {
329- val baseMessage = stringProvider.emailAlreadyInUseRecoveryMessage
330- error.email?.let { email ->
331- " $baseMessage ($email )"
332- } ? : baseMessage
333- }
334- is AuthException .TooManyRequestsException -> stringProvider.tooManyRequestsRecoveryMessage
335- is AuthException .MfaRequiredException -> stringProvider.mfaRequiredRecoveryMessage
336- is AuthException .AccountLinkingRequiredException -> stringProvider.accountLinkingRequiredRecoveryMessage
337- is AuthException .DifferentSignInMethodRequiredException ->
338- error.message ? : stringProvider.accountLinkingRequiredRecoveryMessage
339- is AuthException .AuthCancelledException -> stringProvider.authCancelledRecoveryMessage
340- is AuthException .UnknownException -> stringProvider.unknownErrorRecoveryMessage
341- else -> stringProvider.unknownErrorRecoveryMessage
342- }
343- }
344-
345- private fun getRecoveryActionText (error : AuthException , stringProvider : AuthUIStringProvider ): String {
346- return when (error) {
347- is AuthException .AuthCancelledException -> stringProvider.continueText
348- is AuthException .EmailAlreadyInUseException -> stringProvider.signInDefault
349- is AuthException .AccountLinkingRequiredException -> stringProvider.continueText
350- is AuthException .DifferentSignInMethodRequiredException -> when (error.suggestedSignInMethod) {
351- GoogleAuthProvider .PROVIDER_ID -> stringProvider.continueWithGoogle
352- EmailAuthProvider .EMAIL_LINK_SIGN_IN_METHOD -> stringProvider.signInWithEmailLink
353- else -> stringProvider.continueText
354- }
355- is AuthException .MfaRequiredException -> stringProvider.continueText
356- is AuthException .NetworkException ,
357- is AuthException .InvalidCredentialsException ,
358- is AuthException .UserNotFoundException ,
359- is AuthException .WeakPasswordException ,
360- is AuthException .TooManyRequestsException ,
361- is AuthException .UnknownException -> stringProvider.retryAction
362- else -> stringProvider.retryAction
363- }
364- }
365-
366- private fun isRecoverable (error : AuthException ): Boolean {
367- return when (error) {
368- is AuthException .NetworkException -> true
369- is AuthException .InvalidCredentialsException -> true
370- is AuthException .UserNotFoundException -> true
371- is AuthException .WeakPasswordException -> true
372- is AuthException .EmailAlreadyInUseException -> true
373- is AuthException .TooManyRequestsException -> false
374- is AuthException .MfaRequiredException -> true
375- is AuthException .AccountLinkingRequiredException -> true
376- is AuthException .DifferentSignInMethodRequiredException -> true
377- is AuthException .AuthCancelledException -> true
378- is AuthException .UnknownException -> true
379- else -> true
380- }
381- }
382312}
0 commit comments