Skip to content

Commit 01052cd

Browse files
authored
EmailAuthScreen Tests (#2239)
* feat: EmailAuthScreen integration tests - localize strings and default icon based on validator - setup integration tests to run in CI - add execute permission to start-firebase-emulator.sh script - CI use fake project id for auth emulator * test: email link sign in dialog * refactor: move e2e tests to separate module * fix CI test errors * fix CI test errors * chore: improve test utils * chore: add missing string translations
1 parent 9387fd5 commit 01052cd

File tree

85 files changed

+1653
-142
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+1653
-142
lines changed

.github/workflows/android.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
jobs:
88
build:
99
runs-on: ubuntu-latest
10+
timeout-minutes: 45
1011
steps:
1112
- uses: actions/checkout@v4
1213

@@ -18,13 +19,13 @@ jobs:
1819
~/.gradle/wrapper
1920
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
2021

21-
- name: Set up JDK 17
22+
- name: Set up JDK 21
2223
uses: actions/setup-java@v4
2324
with:
24-
java-version: '17'
25+
java-version: '21'
2526
distribution: 'temurin'
2627

27-
- name: Build with Gradle
28+
- name: Build and Test
2829
run: ./scripts/build.sh
2930

3031
- name: Print Logs

.github/workflows/e2e_test.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: E2E Tests (Firebase Emulator)
2+
3+
on:
4+
- pull_request
5+
- push
6+
7+
jobs:
8+
e2e-tests:
9+
runs-on: ubuntu-latest
10+
timeout-minutes: 45
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Cache Gradle packages
15+
uses: actions/cache@v3
16+
with:
17+
path: |
18+
~/.gradle/caches
19+
~/.gradle/wrapper
20+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
21+
22+
- name: Firebase Emulator Cache
23+
uses: actions/cache@v4
24+
with:
25+
path: ~/.cache/firebase/emulators
26+
key: firebase-emulators-v3-${{ runner.os }}
27+
28+
- name: Install Node.js 20
29+
uses: actions/setup-node@v4
30+
with:
31+
node-version: '20'
32+
33+
- name: Set up JDK 21
34+
uses: actions/setup-java@v4
35+
with:
36+
java-version: '21'
37+
distribution: 'temurin'
38+
39+
- name: Install Firebase Tools
40+
run: |
41+
npm i -g firebase-tools
42+
43+
- name: Start Firebase Auth Emulator
44+
run: ./scripts/start-firebase-emulator.sh
45+
46+
- name: Run E2E Tests
47+
run: |
48+
./gradlew e2eTest
49+
50+
- name: Print Logs
51+
if: failure()
52+
run: ./scripts/print_build_logs.sh

auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthUI.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ class FirebaseAuthUI private constructor(
257257
* @throws AuthException.UnknownException for other errors
258258
* @since 10.0.0
259259
*/
260-
suspend fun signOut(context: Context) {
260+
fun signOut(context: Context) {
261261
try {
262262
// Update state to loading
263263
updateAuthState(AuthState.Loading("Signing out..."))
@@ -452,7 +452,7 @@ class FirebaseAuthUI private constructor(
452452
*/
453453
@JvmStatic
454454
@RestrictTo(RestrictTo.Scope.TESTS)
455-
internal fun clearInstanceCache() {
455+
fun clearInstanceCache() {
456456
instanceCache.clear()
457457
}
458458

auth/src/main/java/com/firebase/ui/auth/compose/configuration/PasswordRule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ abstract class PasswordRule {
3030
}
3131

3232
override fun getErrorMessage(stringProvider: AuthUIStringProvider): String {
33-
return stringProvider.passwordTooShort.format(value)
33+
return stringProvider.passwordTooShort(value)
3434
}
3535
}
3636

auth/src/main/java/com/firebase/ui/auth/compose/configuration/string_provider/AuthUIStringProvider.kt

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ interface AuthUIStringProvider {
8686
val passwordsDoNotMatch: String
8787

8888
/** Error message when password doesn't meet minimum length requirement. Should support string formatting with minimum length parameter. */
89-
val passwordTooShort: String
89+
fun passwordTooShort(minimumLength: Int): String
9090

9191
/** Error message when password is missing at least one uppercase letter (A-Z) */
9292
val passwordMissingUppercase: String
@@ -102,14 +102,17 @@ interface AuthUIStringProvider {
102102

103103
// Email Authentication Strings
104104
/** Title for email signup form */
105-
val titleRegisterEmail: String
105+
val signupPageTitle: String
106106

107107
/** Hint for email input field */
108108
val emailHint: String
109109

110110
/** Hint for password input field */
111111
val passwordHint: String
112112

113+
/** Hint for confirm password input field */
114+
val confirmPasswordHint: String
115+
113116
/** Hint for new password input field */
114117
val newPasswordHint: String
115118

@@ -125,6 +128,24 @@ interface AuthUIStringProvider {
125128
/** Trouble signing in link text */
126129
val troubleSigningIn: String
127130

131+
/** Title for recover password page */
132+
val recoverPasswordPageTitle: String
133+
134+
/** Button text for reset password */
135+
val sendButtonText: String
136+
137+
/** Title for recover password link sent dialog */
138+
val recoverPasswordLinkSentDialogTitle: String
139+
140+
/** Body for recover password link sent dialog */
141+
fun recoverPasswordLinkSentDialogBody(email: String): String
142+
143+
/** Title for email sign in link sent dialog */
144+
val emailSignInLinkSentDialogTitle: String
145+
146+
/** Body for email sign in link sent dialog */
147+
fun emailSignInLinkSentDialogBody(email: String): String
148+
128149
// Phone Authentication Strings
129150
/** Phone number entry form title */
130151
val verifyPhoneNumberTitle: String

auth/src/main/java/com/firebase/ui/auth/compose/configuration/string_provider/DefaultAuthUIStringProvider.kt

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import com.firebase.ui.auth.R
2020
import java.util.Locale
2121

2222
class DefaultAuthUIStringProvider(
23-
private val context: Context,
24-
private val locale: Locale? = null,
23+
context: Context,
24+
locale: Locale? = null,
2525
) : AuthUIStringProvider {
2626
/**
2727
* Allows overriding locale.
@@ -95,8 +95,10 @@ class DefaultAuthUIStringProvider(
9595
get() = localizedContext.getString(R.string.fui_error_invalid_password)
9696
override val passwordsDoNotMatch: String
9797
get() = localizedContext.getString(R.string.fui_passwords_do_not_match)
98-
override val passwordTooShort: String
99-
get() = localizedContext.getString(R.string.fui_error_password_too_short)
98+
99+
override fun passwordTooShort(minimumLength: Int): String =
100+
localizedContext.getString(R.string.fui_error_password_too_short, minimumLength)
101+
100102
override val passwordMissingUppercase: String
101103
get() = localizedContext.getString(R.string.fui_error_password_missing_uppercase)
102104
override val passwordMissingLowercase: String
@@ -109,12 +111,14 @@ class DefaultAuthUIStringProvider(
109111
/**
110112
* Email Authentication Strings
111113
*/
112-
override val titleRegisterEmail: String
114+
override val signupPageTitle: String
113115
get() = localizedContext.getString(R.string.fui_title_register_email)
114116
override val emailHint: String
115117
get() = localizedContext.getString(R.string.fui_email_hint)
116118
override val passwordHint: String
117119
get() = localizedContext.getString(R.string.fui_password_hint)
120+
override val confirmPasswordHint: String
121+
get() = localizedContext.getString(R.string.fui_confirm_password_hint)
118122
override val newPasswordHint: String
119123
get() = localizedContext.getString(R.string.fui_new_password_hint)
120124
override val nameHint: String
@@ -126,6 +130,24 @@ class DefaultAuthUIStringProvider(
126130
override val troubleSigningIn: String
127131
get() = localizedContext.getString(R.string.fui_trouble_signing_in)
128132

133+
override val recoverPasswordPageTitle: String
134+
get() = localizedContext.getString(R.string.fui_title_recover_password_activity)
135+
136+
override val sendButtonText: String
137+
get() = localizedContext.getString(R.string.fui_button_text_send)
138+
139+
override val recoverPasswordLinkSentDialogTitle: String
140+
get() = localizedContext.getString(R.string.fui_title_confirm_recover_password)
141+
142+
override fun recoverPasswordLinkSentDialogBody(email: String): String =
143+
localizedContext.getString(R.string.fui_confirm_recovery_body, email)
144+
145+
override val emailSignInLinkSentDialogTitle: String
146+
get() = localizedContext.getString(R.string.fui_email_link_header)
147+
148+
override fun emailSignInLinkSentDialogBody(email: String): String =
149+
localizedContext.getString(R.string.fui_email_link_email_sent, email)
150+
129151
/**
130152
* Phone Authentication Strings
131153
*/

auth/src/main/java/com/firebase/ui/auth/compose/ui/components/AuthTextField.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,27 @@ fun AuthTextField(
125125
keyboardActions = keyboardActions,
126126
visualTransformation = if (isSecureTextField && !passwordVisible)
127127
PasswordVisualTransformation() else visualTransformation,
128-
leadingIcon = leadingIcon,
128+
leadingIcon = leadingIcon ?: when {
129+
validator is EmailValidator -> {
130+
{
131+
Icon(
132+
imageVector = Icons.Default.Email,
133+
contentDescription = ""
134+
)
135+
}
136+
}
137+
138+
isSecureTextField -> {
139+
{
140+
Icon(
141+
imageVector = Icons.Default.Lock,
142+
contentDescription = ""
143+
)
144+
}
145+
}
146+
147+
else -> null
148+
},
129149
trailingIcon = trailingIcon ?: {
130150
if (isSecureTextField) {
131151
IconButton(

auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/ResetPasswordUI.kt

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,22 @@ package com.firebase.ui.auth.compose.ui.screens
1717
import androidx.compose.foundation.layout.Column
1818
import androidx.compose.foundation.layout.Row
1919
import androidx.compose.foundation.layout.Spacer
20+
import androidx.compose.foundation.rememberScrollState
21+
import androidx.compose.foundation.verticalScroll
2022
import androidx.compose.foundation.layout.height
2123
import androidx.compose.foundation.layout.padding
2224
import androidx.compose.foundation.layout.safeDrawingPadding
2325
import androidx.compose.foundation.layout.size
2426
import androidx.compose.foundation.layout.width
25-
import androidx.compose.material.icons.Icons
26-
import androidx.compose.material.icons.filled.Email
2727
import androidx.compose.material3.AlertDialog
2828
import androidx.compose.material3.Button
2929
import androidx.compose.material3.CircularProgressIndicator
3030
import androidx.compose.material3.ExperimentalMaterial3Api
31-
import androidx.compose.material3.Icon
3231
import androidx.compose.material3.MaterialTheme
3332
import androidx.compose.material3.Scaffold
3433
import androidx.compose.material3.Text
3534
import androidx.compose.material3.TextButton
3635
import androidx.compose.material3.TopAppBar
37-
import androidx.compose.material3.TopAppBarDefaults
3836
import androidx.compose.runtime.Composable
3937
import androidx.compose.runtime.derivedStateOf
4038
import androidx.compose.runtime.mutableStateOf
@@ -83,24 +81,25 @@ fun ResetPasswordUI(
8381
AlertDialog(
8482
title = {
8583
Text(
86-
text = "Reset Link Sent",
84+
text = stringProvider.recoverPasswordLinkSentDialogTitle,
8785
style = MaterialTheme.typography.headlineSmall
8886
)
8987
},
9088
text = {
9189
Text(
92-
text = "Check your email $email",
90+
text = stringProvider.recoverPasswordLinkSentDialogBody(email),
9391
style = MaterialTheme.typography.bodyMedium,
9492
textAlign = TextAlign.Start
9593
)
9694
},
9795
confirmButton = {
9896
TextButton(
9997
onClick = {
98+
onGoToSignIn()
10099
isDialogVisible.value = false
101100
}
102101
) {
103-
Text("Dismiss")
102+
Text(stringProvider.dismissAction)
104103
}
105104
},
106105
onDismissRequest = {
@@ -114,7 +113,7 @@ fun ResetPasswordUI(
114113
topBar = {
115114
TopAppBar(
116115
title = {
117-
Text("Recover Password")
116+
Text(stringProvider.recoverPasswordPageTitle)
118117
},
119118
colors = AuthUITheme.topAppBarColors
120119
)
@@ -124,7 +123,8 @@ fun ResetPasswordUI(
124123
modifier = Modifier
125124
.padding(innerPadding)
126125
.safeDrawingPadding()
127-
.padding(horizontal = 16.dp),
126+
.padding(horizontal = 16.dp)
127+
.verticalScroll(rememberScrollState()),
128128
) {
129129
AuthTextField(
130130
value = email,
@@ -135,12 +135,6 @@ fun ResetPasswordUI(
135135
},
136136
onValueChange = { text ->
137137
onEmailChange(text)
138-
},
139-
leadingIcon = {
140-
Icon(
141-
imageVector = Icons.Default.Email,
142-
contentDescription = ""
143-
)
144138
}
145139
)
146140
Spacer(modifier = Modifier.height(8.dp))
@@ -154,7 +148,7 @@ fun ResetPasswordUI(
154148
},
155149
enabled = !isLoading,
156150
) {
157-
Text("Sign In")
151+
Text(stringProvider.signInDefault.uppercase())
158152
}
159153
Spacer(modifier = Modifier.width(16.dp))
160154
Button(
@@ -169,7 +163,7 @@ fun ResetPasswordUI(
169163
.size(16.dp)
170164
)
171165
} else {
172-
Text("Send")
166+
Text(stringProvider.sendButtonText.uppercase())
173167
}
174168
}
175169
}

0 commit comments

Comments
 (0)