From 9bf2be68ec996758677510a80a45e54ca671adfc Mon Sep 17 00:00:00 2001 From: Isabel Martin Date: Thu, 4 Apr 2024 07:41:24 -0700 Subject: [PATCH] MBL-1336: Remove OAuth feature flag (#2007) --- app/src/main/AndroidManifest.xml | 17 - .../libs/utils/extensions/IntentExt.kt | 4 +- .../ui/activities/ChangePasswordActivity.kt | 10 +- .../ui/activities/CreatePasswordActivity.kt | 6 +- .../FacebookConfirmationActivity.kt | 2 +- .../ui/activities/LoginActivity.kt | 282 ------------ .../ui/activities/LoginToutActivity.kt | 54 +-- .../ui/activities/SignupActivity.kt | 112 ----- .../ui/activities/TwoFactorActivity.kt | 150 ------- .../activities/compose/login/LoginScreen.kt | 228 ---------- .../compose/login/LoginToutScreen.kt | 38 +- .../activities/compose/login/SignupScreen.kt | 291 ------------- .../compose/login/TwoFactorScreen.kt | 214 --------- .../kickstarter/ui/extensions/ActivityExt.kt | 15 - .../viewmodels/LoginToutViewModel.kt | 40 -- .../kickstarter/viewmodels/LoginViewModel.kt | 305 ------------- .../kickstarter/viewmodels/SignupViewModel.kt | 189 -------- .../viewmodels/TwoFactorViewModel.kt | 269 ------------ .../libs/utils/extensions/IntentExtTest.kt | 6 +- .../ui/activities/compose/LoginScreenTest.kt | 222 ---------- .../activities/compose/LoginToutScreenTest.kt | 93 +--- .../activities/compose/TwoFactorScreenTest.kt | 206 --------- .../compose/login/SignupScreenKtTest.kt | 303 ------------- .../viewmodels/LoginToutViewModelTest.kt | 30 -- .../viewmodels/LoginViewModelTest.kt | 405 ------------------ .../viewmodels/SignupViewModelTest.kt | 182 -------- .../viewmodels/TwoFactorViewModelTest.kt | 266 ------------ 27 files changed, 20 insertions(+), 3919 deletions(-) delete mode 100644 app/src/main/java/com/kickstarter/ui/activities/LoginActivity.kt delete mode 100644 app/src/main/java/com/kickstarter/ui/activities/SignupActivity.kt delete mode 100644 app/src/main/java/com/kickstarter/ui/activities/TwoFactorActivity.kt delete mode 100644 app/src/main/java/com/kickstarter/ui/activities/compose/login/LoginScreen.kt delete mode 100644 app/src/main/java/com/kickstarter/ui/activities/compose/login/SignupScreen.kt delete mode 100644 app/src/main/java/com/kickstarter/ui/activities/compose/login/TwoFactorScreen.kt delete mode 100644 app/src/main/java/com/kickstarter/viewmodels/LoginViewModel.kt delete mode 100644 app/src/main/java/com/kickstarter/viewmodels/SignupViewModel.kt delete mode 100644 app/src/main/java/com/kickstarter/viewmodels/TwoFactorViewModel.kt delete mode 100644 app/src/test/java/com/kickstarter/ui/activities/compose/LoginScreenTest.kt delete mode 100644 app/src/test/java/com/kickstarter/ui/activities/compose/TwoFactorScreenTest.kt delete mode 100644 app/src/test/java/com/kickstarter/ui/activities/compose/login/SignupScreenKtTest.kt delete mode 100644 app/src/test/java/com/kickstarter/viewmodels/LoginViewModelTest.kt delete mode 100644 app/src/test/java/com/kickstarter/viewmodels/SignupViewModelTest.kt delete mode 100644 app/src/test/java/com/kickstarter/viewmodels/TwoFactorViewModelTest.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 97c56470e1..63458be4f4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -108,12 +108,10 @@ android:theme="@style/WebViewActivity" /> - - @@ -221,11 +209,6 @@ android:name=".ui.activities.ThanksActivity" android:parentActivityName=".ui.activities.DiscoveryActivity" android:theme="@style/ThanksActivity" /> - diff --git a/app/src/main/java/com/kickstarter/libs/utils/extensions/IntentExt.kt b/app/src/main/java/com/kickstarter/libs/utils/extensions/IntentExt.kt index 0a12aa9421..6bcac25763 100644 --- a/app/src/main/java/com/kickstarter/libs/utils/extensions/IntentExt.kt +++ b/app/src/main/java/com/kickstarter/libs/utils/extensions/IntentExt.kt @@ -6,7 +6,7 @@ import com.kickstarter.models.Project import com.kickstarter.ui.IntentKey import com.kickstarter.ui.activities.CommentsActivity import com.kickstarter.ui.activities.CreatorBioActivity -import com.kickstarter.ui.activities.LoginActivity +import com.kickstarter.ui.activities.LoginToutActivity import com.kickstarter.ui.activities.PaymentMethodsSettingsActivity import com.kickstarter.ui.activities.PreLaunchProjectPageActivity import com.kickstarter.ui.activities.ProjectPageActivity @@ -162,7 +162,7 @@ fun Intent.getLoginActivityIntent( email: String? = null, loginReason: LoginReason? = null ): Intent { - return this.setClass(context, LoginActivity::class.java).apply { + return this.setClass(context, LoginToutActivity::class.java).apply { loginReason?.let { this.putExtra(IntentKey.LOGIN_REASON, it) } diff --git a/app/src/main/java/com/kickstarter/ui/activities/ChangePasswordActivity.kt b/app/src/main/java/com/kickstarter/ui/activities/ChangePasswordActivity.kt index b1adc40768..c6fe6cdfd4 100644 --- a/app/src/main/java/com/kickstarter/ui/activities/ChangePasswordActivity.kt +++ b/app/src/main/java/com/kickstarter/ui/activities/ChangePasswordActivity.kt @@ -15,11 +15,9 @@ import com.kickstarter.libs.Logout import com.kickstarter.libs.featureflag.FlagKey import com.kickstarter.libs.utils.ApplicationUtils import com.kickstarter.libs.utils.extensions.getEnvironment -import com.kickstarter.ui.IntentKey import com.kickstarter.ui.SharedPreferenceKey import com.kickstarter.ui.activities.compose.ChangePasswordScreen import com.kickstarter.ui.compose.designsystem.KickstarterApp -import com.kickstarter.ui.data.LoginReason import com.kickstarter.viewmodels.ChangePasswordViewModel import com.kickstarter.viewmodels.ChangePasswordViewModelFactory import io.reactivex.disposables.CompositeDisposable @@ -101,13 +99,7 @@ class ChangePasswordActivity : ComponentActivity() { private fun logout(email: String) { this.logout?.execute() ApplicationUtils.startNewDiscoveryActivity(this) - val intent = if (oAuthIsEnabled) { - Intent(this, LoginToutActivity::class.java) - } else { - Intent(this, LoginActivity::class.java) - .putExtra(IntentKey.LOGIN_REASON, LoginReason.CHANGE_PASSWORD) - .putExtra(IntentKey.EMAIL, email) - } + val intent = Intent(this, LoginToutActivity::class.java) startActivity( intent ) diff --git a/app/src/main/java/com/kickstarter/ui/activities/CreatePasswordActivity.kt b/app/src/main/java/com/kickstarter/ui/activities/CreatePasswordActivity.kt index 424b12169c..74ba069b56 100644 --- a/app/src/main/java/com/kickstarter/ui/activities/CreatePasswordActivity.kt +++ b/app/src/main/java/com/kickstarter/ui/activities/CreatePasswordActivity.kt @@ -15,11 +15,9 @@ import com.kickstarter.libs.featureflag.FlagKey import com.kickstarter.libs.utils.ApplicationUtils import com.kickstarter.libs.utils.extensions.addToDisposable import com.kickstarter.libs.utils.extensions.getEnvironment -import com.kickstarter.ui.IntentKey import com.kickstarter.ui.SharedPreferenceKey import com.kickstarter.ui.activities.compose.login.CreatePasswordScreen import com.kickstarter.ui.compose.designsystem.KickstarterApp -import com.kickstarter.ui.data.LoginReason import com.kickstarter.viewmodels.CreatePasswordViewModel import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable @@ -105,9 +103,7 @@ class CreatePasswordActivity : AppCompatActivity() { this.logout?.execute() ApplicationUtils.startNewDiscoveryActivity(this) startActivity( - Intent(this, LoginActivity::class.java) - .putExtra(IntentKey.LOGIN_REASON, LoginReason.CREATE_PASSWORD) - .putExtra(IntentKey.EMAIL, email) + Intent(this, LoginToutActivity::class.java) ) } } diff --git a/app/src/main/java/com/kickstarter/ui/activities/FacebookConfirmationActivity.kt b/app/src/main/java/com/kickstarter/ui/activities/FacebookConfirmationActivity.kt index 50a2132fba..961bbdc046 100644 --- a/app/src/main/java/com/kickstarter/ui/activities/FacebookConfirmationActivity.kt +++ b/app/src/main/java/com/kickstarter/ui/activities/FacebookConfirmationActivity.kt @@ -64,7 +64,7 @@ class FacebookConfirmationActivity : BaseActivity - environment = env - viewModelFactory = LoginViewModel.Factory(env, intent = intent) - darkModeEnabled = - env.featureFlagClient()?.getBoolean(FlagKey.ANDROID_DARK_MODE_ENABLED) ?: false - theme = env.sharedPreferences() - ?.getInt(SharedPreferenceKey.APP_THEME, AppThemes.MATCH_SYSTEM.ordinal) - ?: AppThemes.MATCH_SYSTEM.ordinal - env - } - - setContent { - var prefillEmail = viewModel.outputs.prefillEmail().subscribeAsState(initial = "").value - - var isLoading = viewModel.isLoading().subscribeAsState(initial = false).value - - var error by remember { mutableStateOf("") } - - errorMessages().subscribe { - error = it - }.addToDisposable(disposables) - - var scaffoldState = rememberScaffoldState() - - var resetPasswordDialogMessage by rememberSaveable { mutableStateOf("") } - - var showDialog by rememberSaveable { mutableStateOf(false) } - - var showResetPasswordDialog = viewModel.outputs.showResetPasswordSuccessDialog() - .subscribeAsState(initial = Pair(false, Pair("", LoginReason.DEFAULT))).value - when { - showResetPasswordDialog.first -> { - val emailAndReason = showResetPasswordDialog.second - val message = - if (emailAndReason.second == LoginReason.RESET_FACEBOOK_PASSWORD) { - this.ksString.format( - getString(this.resetPasswordSentEmailString), - "email", - emailAndReason.first - ) - } else { - this.ksString.format( - getString(this.resetPasswordSentEmailString), - "email", - emailAndReason.first - ) - } - resetPasswordDialogMessage = message - showDialog = true - } - - else -> { - resetPasswordDialogMessage = "" - showDialog = false - } - } - - var changedPasswordMessage by remember { mutableStateOf("") } - - this.viewModel.outputs.showChangedPasswordSnackbar() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - changedPasswordMessage = getString(R.string.Got_it_your_changes_have_been_saved) - } - .addToDisposable(disposables) - - var createPasswordMessage by remember { mutableStateOf("") } - - this.viewModel.outputs.showCreatedPasswordSnackbar() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - createPasswordMessage = getString(R.string.Got_it_your_changes_have_been_saved) - } - .addToDisposable(disposables) - - KickstarterApp( - useDarkTheme = - if (darkModeEnabled) { - when (theme) { - AppThemes.MATCH_SYSTEM.ordinal -> isSystemInDarkTheme() - AppThemes.DARK.ordinal -> true - AppThemes.LIGHT.ordinal -> false - else -> false - } - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - isSystemInDarkTheme() // Force dark mode uses system theme - } else false - ) { - LoginScreen( - prefillEmail = prefillEmail, - scaffoldState = scaffoldState, - isLoading = isLoading, - onBackClicked = { onBackPressedDispatcher.onBackPressed() }, - onLoginClicked = { email, password -> - currentEmail = email - currentPassword = password - viewModel.inputs.email(email) - viewModel.inputs.password(password) - viewModel.inputs.loginClick() - }, - onTermsOfUseClicked = { startDisclaimerScreen(DisclaimerItems.TERMS) }, - onPrivacyPolicyClicked = { startDisclaimerScreen(DisclaimerItems.PRIVACY) }, - onCookiePolicyClicked = { startDisclaimerScreen(DisclaimerItems.COOKIES) }, - onHelpClicked = { startDisclaimerScreen(DisclaimerItems.HELP) }, - onForgotPasswordClicked = { startResetPasswordActivity() }, - resetPasswordDialogMessage = resetPasswordDialogMessage, - showDialog = showDialog, - setShowDialog = { - showDialog = it - this.viewModel.inputs.resetPasswordConfirmationDialogDismissed() - } - ) - } - - when { - error.isNotEmpty() -> { - LaunchedEffect(scaffoldState) { - scaffoldState.snackbarHostState.showSnackbar(error, actionLabel = "error") - error = "" - } - } - - changedPasswordMessage.isNotEmpty() -> { - LaunchedEffect(scaffoldState) { - scaffoldState.snackbarHostState.showSnackbar( - changedPasswordMessage, - actionLabel = "success" - ) - changedPasswordMessage = "" - viewModel.hideChangePasswordSnackbar() - } - } - - createPasswordMessage.isNotEmpty() -> { - LaunchedEffect(scaffoldState) { - scaffoldState.snackbarHostState.showSnackbar( - createPasswordMessage, - actionLabel = "success" - ) - createPasswordMessage = "" - viewModel.hideCreatedPasswordSnackbar() - } - } - } - } - - this.ksString = requireNotNull(env?.ksString()) - - this.viewModel.outputs.tfaChallenge() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { startTwoFactorActivity() } - .addToDisposable(disposables) - - this.viewModel.outputs.loginSuccess() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { onSuccess() } - .addToDisposable(disposables) - - onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - this@LoginActivity.finishWithAnimation() - } - }) - } - - private fun errorMessages() = - this.viewModel.outputs.invalidLoginError() - .map(coalesceWithV2(getString(this.loginDoesNotMatchString))) - .mergeWith( - this.viewModel.outputs.genericLoginError() - .map(coalesceWithV2(getString(this.unableToLoginString))) - ) - - override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) { - super.onActivityResult(requestCode, resultCode, intent) - viewModel.activityResult(create(requestCode, resultCode, intent)) - - if (requestCode != ActivityRequestCodes.LOGIN_FLOW && requestCode != ActivityRequestCodes.RESET_FLOW) { - return - } - - if (requestCode != ActivityRequestCodes.RESET_FLOW) { - setResult(resultCode, intent) - finish() - } - } - - override fun onDestroy() { - disposables.clear() - super.onDestroy() - } - - private fun onSuccess() { - setResult(Activity.RESULT_OK) - finish() - } - - private fun startDisclaimerScreen(disclaimerItems: DisclaimerItems) { - startDisclaimerChromeTab(disclaimerItems, environment) - } - - private fun startTwoFactorActivity() { - val intent = Intent(this, TwoFactorActivity::class.java) - .putExtra(IntentKey.EMAIL, currentEmail) - .putExtra(IntentKey.PASSWORD, currentPassword) - startActivityForResult(intent, ActivityRequestCodes.LOGIN_FLOW) - overridePendingTransition(R.anim.slide_in_right, R.anim.fade_out_slide_out_left) - } - - private fun startResetPasswordActivity() { - val intent = Intent().getResetPasswordIntent(this, email = currentEmail) - startActivityForResult(intent, ActivityRequestCodes.RESET_FLOW) - overridePendingTransition(R.anim.slide_in_right, R.anim.fade_out_slide_out_left) - } -} diff --git a/app/src/main/java/com/kickstarter/ui/activities/LoginToutActivity.kt b/app/src/main/java/com/kickstarter/ui/activities/LoginToutActivity.kt index 0dbfc41fbb..7553c53b8d 100644 --- a/app/src/main/java/com/kickstarter/ui/activities/LoginToutActivity.kt +++ b/app/src/main/java/com/kickstarter/ui/activities/LoginToutActivity.kt @@ -11,7 +11,6 @@ import androidx.annotation.StringRes import androidx.browser.customtabs.CustomTabsIntent import androidx.compose.foundation.isSystemInDarkTheme import androidx.lifecycle.lifecycleScope -import com.facebook.AccessToken import com.kickstarter.R import com.kickstarter.libs.ActivityRequestCodes import com.kickstarter.libs.Environment @@ -34,8 +33,6 @@ import com.kickstarter.ui.compose.designsystem.KickstarterApp import com.kickstarter.ui.data.ActivityResult.Companion.create import com.kickstarter.ui.data.LoginReason import com.kickstarter.ui.extensions.startDisclaimerChromeTab -import com.kickstarter.ui.extensions.startLogin -import com.kickstarter.ui.extensions.startSignup import com.kickstarter.viewmodels.LoginToutViewModel import com.kickstarter.viewmodels.OAuthViewModel import com.kickstarter.viewmodels.OAuthViewModelFactory @@ -67,8 +64,6 @@ class LoginToutActivity : ComponentActivity() { private val oAuthLogcat = "OAuth: " - var oauthFlagEnabled: Boolean = false - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) var darkModeEnabled = false @@ -83,7 +78,6 @@ class LoginToutActivity : ComponentActivity() { theme = env.sharedPreferences() ?.getInt(SharedPreferenceKey.APP_THEME, AppThemes.MATCH_SYSTEM.ordinal) ?: AppThemes.MATCH_SYSTEM.ordinal - oauthFlagEnabled = env.featureFlagClient()?.getBoolean(FlagKey.ANDROID_OAUTH) ?: false } setContent { @@ -103,8 +97,6 @@ class LoginToutActivity : ComponentActivity() { LoginToutScreen( onBackClicked = { onBackPressedDispatcher.onBackPressed() }, onFacebookButtonClicked = { facebookLoginClick() }, - onEmailLoginClicked = { loginButtonClick() }, - onEmailSignupClicked = { signupButtonClick() }, onTermsOfUseClicked = { viewModel.inputs.disclaimerItemClicked(DisclaimerItems.TERMS) }, onPrivacyPolicyClicked = { viewModel.inputs.disclaimerItemClicked( @@ -115,7 +107,6 @@ class LoginToutActivity : ComponentActivity() { onHelpClicked = { viewModel.inputs.disclaimerItemClicked(DisclaimerItems.HELP) }, - featureFlagState = oauthFlagEnabled, onSignUpOrLogInClicked = { oAuthViewModel.produceState(intent = intent) } @@ -125,9 +116,7 @@ class LoginToutActivity : ComponentActivity() { logInAndSignUpAndLoginWithFacebookVM() - if (oauthFlagEnabled) { - setUpOAuthViewModel() - } + setUpOAuthViewModel() } /*** @@ -152,20 +141,6 @@ class LoginToutActivity : ComponentActivity() { .subscribe { finishWithSuccessfulResult() } .addToDisposable(disposables) - viewModel.outputs.startLoginActivity() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - this.startLogin() - } - .addToDisposable(disposables) - - viewModel.outputs.startSignupActivity() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - this.startSignup() - } - .addToDisposable(disposables) - viewModel.outputs.startFacebookConfirmationActivity() .observeOn(AndroidSchedulers.mainThread()) .subscribe { startFacebookConfirmationActivity(it.first, it.second) } @@ -188,11 +163,6 @@ class LoginToutActivity : ComponentActivity() { .subscribe { ViewUtils.showToast(this) } .addToDisposable(disposables) - viewModel.outputs.startTwoFactorChallenge() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { startTwoFactorFacebookChallenge() } - .addToDisposable(disposables) - viewModel.outputs.showUnauthorizedErrorDialog() .observeOn(AndroidSchedulers.mainThread()) .subscribe { @@ -248,11 +218,9 @@ class LoginToutActivity : ComponentActivity() { } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - if (oauthFlagEnabled) { - Timber.d("$oAuthLogcat onNewIntent Intent: $intent, data: ${intent?.data}") - // - Intent generated when the deepLink redirection takes place - intent?.let { oAuthViewModel.produceState(intent = it) } - } + Timber.d("$oAuthLogcat onNewIntent Intent: $intent, data: ${intent?.data}") + // - Intent generated when the deepLink redirection takes place + intent?.let { oAuthViewModel.produceState(intent = it) } } private fun setUpOAuthViewModel() { @@ -287,12 +255,6 @@ class LoginToutActivity : ComponentActivity() { resources.getStringArray(R.array.facebook_permissions_array).asList() ) - private fun loginButtonClick() = - viewModel.inputs.loginClick() - - private fun signupButtonClick() = - viewModel.inputs.signupClick() - private fun showErrorMessageToasts(): Observable { return viewModel.outputs.showMissingFacebookEmailErrorToast() .map(coalesceWithV2(getString(R.string.login_errors_unable_to_log_in))) @@ -324,14 +286,6 @@ class LoginToutActivity : ComponentActivity() { TransitionUtils.transition(this, TransitionUtils.fadeIn()) } - private fun startTwoFactorFacebookChallenge() { - val intent = Intent(this, TwoFactorActivity::class.java) - .putExtra(IntentKey.FACEBOOK_LOGIN, true) - .putExtra(IntentKey.FACEBOOK_TOKEN, AccessToken.getCurrentAccessToken()?.token) - startActivityForResult(intent, ActivityRequestCodes.LOGIN_FLOW) - TransitionUtils.transition(this, TransitionUtils.fadeIn()) - } - @Deprecated("Needs to be replaced with new method, but requires request code usage to go away as well") override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) { super.onActivityResult(requestCode, resultCode, intent) diff --git a/app/src/main/java/com/kickstarter/ui/activities/SignupActivity.kt b/app/src/main/java/com/kickstarter/ui/activities/SignupActivity.kt deleted file mode 100644 index 45a9d9ba48..0000000000 --- a/app/src/main/java/com/kickstarter/ui/activities/SignupActivity.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.kickstarter.ui.activities - -import android.os.Build -import android.os.Bundle -import androidx.activity.compose.setContent -import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material.rememberScaffoldState -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.rxjava2.subscribeAsState -import com.kickstarter.libs.Environment -import com.kickstarter.libs.featureflag.FlagKey -import com.kickstarter.libs.utils.extensions.addToDisposable -import com.kickstarter.libs.utils.extensions.getEnvironment -import com.kickstarter.ui.SharedPreferenceKey -import com.kickstarter.ui.activities.compose.login.SignupScreen -import com.kickstarter.ui.compose.designsystem.KickstarterApp -import com.kickstarter.ui.extensions.startDisclaimerChromeTab -import com.kickstarter.viewmodels.SignupViewModel -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable - -class SignupActivity : AppCompatActivity() { - - private lateinit var viewModelFactory: SignupViewModel.Factory - private val viewModel: SignupViewModel.SignupViewModel by viewModels { viewModelFactory } - private val disposables = CompositeDisposable() - var darkModeEnabled = false - private var environment: Environment? = null - private var theme = AppThemes.MATCH_SYSTEM.ordinal - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - this.getEnvironment()?.let { env -> - environment = env - viewModelFactory = SignupViewModel.Factory(env) - darkModeEnabled = env.featureFlagClient()?.getBoolean(FlagKey.ANDROID_DARK_MODE_ENABLED) ?: false - theme = env.sharedPreferences() - ?.getInt(SharedPreferenceKey.APP_THEME, AppThemes.MATCH_SYSTEM.ordinal) - ?: AppThemes.MATCH_SYSTEM.ordinal - } - - setContent { - var scaffoldState = rememberScaffoldState() - - var showProgressBar = - viewModel.outputs.progressBarIsVisible().subscribeAsState(initial = false).value - - var error = viewModel.outputs.errorString().subscribeAsState(initial = "").value - - when { - error.isNotEmpty() -> { - LaunchedEffect(scaffoldState) { - scaffoldState.snackbarHostState.showSnackbar(error) - } - } - } - KickstarterApp( - useDarkTheme = - if (darkModeEnabled) { - when (theme) { - AppThemes.MATCH_SYSTEM.ordinal -> isSystemInDarkTheme() - AppThemes.DARK.ordinal -> true - AppThemes.LIGHT.ordinal -> false - else -> false - } - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - isSystemInDarkTheme() // Force dark mode uses system theme - } else false - ) { - SignupScreen( - onBackClicked = { onBackPressedDispatcher.onBackPressed() }, - onSignupButtonClicked = { name, email, password, sendNewsletters -> - viewModel.inputs.name(name) - viewModel.inputs.email(email) - viewModel.inputs.password(password) - viewModel.inputs.sendNewsletters(sendNewsletters) - viewModel.inputs.signupClick() - }, - showProgressBar = showProgressBar, - isFormSubmitting = viewModel.outputs.formSubmitting().subscribeAsState(initial = false).value, - onTermsOfUseClicked = { startDisclaimerScreen(DisclaimerItems.TERMS) }, - onPrivacyPolicyClicked = { startDisclaimerScreen(DisclaimerItems.PRIVACY) }, - onCookiePolicyClicked = { startDisclaimerScreen(DisclaimerItems.COOKIES) }, - onHelpClicked = { startDisclaimerScreen(DisclaimerItems.HELP) }, - scaffoldState = scaffoldState - ) - } - } - - viewModel.outputs.signupSuccess() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { onSuccess() } - .addToDisposable(disposables) - } - - override fun onDestroy() { - disposables.clear() - super.onDestroy() - } - - private fun onSuccess() { - setResult(RESULT_OK) - finish() - } - - private fun startDisclaimerScreen(disclaimerItems: DisclaimerItems) { - startDisclaimerChromeTab(disclaimerItems, environment) - } -} diff --git a/app/src/main/java/com/kickstarter/ui/activities/TwoFactorActivity.kt b/app/src/main/java/com/kickstarter/ui/activities/TwoFactorActivity.kt deleted file mode 100644 index e143614d1b..0000000000 --- a/app/src/main/java/com/kickstarter/ui/activities/TwoFactorActivity.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.kickstarter.ui.activities - -import android.os.Build -import android.os.Bundle -import androidx.activity.compose.setContent -import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material.rememberScaffoldState -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rxjava2.subscribeAsState -import androidx.compose.runtime.setValue -import com.kickstarter.R -import com.kickstarter.libs.Environment -import com.kickstarter.libs.featureflag.FlagKey -import com.kickstarter.libs.utils.extensions.addToDisposable -import com.kickstarter.libs.utils.extensions.getEnvironment -import com.kickstarter.ui.SharedPreferenceKey -import com.kickstarter.ui.activities.compose.login.TwoFactorScreen -import com.kickstarter.ui.compose.designsystem.KickstarterApp -import com.kickstarter.ui.extensions.startDisclaimerChromeTab -import com.kickstarter.viewmodels.TwoFactorViewModel -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable - -class TwoFactorActivity : AppCompatActivity() { - private lateinit var viewModelFactory: TwoFactorViewModel.Factory - private val viewModel: TwoFactorViewModel.TwoFactorViewModel by viewModels { viewModelFactory } - private val disposables = CompositeDisposable() - private var darkModeEnabled = false - private var environment: Environment? = null - private var theme = AppThemes.MATCH_SYSTEM.ordinal - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - this.getEnvironment()?.let { env -> - environment = env - viewModelFactory = TwoFactorViewModel.Factory(env, intent) - darkModeEnabled = - env.featureFlagClient()?.getBoolean(FlagKey.ANDROID_DARK_MODE_ENABLED) ?: false - theme = env.sharedPreferences() - ?.getInt(SharedPreferenceKey.APP_THEME, AppThemes.MATCH_SYSTEM.ordinal) - ?: AppThemes.MATCH_SYSTEM.ordinal - } - - setContent { - var error by remember { mutableStateOf("") } - - errorMessages().subscribe { - error = it - }.addToDisposable(disposables) - - var resendMessage by remember { mutableStateOf("") } - - viewModel.showResendCodeConfirmation().subscribe { - resendMessage = getString(R.string.messages_navigation_sent) - }.addToDisposable(disposables) - - var scaffoldState = rememberScaffoldState() - - var formSubmitting = viewModel.formSubmitting().subscribeAsState(initial = false).value - - when { - error.isNotEmpty() -> { - LaunchedEffect(scaffoldState) { - scaffoldState.snackbarHostState.showSnackbar(message = error, actionLabel = "error") - error = "" - } - } - - resendMessage.isNotEmpty() -> { - LaunchedEffect(scaffoldState) { - scaffoldState.snackbarHostState.showSnackbar(message = resendMessage, actionLabel = "action") - resendMessage = "" - } - } - } - - KickstarterApp( - useDarkTheme = - if (darkModeEnabled) { - when (theme) { - AppThemes.MATCH_SYSTEM.ordinal -> isSystemInDarkTheme() - AppThemes.DARK.ordinal -> true - AppThemes.LIGHT.ordinal -> false - else -> false - } - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - isSystemInDarkTheme() // Force dark mode uses system theme - } else false - ) { - TwoFactorScreen( - scaffoldState = scaffoldState, - onBackClicked = { onBackPressedDispatcher.onBackPressed() }, - onTermsOfUseClicked = { startDisclaimerScreen(DisclaimerItems.TERMS) }, - onPrivacyPolicyClicked = { startDisclaimerScreen(DisclaimerItems.PRIVACY) }, - onCookiePolicyClicked = { startDisclaimerScreen(DisclaimerItems.COOKIES) }, - onHelpClicked = { startDisclaimerScreen(DisclaimerItems.HELP) }, - onResendClicked = { resendButtonOnClick() }, - onSubmitClicked = { - codeEditTextOnTextChanged(it) - loginButtonOnClick() - }, - isLoading = formSubmitting - ) - } - } - - viewModel.outputs.tfaSuccess() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { onSuccess() } - .addToDisposable(disposables) - } - - override fun onDestroy() { - disposables.clear() - super.onDestroy() - } - - private fun startDisclaimerScreen(disclaimerItems: DisclaimerItems) { - startDisclaimerChromeTab(disclaimerItems, environment) - } - - private fun errorMessages(): Observable { - return viewModel.outputs.tfaCodeMismatchError().map { getString(R.string.two_factor_error_message) } - .mergeWith(viewModel.outputs.genericTfaError().map { getString(R.string.login_errors_unable_to_log_in) }) - } - - private fun codeEditTextOnTextChanged(code: CharSequence) { - viewModel.inputs.code(code.toString()) - } - - private fun resendButtonOnClick() { - viewModel.inputs.resendClick() - } - - private fun loginButtonOnClick() { - viewModel.inputs.loginClick() - } - - private fun onSuccess() { - setResult(RESULT_OK) - finish() - } -} diff --git a/app/src/main/java/com/kickstarter/ui/activities/compose/login/LoginScreen.kt b/app/src/main/java/com/kickstarter/ui/activities/compose/login/LoginScreen.kt deleted file mode 100644 index a15b767afe..0000000000 --- a/app/src/main/java/com/kickstarter/ui/activities/compose/login/LoginScreen.kt +++ /dev/null @@ -1,228 +0,0 @@ -package com.kickstarter.ui.activities.compose.login - -import android.content.res.Configuration -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.Scaffold -import androidx.compose.material.ScaffoldState -import androidx.compose.material.SnackbarHost -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.material.rememberScaffoldState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import com.kickstarter.R -import com.kickstarter.libs.utils.extensions.isEmail -import com.kickstarter.libs.utils.extensions.isValidPassword -import com.kickstarter.ui.compose.designsystem.KSAlertDialogNoHeadline -import com.kickstarter.ui.compose.designsystem.KSClickableText -import com.kickstarter.ui.compose.designsystem.KSErrorSnackbar -import com.kickstarter.ui.compose.designsystem.KSHiddenTextInput -import com.kickstarter.ui.compose.designsystem.KSLinearProgressIndicator -import com.kickstarter.ui.compose.designsystem.KSPrimaryGreenButton -import com.kickstarter.ui.compose.designsystem.KSSuccessSnackbar -import com.kickstarter.ui.compose.designsystem.KSTextInput -import com.kickstarter.ui.compose.designsystem.KSTheme -import com.kickstarter.ui.compose.designsystem.KSTheme.dimensions -import com.kickstarter.ui.toolbars.compose.TopToolBar - -@Composable -@Preview(name = "Light", uiMode = Configuration.UI_MODE_NIGHT_NO) -@Preview(name = "Dark", uiMode = Configuration.UI_MODE_NIGHT_YES) -fun LoginScreenPreview() { - KSTheme { - LoginScreen( - "", - rememberScaffoldState(), - false, - {}, - { _, _ -> }, - {}, - {}, - {}, - {}, - {}, - "", - false, - {} - ) - } -} - -enum class LoginTestTag { - LOADING, - PAGE_TITLE, - BACK_BUTTON, - OPTIONS_ICON, - EMAIL, - PASSWORD, - LOGIN_BUTTON, - FORGOT_PASSWORD_TEXT -} - -@Composable -fun LoginScreen( - prefillEmail: String = "", - scaffoldState: ScaffoldState, - isLoading: Boolean, - onBackClicked: () -> Unit, - onLoginClicked: (email: String, password: String) -> Unit, - onTermsOfUseClicked: () -> Unit, - onPrivacyPolicyClicked: () -> Unit, - onCookiePolicyClicked: () -> Unit, - onHelpClicked: () -> Unit, - onForgotPasswordClicked: () -> Unit, - resetPasswordDialogMessage: String, - showDialog: Boolean, - setShowDialog: (Boolean) -> Unit -) { - var expanded by remember { mutableStateOf(false) } - var email by rememberSaveable { mutableStateOf("") } - var password by rememberSaveable { mutableStateOf("") } - - var logInButtonEnabled = when { - email.isEmail() && password.isValidPassword() -> true - else -> false - } - - Scaffold( - topBar = { - TopToolBar( - title = stringResource(id = R.string.login_navbar_title), - titleColor = KSTheme.colors.kds_support_700, - titleModifier = Modifier.testTag(LoginTestTag.PAGE_TITLE.name), - leftOnClickAction = onBackClicked, - leftIconColor = KSTheme.colors.kds_support_700, - leftIconModifier = Modifier.testTag(LoginTestTag.BACK_BUTTON.name), - backgroundColor = KSTheme.colors.kds_white, - right = { - IconButton( - modifier = Modifier.testTag(LoginTestTag.OPTIONS_ICON.name), - onClick = { expanded = !expanded }, - enabled = true - ) { - Box { - Icon( - imageVector = Icons.Default.MoreVert, - contentDescription = stringResource( - id = R.string.general_navigation_accessibility_button_help_menu_label - ), - tint = KSTheme.colors.kds_black - ) - - KSLoginDropdownMenu( - expanded = expanded, - onDismissed = { expanded = !expanded }, - onTermsOfUseClicked = onTermsOfUseClicked, - onPrivacyPolicyClicked = onPrivacyPolicyClicked, - onCookiePolicyClicked = onCookiePolicyClicked, - onHelpClicked = onHelpClicked - ) - } - } - } - ) - }, - snackbarHost = { - SnackbarHost( - hostState = scaffoldState.snackbarHostState, - snackbar = { data -> - if (data.actionLabel == "error") { - KSErrorSnackbar(text = data.message) - } else { - KSSuccessSnackbar(text = data.message) - } - } - ) - }, - scaffoldState = scaffoldState - ) { padding -> - Column( - Modifier - .background(KSTheme.colors.kds_white) - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding( - PaddingValues( - start = dimensions.paddingLarge, - end = dimensions.paddingLarge - ) - ), - horizontalAlignment = Alignment.CenterHorizontally - ) { - if (isLoading) { - KSLinearProgressIndicator( - modifier = Modifier - .fillMaxWidth() - .testTag(LoginTestTag.LOADING.name) - ) - } - - Spacer(modifier = Modifier.height(dimensions.paddingMedium)) - - KSTextInput( - modifier = Modifier - .fillMaxWidth() - .testTag(LoginTestTag.EMAIL.name), - label = stringResource(id = R.string.email), - initialValue = prefillEmail, - onValueChanged = { email = it } - ) - - Spacer(modifier = Modifier.height(dimensions.paddingMedium)) - - KSHiddenTextInput( - Modifier - .fillMaxWidth() - .testTag(LoginTestTag.PASSWORD.name), - label = stringResource(id = R.string.login_placeholder_password), - onValueChanged = { password = it } - ) - - Spacer(modifier = Modifier.height(dimensions.paddingLarge)) - - KSPrimaryGreenButton( - modifier = Modifier.testTag(LoginTestTag.LOGIN_BUTTON.name), - onClickAction = { onLoginClicked(email, password) }, - text = stringResource(id = R.string.login_buttons_log_in), - isEnabled = logInButtonEnabled && !isLoading - ) - - Spacer(modifier = Modifier.height(dimensions.paddingMedium)) - - KSClickableText( - modifier = Modifier.testTag(LoginTestTag.FORGOT_PASSWORD_TEXT.name), - resourceId = R.string.forgot_password_title, - clickCallback = onForgotPasswordClicked - ) - - if (showDialog) { - KSAlertDialogNoHeadline( - setShowDialog = setShowDialog, - bodyText = resetPasswordDialogMessage, - rightButtonText = stringResource(id = R.string.login_errors_button_ok) - ) - } - } - } -} diff --git a/app/src/main/java/com/kickstarter/ui/activities/compose/login/LoginToutScreen.kt b/app/src/main/java/com/kickstarter/ui/activities/compose/login/LoginToutScreen.kt index 94261cbd00..3672a46410 100644 --- a/app/src/main/java/com/kickstarter/ui/activities/compose/login/LoginToutScreen.kt +++ b/app/src/main/java/com/kickstarter/ui/activities/compose/login/LoginToutScreen.kt @@ -33,7 +33,6 @@ import androidx.compose.ui.tooling.preview.Preview import com.kickstarter.R import com.kickstarter.ui.compose.designsystem.KSFacebookButton import com.kickstarter.ui.compose.designsystem.KSPrimaryGreenButton -import com.kickstarter.ui.compose.designsystem.KSSecondaryGreyButton import com.kickstarter.ui.compose.designsystem.KSTheme import com.kickstarter.ui.compose.designsystem.KSTheme.colors import com.kickstarter.ui.compose.designsystem.KSTheme.dimensions @@ -52,9 +51,6 @@ fun LoginToutScreenPreview() { {}, {}, {}, - {}, - {}, - false, {} ) } @@ -78,13 +74,10 @@ enum class LoginToutTestTag { fun LoginToutScreen( onBackClicked: () -> Unit, onFacebookButtonClicked: () -> Unit, - onEmailLoginClicked: () -> Unit, - onEmailSignupClicked: () -> Unit, onTermsOfUseClicked: () -> Unit, onPrivacyPolicyClicked: () -> Unit, onCookiePolicyClicked: () -> Unit, onHelpClicked: () -> Unit, - featureFlagState: Boolean = false, onSignUpOrLogInClicked: () -> Unit ) { var expanded by remember { mutableStateOf(false) } @@ -177,30 +170,13 @@ fun LoginToutScreen( Spacer(modifier = Modifier.height(dimensions.paddingMedium)) - if (featureFlagState) { - KSPrimaryGreenButton( - modifier = Modifier.testTag(LoginToutTestTag.LOG_IN_OR_SIGN_UP.name), - onClickAction = onSignUpOrLogInClicked, - text = stringResource(id = R.string.discovery_onboarding_buttons_signup_or_login), - isEnabled = true - ) - } else { - KSPrimaryGreenButton( - modifier = Modifier.testTag(LoginToutTestTag.EMAIL_LOG_IN_BUTTON.name), - onClickAction = onEmailLoginClicked, - text = stringResource(id = R.string.login_buttons_log_in_email), - isEnabled = true - ) - - Spacer(modifier = Modifier.height(dimensions.paddingMedium)) - - KSSecondaryGreyButton( - modifier = Modifier.testTag(LoginToutTestTag.EMAIL_SIGN_UP_BUTTON.name), - onClickAction = onEmailSignupClicked, - text = stringResource(id = R.string.signup_button_email), - isEnabled = true - ) - } + KSPrimaryGreenButton( + modifier = Modifier.testTag(LoginToutTestTag.LOG_IN_OR_SIGN_UP.name), + onClickAction = onSignUpOrLogInClicked, + text = stringResource(id = R.string.discovery_onboarding_buttons_signup_or_login), + isEnabled = true + ) + Spacer(modifier = Modifier.height(dimensions.paddingMedium)) LogInSignUpClickableDisclaimerText( diff --git a/app/src/main/java/com/kickstarter/ui/activities/compose/login/SignupScreen.kt b/app/src/main/java/com/kickstarter/ui/activities/compose/login/SignupScreen.kt deleted file mode 100644 index 4b40677cbe..0000000000 --- a/app/src/main/java/com/kickstarter/ui/activities/compose/login/SignupScreen.kt +++ /dev/null @@ -1,291 +0,0 @@ -package com.kickstarter.ui.activities.compose.login - -import android.content.res.Configuration -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.Scaffold -import androidx.compose.material.ScaffoldState -import androidx.compose.material.SnackbarHost -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.material.rememberScaffoldState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusDirection -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.tooling.preview.Preview -import com.kickstarter.R -import com.kickstarter.libs.utils.extensions.isEmail -import com.kickstarter.libs.utils.extensions.validPassword -import com.kickstarter.ui.compose.designsystem.KSErrorSnackbar -import com.kickstarter.ui.compose.designsystem.KSHiddenTextInput -import com.kickstarter.ui.compose.designsystem.KSLinearProgressIndicator -import com.kickstarter.ui.compose.designsystem.KSPrimaryGreenButton -import com.kickstarter.ui.compose.designsystem.KSSwitch -import com.kickstarter.ui.compose.designsystem.KSTextInput -import com.kickstarter.ui.compose.designsystem.KSTheme -import com.kickstarter.ui.toolbars.compose.TopToolBar - -@Composable -@Preview(name = "Light", uiMode = Configuration.UI_MODE_NIGHT_NO) -@Preview(name = "Dark", uiMode = Configuration.UI_MODE_NIGHT_YES) -fun SignupPreview() { - KSTheme { - SignupScreen( - onBackClicked = {}, - onSignupButtonClicked = { _, _, _, _ -> }, - showProgressBar = true, - isFormSubmitting = false, - onTermsOfUseClicked = { }, - onPrivacyPolicyClicked = { }, - onCookiePolicyClicked = { }, - onHelpClicked = { }, - scaffoldState = rememberScaffoldState() - ) - } -} -enum class SignupScreenTestTag { - PAGE_TITLE, - NAME_EDIT_TEXT, - EMAIL_EDIT_TEXT, - BACK_BUTTON, - OPTIONS_ICON, - PROGRESS_BAR, - PASSWORD_EDIT_TEXT, - NEWSLETTER_OPT_IN_TEXT, - NEWSLETTER_OPT_IN_SWITCH, - SIGNUP_BUTTON -} - -@Composable -fun SignupScreen( - onBackClicked: () -> Unit, - onSignupButtonClicked: (name: String, email: String, password: String, receiveNewsLetter: Boolean) -> Unit, - showProgressBar: Boolean, - isFormSubmitting: Boolean, - onTermsOfUseClicked: () -> Unit, - onPrivacyPolicyClicked: () -> Unit, - onCookiePolicyClicked: () -> Unit, - onHelpClicked: () -> Unit, - scaffoldState: ScaffoldState -) { - - var expanded by remember { mutableStateOf(false) } - - var name by remember { mutableStateOf("") } - - var email by remember { mutableStateOf("") } - - var password by rememberSaveable { mutableStateOf("") } - - val focusManager = LocalFocusManager.current - - var receiveNewsletterChecked by remember { mutableStateOf(false) } - - val signUpButtonEnabled = when { - name.isNotEmpty() && - email.isEmail() && - !isFormSubmitting && - password.validPassword() -> true - - else -> false - } - - Scaffold( - scaffoldState = scaffoldState, - topBar = { - TopToolBar( - title = stringResource(id = R.string.Sign_up), - titleColor = KSTheme.colors.kds_support_700, - titleModifier = Modifier.testTag(SignupScreenTestTag.PAGE_TITLE.name), - leftOnClickAction = onBackClicked, - leftIconColor = KSTheme.colors.kds_support_700, - leftIconModifier = Modifier.testTag(SignupScreenTestTag.BACK_BUTTON.name), - backgroundColor = KSTheme.colors.kds_white, - right = { - IconButton( - modifier = Modifier.testTag(SignupScreenTestTag.OPTIONS_ICON.name), - onClick = { expanded = !expanded }, - enabled = true - ) { - Box { - Icon( - imageVector = Icons.Default.MoreVert, - contentDescription = stringResource( - id = R.string.general_navigation_accessibility_button_help_menu_label - ), - tint = KSTheme.colors.kds_black - ) - - KSLoginDropdownMenu( - expanded = expanded, - onDismissed = { expanded = !expanded }, - onTermsOfUseClicked = onTermsOfUseClicked, - onPrivacyPolicyClicked = onPrivacyPolicyClicked, - onCookiePolicyClicked = onCookiePolicyClicked, - onHelpClicked = onHelpClicked - ) - } - } - } - ) - }, - snackbarHost = { - SnackbarHost( - hostState = scaffoldState.snackbarHostState, - snackbar = { data -> - KSErrorSnackbar(text = data.message) - } - ) - } - ) { padding -> - Column( - Modifier - .background(KSTheme.colors.kds_white) - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(padding) - ) { - AnimatedVisibility(visible = showProgressBar) { - KSLinearProgressIndicator( - modifier = Modifier - .fillMaxWidth() - .testTag(SignupScreenTestTag.PROGRESS_BAR.name) - ) - } - - Column( - Modifier - .background(color = KSTheme.colors.kds_white) - .padding(KSTheme.dimensions.paddingMedium) - .fillMaxWidth() - - ) { - KSTextInput( - modifier = Modifier - .fillMaxWidth() - .testTag(SignupScreenTestTag.NAME_EDIT_TEXT.name), - onValueChanged = { name = it }, - label = stringResource(id = R.string.Name), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), - keyboardActions = KeyboardActions( - onNext = { - focusManager.moveFocus( - focusDirection = FocusDirection.Down - ) - } - ) - ) - - Spacer(modifier = Modifier.height(KSTheme.dimensions.listItemSpacingMedium)) - - KSTextInput( - modifier = Modifier - .fillMaxWidth() - .testTag(SignupScreenTestTag.EMAIL_EDIT_TEXT.name), - onValueChanged = { email = it }, - label = stringResource(id = R.string.login_placeholder_email), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), - keyboardActions = KeyboardActions( - onNext = { - focusManager.moveFocus( - focusDirection = FocusDirection.Down - ) - } - ) - ) - - Spacer(modifier = Modifier.height(KSTheme.dimensions.listItemSpacingMedium)) - - KSHiddenTextInput( - modifier = Modifier - .fillMaxWidth() - .testTag(SignupScreenTestTag.PASSWORD_EDIT_TEXT.name), - onValueChanged = { password = it }, - label = stringResource(id = R.string.signup_input_fields_password_min_characters), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - keyboardActions = KeyboardActions( - onDone = { - focusManager.clearFocus() - } - ) - ) - - Spacer(modifier = Modifier.height(KSTheme.dimensions.listItemSpacingMedium)) - - Row( - modifier = Modifier - .background(color = KSTheme.colors.kds_white) - ) { - - Text( - modifier = Modifier - .weight(1f) - .testTag(SignupScreenTestTag.NEWSLETTER_OPT_IN_TEXT.name), - text = stringResource(id = R.string.signup_newsletter_full_opt_out), - style = KSTheme.typography.caption2, - color = KSTheme.colors.kds_support_700 - ) - - KSSwitch( - modifier = Modifier.testTag(SignupScreenTestTag.NEWSLETTER_OPT_IN_SWITCH.name), - checked = receiveNewsletterChecked, - onCheckChanged = { - receiveNewsletterChecked = it - }, - enabled = true - ) - } - - Spacer(modifier = Modifier.height(KSTheme.dimensions.listItemSpacingMedium)) - - KSPrimaryGreenButton( - modifier = Modifier.testTag(SignupScreenTestTag.SIGNUP_BUTTON.name), - text = stringResource(id = R.string.Sign_up), - onClickAction = { - onSignupButtonClicked.invoke( - name, - email, - password, - receiveNewsletterChecked - ) - }, - isEnabled = signUpButtonEnabled - ) - - Spacer(modifier = Modifier.height(KSTheme.dimensions.paddingMedium)) - - LogInSignUpClickableDisclaimerText( - onTermsOfUseClicked = onTermsOfUseClicked, - onPrivacyPolicyClicked = onPrivacyPolicyClicked, - onCookiePolicyClicked = onCookiePolicyClicked - ) - - Spacer(modifier = Modifier.height(KSTheme.dimensions.paddingDoubleLarge)) - } - } - } -} diff --git a/app/src/main/java/com/kickstarter/ui/activities/compose/login/TwoFactorScreen.kt b/app/src/main/java/com/kickstarter/ui/activities/compose/login/TwoFactorScreen.kt deleted file mode 100644 index cce89b4f3d..0000000000 --- a/app/src/main/java/com/kickstarter/ui/activities/compose/login/TwoFactorScreen.kt +++ /dev/null @@ -1,214 +0,0 @@ -package com.kickstarter.ui.activities.compose.login - -import android.content.res.Configuration -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.Scaffold -import androidx.compose.material.ScaffoldState -import androidx.compose.material.SnackbarHost -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.material.rememberScaffoldState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import com.kickstarter.R -import com.kickstarter.ui.compose.designsystem.KSErrorSnackbar -import com.kickstarter.ui.compose.designsystem.KSLinearProgressIndicator -import com.kickstarter.ui.compose.designsystem.KSPrimaryGreenButton -import com.kickstarter.ui.compose.designsystem.KSSecondaryGreyButton -import com.kickstarter.ui.compose.designsystem.KSSuccessSnackbar -import com.kickstarter.ui.compose.designsystem.KSTextInput -import com.kickstarter.ui.compose.designsystem.KSTheme -import com.kickstarter.ui.compose.designsystem.KSTheme.colors -import com.kickstarter.ui.compose.designsystem.KSTheme.dimensions -import com.kickstarter.ui.compose.designsystem.KSTheme.typography -import com.kickstarter.ui.toolbars.compose.TopToolBar - -@Composable -@Preview(name = "Light", uiMode = Configuration.UI_MODE_NIGHT_NO) -@Preview(name = "Dark", uiMode = Configuration.UI_MODE_NIGHT_YES) -fun TwoFactorScreenPreview() { - KSTheme { - TwoFactorScreen( - rememberScaffoldState(), - {}, - {}, - {}, - {}, - {}, - {}, - {}, - false - ) - } -} - -enum class TwoFactorScreenTestTag { - BACK_BUTTON, - PAGE_TITLE, - OPTIONS_ICON, - LOADING, - HEADLINE, - CODE, - RESEND, - SUBMIT -} - -@Composable -fun TwoFactorScreen( - scaffoldState: ScaffoldState, - onBackClicked: () -> Unit, - onTermsOfUseClicked: () -> Unit, - onPrivacyPolicyClicked: () -> Unit, - onCookiePolicyClicked: () -> Unit, - onHelpClicked: () -> Unit, - onResendClicked: () -> Unit, - onSubmitClicked: (String) -> Unit, - isLoading: Boolean -) { - var expanded by remember { mutableStateOf(false) } - var code by rememberSaveable { mutableStateOf("") } - var submitEnabled = code.isNotEmpty() - Scaffold( - topBar = { - TopToolBar( - title = stringResource(id = R.string.two_factor_title), - titleColor = colors.kds_support_700, - titleModifier = Modifier.testTag(TwoFactorScreenTestTag.PAGE_TITLE.name), - leftOnClickAction = onBackClicked, - leftIconColor = colors.kds_support_700, - leftIconModifier = Modifier.testTag(TwoFactorScreenTestTag.BACK_BUTTON.name), - backgroundColor = colors.kds_white, - right = { - IconButton( - modifier = Modifier.testTag(TwoFactorScreenTestTag.OPTIONS_ICON.name), - onClick = { expanded = !expanded }, - enabled = true - ) { - Box { - Icon( - imageVector = Icons.Default.MoreVert, - contentDescription = stringResource( - id = R.string.general_navigation_accessibility_button_help_menu_label - ), - tint = colors.kds_black - ) - - KSLoginDropdownMenu( - expanded = expanded, - onDismissed = { expanded = !expanded }, - onTermsOfUseClicked = onTermsOfUseClicked, - onPrivacyPolicyClicked = onPrivacyPolicyClicked, - onCookiePolicyClicked = onCookiePolicyClicked, - onHelpClicked = onHelpClicked - ) - } - } - } - ) - }, - scaffoldState = scaffoldState, - snackbarHost = { - SnackbarHost( - hostState = scaffoldState.snackbarHostState, - snackbar = { data -> - if (data.actionLabel == "error") { - KSErrorSnackbar(text = data.message) - } else { - KSSuccessSnackbar(text = data.message) - } - } - ) - } - ) { padding -> - Column( - Modifier - .background(colors.kds_white) - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding( - PaddingValues( - start = dimensions.paddingLarge, - end = dimensions.paddingLarge - ) - ), - horizontalAlignment = Alignment.CenterHorizontally - ) { - if (isLoading) { - KSLinearProgressIndicator( - modifier = Modifier - .fillMaxWidth() - .testTag(TwoFactorScreenTestTag.LOADING.name) - ) - } - - Spacer(modifier = Modifier.height(dimensions.paddingDoubleLarge)) - - Text( - modifier = Modifier.testTag(TwoFactorScreenTestTag.HEADLINE.name), - text = stringResource(id = R.string.two_factor_message), - style = typography.subheadline, - color = colors.kds_support_700 - ) - - Spacer(modifier = Modifier.height(dimensions.paddingLarge)) - - KSTextInput( - modifier = Modifier - .fillMaxWidth() - .testTag(TwoFactorScreenTestTag.CODE.name), - label = stringResource(id = R.string.two_factor_code_placeholder), - onValueChanged = { code = it } - ) - - Spacer(modifier = Modifier.height(dimensions.paddingXLarge)) - - Row { - KSSecondaryGreyButton( - modifier = Modifier - .fillMaxWidth() - .weight(1f) - .testTag(TwoFactorScreenTestTag.RESEND.name), - onClickAction = onResendClicked, - isEnabled = true, - text = stringResource(id = R.string.two_factor_buttons_resend), - ) - - Spacer(modifier = Modifier.width(dimensions.paddingMediumSmall)) - - KSPrimaryGreenButton( - modifier = Modifier - .fillMaxWidth() - .weight(1f) - .testTag(TwoFactorScreenTestTag.SUBMIT.name), - onClickAction = { onSubmitClicked(code) }, - text = stringResource(id = R.string.two_factor_buttons_submit), - isEnabled = submitEnabled - ) - } - } - } -} diff --git a/app/src/main/java/com/kickstarter/ui/extensions/ActivityExt.kt b/app/src/main/java/com/kickstarter/ui/extensions/ActivityExt.kt index 643c70f75e..722a4c3bea 100644 --- a/app/src/main/java/com/kickstarter/ui/extensions/ActivityExt.kt +++ b/app/src/main/java/com/kickstarter/ui/extensions/ActivityExt.kt @@ -15,14 +15,12 @@ import androidx.lifecycle.Lifecycle import com.google.android.play.core.review.ReviewInfo import com.google.android.play.core.review.ReviewManagerFactory import com.kickstarter.R -import com.kickstarter.libs.ActivityRequestCodes import com.kickstarter.libs.Environment import com.kickstarter.libs.RefTag import com.kickstarter.libs.utils.Secrets import com.kickstarter.libs.utils.TransitionUtils import com.kickstarter.libs.utils.UrlUtils import com.kickstarter.libs.utils.extensions.getCreatorBioWebViewActivityIntent -import com.kickstarter.libs.utils.extensions.getLoginActivityIntent import com.kickstarter.libs.utils.extensions.getPreLaunchProjectActivity import com.kickstarter.libs.utils.extensions.getProjectUpdatesActivityIntent import com.kickstarter.libs.utils.extensions.getReportProjectActivityIntent @@ -38,7 +36,6 @@ import com.kickstarter.ui.IntentKey import com.kickstarter.ui.activities.DisclaimerItems import com.kickstarter.ui.activities.HelpActivity import com.kickstarter.ui.activities.LoginToutActivity -import com.kickstarter.ui.activities.SignupActivity import com.kickstarter.ui.data.PledgeData import com.kickstarter.ui.data.PledgeReason import com.kickstarter.ui.data.ProjectData @@ -240,18 +237,6 @@ fun Activity.startPreLaunchProjectActivity(uri: Uri, project: Project, previousS TransitionUtils.transition(this, TransitionUtils.slideInFromRight()) } -fun Activity.startLogin() { - val intent = Intent().getLoginActivityIntent(this) - startActivityForResult(intent, ActivityRequestCodes.LOGIN_FLOW) - TransitionUtils.transition(this, TransitionUtils.slideInFromRight()) -} - -fun Activity.startSignup() { - val intent = Intent().setClass(this, SignupActivity::class.java) - startActivityForResult(intent, ActivityRequestCodes.LOGIN_FLOW) - TransitionUtils.transition(this, TransitionUtils.slideInFromRight()) -} - fun Activity.startDisclaimerChromeTab(disclaimerItem: DisclaimerItems, environment: Environment?) { val path = when (disclaimerItem) { DisclaimerItems.TERMS -> HelpActivity.TERMS_OF_USE diff --git a/app/src/main/java/com/kickstarter/viewmodels/LoginToutViewModel.kt b/app/src/main/java/com/kickstarter/viewmodels/LoginToutViewModel.kt index e5a253a9ef..afdbdf8a05 100644 --- a/app/src/main/java/com/kickstarter/viewmodels/LoginToutViewModel.kt +++ b/app/src/main/java/com/kickstarter/viewmodels/LoginToutViewModel.kt @@ -40,12 +40,6 @@ interface LoginToutViewModel { /** Call when the Login to Facebook button is clicked. */ fun facebookLoginClick(activity: LoginToutActivity?, facebookPermissions: List) - /** Call when the login button is clicked. */ - fun loginClick() - - /** Call when the signup button is clicked. */ - fun signupClick() - /** Call when the disclaimer Item is clicked. */ fun disclaimerItemClicked(disclaimerItem: DisclaimerItems) @@ -81,12 +75,6 @@ interface LoginToutViewModel { /** Emits when the login activity should be started. */ fun startLoginActivity(): Observable - /** Emits when the signup activity should be started. */ - fun startSignupActivity(): Observable - - /** Emits when a user has successfully logged in using Facebook, but has require two-factor authentication enabled. */ - fun startTwoFactorChallenge(): Observable - /** Emits when click one of disclaimer items */ fun showDisclaimerActivity(): Observable @@ -160,7 +148,6 @@ interface LoginToutViewModel { private val startResetPasswordActivity = BehaviorSubject.create() private val startFacebookConfirmationActivity: Observable> private val startLoginActivity: Observable - private val startSignupActivity: Observable private val showDisclaimerActivity: Observable private val finishOauthWithSuccessfulResult = BehaviorSubject.create() @@ -190,14 +177,6 @@ interface LoginToutViewModel { onResetPasswordFacebookErrorDialogClicked.onNext(Unit) } - override fun loginClick() { - loginClick.onNext(Unit) - } - - override fun signupClick() { - signupClick.onNext(Unit) - } - override fun disclaimerItemClicked(disclaimerItem: DisclaimerItems) { disclaimerItemClicked.onNext(disclaimerItem) } @@ -244,16 +223,6 @@ interface LoginToutViewModel { return startLoginActivity } - override fun startSignupActivity(): Observable { - return startSignupActivity - } - - override fun startTwoFactorChallenge(): Observable { - return loginError - .filter(ErrorEnvelope::isTfaRequiredError) - .map { } - } - override fun showDisclaimerActivity(): Observable { return showDisclaimerActivity } @@ -318,7 +287,6 @@ interface LoginToutViewModel { .addToDisposable(disposables) startLoginActivity = loginClick - startSignupActivity = signupClick showDisclaimerActivity = disclaimerItemClicked facebookLoginClick @@ -331,14 +299,6 @@ interface LoginToutViewModel { } .addToDisposable(disposables) - loginClick - .subscribe { analyticEvents?.trackLogInInitiateCtaClicked() } - .addToDisposable(disposables) - - signupClick - .subscribe { analyticEvents?.trackSignUpInitiateCtaClicked() } - .addToDisposable(disposables) - onResetPasswordFacebookErrorDialogClicked .subscribe { startResetPasswordActivity.onNext(Unit) } .addToDisposable(disposables) diff --git a/app/src/main/java/com/kickstarter/viewmodels/LoginViewModel.kt b/app/src/main/java/com/kickstarter/viewmodels/LoginViewModel.kt deleted file mode 100644 index 9ec93f4b91..0000000000 --- a/app/src/main/java/com/kickstarter/viewmodels/LoginViewModel.kt +++ /dev/null @@ -1,305 +0,0 @@ -package com.kickstarter.viewmodels - -import android.content.Intent -import android.util.Pair -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.kickstarter.libs.ActivityRequestCodes -import com.kickstarter.libs.Environment -import com.kickstarter.libs.rx.transformers.Transformers.combineLatestPair -import com.kickstarter.libs.rx.transformers.Transformers.ignoreValuesV2 -import com.kickstarter.libs.rx.transformers.Transformers.takeWhenV2 -import com.kickstarter.libs.utils.extensions.addToDisposable -import com.kickstarter.libs.utils.extensions.isNotNull -import com.kickstarter.libs.utils.extensions.negate -import com.kickstarter.models.User -import com.kickstarter.services.apiresponses.AccessTokenEnvelope -import com.kickstarter.services.apiresponses.ErrorEnvelope -import com.kickstarter.ui.IntentKey -import com.kickstarter.ui.data.ActivityResult -import com.kickstarter.ui.data.LoginReason -import com.kickstarter.viewmodels.usecases.LoginUseCase -import com.kickstarter.viewmodels.usecases.RefreshUserUseCase -import io.reactivex.Observable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.subjects.BehaviorSubject -import io.reactivex.subjects.PublishSubject - -interface LoginViewModel { - - interface Inputs { - /** Call when the email field changes. */ - fun email(email: String) - - /** Call when the log in button has been clicked. */ - fun loginClick() - - /** Call when the password field changes. */ - fun password(password: String) - - /** Call when the user cancels or dismisses the reset password success confirmation dialog. */ - fun resetPasswordConfirmationDialogDismissed() - - fun activityResult(result: ActivityResult) - } - - interface Outputs { - /** Emits a string to display when log in fails. */ - fun genericLoginError(): Observable - - /** Emits a string to display when log in fails, specifically for invalid credentials. */ - fun invalidLoginError(): Observable - - /** Emits a boolean that determines if the log in button is enabled. */ - fun isLoading(): Observable - - /** Finish the activity with a successful result. */ - fun loginSuccess(): Observable - - /** Fill the view's email address when it's supplied from the intent. */ - fun prefillEmail(): Observable - - /** Emits when a user has successfully changed their password and needs to login again. */ - fun showChangedPasswordSnackbar(): Observable - - /** Emits when a user has successfully created their password and needs to login again. */ - fun showCreatedPasswordSnackbar(): Observable - - /** Emits a boolean to determine whether reset password dialog should be shown. */ - fun showResetPasswordSuccessDialog(): Observable>> - - /** Start two factor activity for result. */ - fun tfaChallenge(): Observable - } - - class LoginViewModel( - private val environment: Environment, - private val intent: Intent - ) : ViewModel(), Inputs, Outputs { - - private val emailEditTextChanged = BehaviorSubject.create() - private val logInButtonClicked = BehaviorSubject.create() - private val passwordEditTextChanged = PublishSubject.create() - private val resetPasswordConfirmationDialogDismissed = PublishSubject.create() - private val activityResult = PublishSubject.create() - - private val emailAndReason = BehaviorSubject.create>() - private val genericLoginError: Observable - private val invalidloginError: Observable - private val isLoading = BehaviorSubject.create() - private val loginSuccess = PublishSubject.create() - private val prefillEmail = BehaviorSubject.create() - private val showChangedPasswordSnackbar = BehaviorSubject.create() - private val showCreatedPasswordSnackbar = BehaviorSubject.create() - private val showResetPasswordSuccessDialog = - BehaviorSubject.create>>() - private val tfaChallenge: Observable - - private val loginError = PublishSubject.create() - - val inputs: Inputs = this - val outputs: Outputs = this - - private val client = requireNotNull(environment.apiClientV2()) - private val analyticEvents = requireNotNull(environment.analytics()) - private val loginUserCase = LoginUseCase(environment) - private val refreshUserUseCase = RefreshUserUseCase(environment) - - private val internalIntent = BehaviorSubject.createDefault(this.intent) - - private val disposables = CompositeDisposable() - - init { - - val emailAndPassword = this.emailEditTextChanged - .compose>(combineLatestPair(this.passwordEditTextChanged)) - - internalIntent - .filter { it.hasExtra(IntentKey.EMAIL) } - .map { - extractFromIntent(it) - } - .subscribe { - emailAndReason.onNext(it) - } - .addToDisposable(disposables) - - // - Contain the errors if any from the login endpoint response - val errors = PublishSubject.create() - // - Contains success data if any from the login endpoint response - val successResponseData = PublishSubject.create() - - emailAndPassword - .compose(takeWhenV2(this.logInButtonClicked)) - .switchMap { ep -> this.client.login(ep.first, ep.second).materialize() } - .share() - .subscribe { - this.isLoading.onNext(false) - if (it.isOnError) { - it.error?.let { e -> errors.onNext(e) } - } else { - it.value?.let { v -> successResponseData.onNext(v) } - } - }.addToDisposable(disposables) - - logInButtonClicked - .subscribe { - this.isLoading.onNext(true) - this.analyticEvents.trackLogInButtonCtaClicked() - } - .addToDisposable(disposables) - - emailAndReason - .map { it.first } - .distinctUntilChanged() - .ofType(String::class.java) - .subscribe(this.prefillEmail) - - emailAndReason - .filter { it.second == LoginReason.RESET_PASSWORD } - .map { e -> Pair.create(true, e) } - .subscribe(this.showResetPasswordSuccessDialog) - - emailAndReason - .filter { it.second == LoginReason.RESET_FACEBOOK_PASSWORD } - .map { e -> Pair.create(true, e) } - .subscribe(this.showResetPasswordSuccessDialog) - - emailAndReason - .map { it.second } - .ofType(LoginReason::class.java) - .filter { LoginReason.CHANGE_PASSWORD == it } - .compose(ignoreValuesV2()) - .subscribe { - this.showChangedPasswordSnackbar.onNext(true) - }.addToDisposable(disposables) - - emailAndReason - .map { it.second } - .ofType(LoginReason::class.java) - .filter { LoginReason.CREATE_PASSWORD == it } - .compose(ignoreValuesV2()) - .subscribe { - this.showCreatedPasswordSnackbar.onNext(true) - }.addToDisposable(disposables) - - this.resetPasswordConfirmationDialogDismissed - .map { it.negate() } - .compose>>(combineLatestPair(emailAndReason)) - .map { Pair.create(it.first, it.second) } - .subscribe(this.showResetPasswordSuccessDialog) - - successResponseData - .distinctUntilChanged() - .switchMap { - this.loginUserCase - .loginAndUpdateUserPrivacy(it.user(), it.accessToken()) - } - .subscribe { user -> - this.success(user) - } - .addToDisposable(disposables) - - errors - .map { ErrorEnvelope.fromThrowable(it) } - .filter { it.isNotNull() } - .subscribe(this.loginError) - - this.genericLoginError = this.loginError - .filter { it.isGenericLoginError } - .map { it.errorMessage() } - - this.invalidloginError = this.loginError - .filter { it.isInvalidLoginError } - .map { it.errorMessage() } - - this.tfaChallenge = this.loginError - .filter { it.isTfaRequiredError } - .map { } - - this.analyticEvents.trackLoginPagedViewed() - - this.activityResult - .filter { it.isRequestCode(ActivityRequestCodes.RESET_FLOW) } - .filter(ActivityResult::isOk) - .subscribe { - it.intent?.let { intent -> - this.emailAndReason.onNext(extractFromIntent(intent)) - } - } - .addToDisposable(disposables) - } - - private fun extractFromIntent(it: Intent): Pair = - Pair.create( - it.getStringExtra(IntentKey.EMAIL) ?: "", - if (it.hasExtra(IntentKey.LOGIN_REASON)) { - (it.getSerializableExtra(IntentKey.LOGIN_REASON) as LoginReason?) - ?: LoginReason.DEFAULT - } else { - LoginReason.DEFAULT - } - ) - - private fun success(newUser: User) { - this.refreshUserUseCase.refresh(newUser) - this.loginSuccess.onNext(Unit) - } - - override fun onCleared() { - disposables.clear() - super.onCleared() - } - - fun hideChangePasswordSnackbar() { - showChangedPasswordSnackbar.onNext(false) - } - - fun hideCreatedPasswordSnackbar() { - showCreatedPasswordSnackbar.onNext(false) - } - - // - Inputs - override fun email(email: String) = this.emailEditTextChanged.onNext(email) - - override fun loginClick() { - return this.logInButtonClicked.onNext(Unit) - } - - override fun password(password: String) = this.passwordEditTextChanged.onNext(password) - - override fun resetPasswordConfirmationDialogDismissed() = - this.resetPasswordConfirmationDialogDismissed.onNext(true) - - override fun activityResult(result: ActivityResult) = this.activityResult.onNext(result) - - // - Outputs - override fun genericLoginError() = this.genericLoginError - - override fun invalidLoginError() = this.invalidloginError - - override fun isLoading(): BehaviorSubject = this.isLoading - - override fun loginSuccess(): PublishSubject = this.loginSuccess - - override fun prefillEmail(): BehaviorSubject = this.prefillEmail - - override fun showChangedPasswordSnackbar(): Observable = - this.showChangedPasswordSnackbar - - override fun showCreatedPasswordSnackbar(): Observable = - this.showCreatedPasswordSnackbar - - override fun showResetPasswordSuccessDialog(): BehaviorSubject>> = - this.showResetPasswordSuccessDialog - - override fun tfaChallenge() = this.tfaChallenge - } - - class Factory(private val environment: Environment, private val intent: Intent) : - ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - return LoginViewModel(environment, intent) as T - } - } -} diff --git a/app/src/main/java/com/kickstarter/viewmodels/SignupViewModel.kt b/app/src/main/java/com/kickstarter/viewmodels/SignupViewModel.kt deleted file mode 100644 index 9eceabfb1a..0000000000 --- a/app/src/main/java/com/kickstarter/viewmodels/SignupViewModel.kt +++ /dev/null @@ -1,189 +0,0 @@ -package com.kickstarter.viewmodels - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.kickstarter.libs.CurrentConfigTypeV2 -import com.kickstarter.libs.Environment -import com.kickstarter.libs.rx.transformers.Transformers -import com.kickstarter.libs.utils.extensions.addToDisposable -import com.kickstarter.libs.utils.extensions.isNotNull -import com.kickstarter.models.User -import com.kickstarter.services.ApiClientTypeV2 -import com.kickstarter.services.apiresponses.AccessTokenEnvelope -import com.kickstarter.services.apiresponses.ErrorEnvelope -import com.kickstarter.viewmodels.usecases.LoginUseCase -import com.kickstarter.viewmodels.usecases.RefreshUserUseCase -import io.reactivex.Observable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.subjects.BehaviorSubject -import io.reactivex.subjects.PublishSubject - -interface SignupViewModel { - interface Inputs { - /** Call when the email field changes. */ - fun email(email: String) - - /** Call when the name field changes. */ - fun name(name: String) - - /** Call when the password field changes. */ - fun password(password: String) - - /** Call when the send newsletter toggle changes. */ - fun sendNewsletters(send: Boolean) - - /** Call when the signup button has been clicked. */ - fun signupClick() - } - - interface Outputs { - /** Emits a string to display when signup fails. */ - fun errorString(): Observable - - /** Emits a boolean that determines if the sign up button is disabled. */ - fun formSubmitting(): Observable - - /** Finish the activity with a successful result. */ - fun signupSuccess(): Observable - - fun progressBarIsVisible(): Observable - } - - class SignupViewModel(environment: Environment) : - ViewModel(), - Inputs, - Outputs { - private val client: ApiClientTypeV2 - private val analyticEvents = requireNotNull(environment.analytics()) - private val currentConfig: CurrentConfigTypeV2 - private val loginUserCase = LoginUseCase(environment) - private val refreshUserUseCase = RefreshUserUseCase(environment) - private val disposables = CompositeDisposable() - - private fun submit(data: SignupData): Observable { - return client.signup( - data.name, - data.email, - data.password, - data.password, - data.sendNewsletters - ) - .compose(Transformers.pipeApiErrorsToV2(signupError)) - .compose(Transformers.neverErrorV2()) - .doOnSubscribe { - this.progressBarIsVisible.onNext(true) - formSubmitting.onNext(true) - } - .doAfterTerminate { - this.progressBarIsVisible.onNext(false) - formSubmitting.onNext(false) - } - } - - private fun success(user: User) { - refreshUserUseCase.refresh(user) - signupSuccess.onNext(Unit) - } - - private val email = PublishSubject.create() - private val name = PublishSubject.create() - private val password = PublishSubject.create() - private val sendNewsletters = PublishSubject.create() - private val signupClick = PublishSubject.create() - private val errorString: Observable - private val signupSuccess = PublishSubject.create() - private val formSubmitting = BehaviorSubject.create() - private val signupError = PublishSubject.create() - private val progressBarIsVisible = BehaviorSubject.create() - - val inputs: Inputs = this - val outputs: Outputs = this - - init { - client = requireNotNull(environment.apiClientV2()) - currentConfig = requireNotNull(environment.currentConfigV2()) - - val signupData = Observable.combineLatest( - name, - email, - password, - sendNewsletters - ) { name: String, email: String, password: String, sendNewsletters: Boolean -> - SignupData( - name, - email, - password, - sendNewsletters - ) - } - - signupData - .compose(Transformers.takeWhenV2(signupClick)) - .switchMap { - submit(it) - } - .distinctUntilChanged() - .switchMap { - this.loginUserCase - .loginAndUpdateUserPrivacy(it.user(), it.accessToken()) - } - .subscribe { success(it) } - .addToDisposable(disposables) - - errorString = signupError - .takeUntil(signupSuccess) - .filter { it.isNotNull() } - .map { it.errorMessage() } - - signupClick - .subscribe { analyticEvents.trackSignUpSubmitCtaClicked() } - .addToDisposable(disposables) - - analyticEvents.trackSignUpPageViewed() - } - - override fun email(email: String) { - this.email.onNext(email) - } - - override fun name(name: String) { - this.name.onNext(name) - } - - override fun password(password: String) { - this.password.onNext(password) - } - - override fun sendNewsletters(send: Boolean) { - sendNewsletters.onNext(send) - } - - override fun signupClick() { - signupClick.onNext(Unit) - } - - override fun errorString(): Observable = errorString - override fun formSubmitting(): BehaviorSubject = formSubmitting - override fun signupSuccess(): PublishSubject = signupSuccess - - override fun progressBarIsVisible(): Observable = this.progressBarIsVisible - - override fun onCleared() { - disposables.clear() - super.onCleared() - } - - internal class SignupData( - val name: String, - val email: String, - val password: String, - val sendNewsletters: Boolean - ) - } - - class Factory(private val environment: Environment) : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - return SignupViewModel(environment) as T - } - } -} diff --git a/app/src/main/java/com/kickstarter/viewmodels/TwoFactorViewModel.kt b/app/src/main/java/com/kickstarter/viewmodels/TwoFactorViewModel.kt deleted file mode 100644 index fc6f6de519..0000000000 --- a/app/src/main/java/com/kickstarter/viewmodels/TwoFactorViewModel.kt +++ /dev/null @@ -1,269 +0,0 @@ -package com.kickstarter.viewmodels - -import android.content.Intent -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.kickstarter.libs.Environment -import com.kickstarter.libs.rx.transformers.Transformers -import com.kickstarter.libs.utils.extensions.addToDisposable -import com.kickstarter.models.User -import com.kickstarter.services.ApiClientTypeV2 -import com.kickstarter.services.apiresponses.AccessTokenEnvelope -import com.kickstarter.services.apiresponses.ErrorEnvelope -import com.kickstarter.ui.IntentKey -import com.kickstarter.viewmodels.usecases.LoginUseCase -import com.kickstarter.viewmodels.usecases.RefreshUserUseCase -import io.reactivex.Observable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.subjects.BehaviorSubject -import io.reactivex.subjects.PublishSubject - -interface TwoFactorViewModel { - interface Inputs { - /** Call when the 2FA code has been submitted. */ - fun code(code: String) - - /** Call when the log in button has been clicked. */ - fun loginClick() - - /** Call when the resend button has been clicked. */ - fun resendClick() - } - - interface Outputs { - /** Emits when submitting TFA code errored for an unknown reason. */ - fun genericTfaError(): Observable - - /** Emits when TFA code was submitted. */ - fun formSubmitting(): Observable - - /** Emits when resend code confirmation should be shown. */ - fun showResendCodeConfirmation(): Observable - - /** Emits when a submitted TFA code does not match. */ - fun tfaCodeMismatchError(): Observable - - /** Emits when submitting TFA code was successful. */ - fun tfaSuccess(): Observable - } - - class TwoFactorViewModel( - environment: Environment, - intent: Intent - ) : ViewModel(), Inputs, Outputs { - private val client: ApiClientTypeV2 = requireNotNull(environment.apiClientV2()) - private val analytics = requireNotNull(environment.analytics()) - private val loginUserCase = LoginUseCase(environment) - private val refreshUserUseCase = RefreshUserUseCase(environment) - private val internalIntent = BehaviorSubject.createDefault(intent) - private val disposables = CompositeDisposable() - - private fun success(user: User) { - refreshUserUseCase.refresh(user) - tfaSuccess.onNext(Unit) - } - - private fun login( - code: String, - email: String, - password: String - ): Observable { - return client.login(email, password, code) - .compose(Transformers.pipeApiErrorsToV2(tfaError)) - .compose(Transformers.neverErrorV2()) - .doOnSubscribe { formSubmitting.onNext(true) } - .doAfterTerminate { formSubmitting.onNext(false) } - } - - private fun loginWithFacebook( - code: String, - fbAccessToken: String - ): Observable { - return client.loginWithFacebook(fbAccessToken, code) - .compose(Transformers.pipeApiErrorsToV2(tfaError)) - .compose(Transformers.neverErrorV2()) - .doOnSubscribe { formSubmitting.onNext(true) } - .doAfterTerminate { formSubmitting.onNext(false) } - } - - private fun resendCode(email: String, password: String): Observable { - return client.login(email, password) - .compose(Transformers.neverErrorV2()) - .doOnSubscribe { showResendCodeConfirmation.onNext(Unit) } - } - - private fun resendCodeWithFacebook(fbAccessToken: String): Observable { - return client.loginWithFacebook(fbAccessToken) - .compose(Transformers.neverErrorV2()) - .doOnSubscribe { showResendCodeConfirmation.onNext(Unit) } - } - - private val code = PublishSubject.create() - private val loginClick = PublishSubject.create() - private val resendClick = PublishSubject.create() - private val formIsValid = PublishSubject.create() - private val formSubmitting = PublishSubject.create() - private val showResendCodeConfirmation = PublishSubject.create() - private val tfaError = PublishSubject.create() - private val tfaSuccess = PublishSubject.create() - - val inputs: Inputs = this - val outputs: Outputs = this - - override fun formSubmitting(): Observable = formSubmitting - - override fun genericTfaError(): Observable { - return tfaError - .filter { env: ErrorEnvelope -> !env.isTfaFailedError } - .map { Unit } - } - - override fun showResendCodeConfirmation(): Observable = showResendCodeConfirmation - - override fun tfaCodeMismatchError(): Observable { - return tfaError - .filter(ErrorEnvelope::isTfaFailedError) - .map { Unit } - } - - override fun tfaSuccess(): Observable { - return tfaSuccess - } - - override fun code(s: String) { - this.code.onNext(s) - } - - override fun loginClick() { - loginClick.onNext(Unit) - } - - override fun resendClick() { - resendClick.onNext(Unit) - } - - protected inner class TfaData( - val email: String, - val isFacebookLogin: Boolean, - val password: String - ) - - protected inner class TfaDataForFacebook( - val fbAccessToken: String, - val isFacebookLogin: Boolean - ) - companion object { - private fun isCodeValid(code: String?): Boolean { - return !code.isNullOrEmpty() - } - } - - init { - - val email = internalIntent - .map { it.getStringExtra(IntentKey.EMAIL) ?: "" } - - val fbAccessToken = internalIntent - .map { it.getStringExtra(IntentKey.FACEBOOK_TOKEN) ?: "" } - - val isFacebookLogin = internalIntent - .map { it.getBooleanExtra(IntentKey.FACEBOOK_LOGIN, false) } - - val password = internalIntent - .map { it.getStringExtra(IntentKey.PASSWORD) ?: "" } - - val tfaData = Observable.combineLatest( - email, - isFacebookLogin, - password - ) { email: String, isFacebookLogin: Boolean, password: String -> - TfaData( - email, - isFacebookLogin, - password - ) - } - - val tfaFacebookData = Observable.combineLatest( - fbAccessToken, - isFacebookLogin - ) { fbAccessToken: String?, isFacebookLogin: Boolean -> - TfaDataForFacebook( - fbAccessToken = fbAccessToken ?: "", - isFacebookLogin = isFacebookLogin - ) - } - - this.code - .map { isCodeValid(it) } - .subscribe { formIsValid.onNext(it) } - .addToDisposable(disposables) - - this.code - .compose(Transformers.combineLatestPair(tfaData)) - .compose(Transformers.takeWhenV2(loginClick)) - .filter { !it.second.isFacebookLogin } - .switchMap { - login( - it.first, - it.second.email, - it.second.password - ) - } - .switchMap { - this.loginUserCase - .loginAndUpdateUserPrivacy(it.user(), it.accessToken()) - } - .subscribe { success(it) } - .addToDisposable(disposables) - - this.code - .compose(Transformers.combineLatestPair(tfaFacebookData)) - .compose(Transformers.takeWhenV2(loginClick)) - .filter { it.second.isFacebookLogin } - .switchMap { - loginWithFacebook( - it.first, - it.second.fbAccessToken - ) - } - .switchMap { - this.loginUserCase - .loginAndUpdateUserPrivacy(it.user(), it.accessToken()) - } - .subscribe { success(it) } - .addToDisposable(disposables) - - tfaData - .compose(Transformers.takeWhenV2(resendClick)) - .filter { !it.isFacebookLogin } - .flatMap { - resendCode(it.email, it.password) - } - .subscribe() - .addToDisposable(disposables) - - tfaFacebookData - .compose(Transformers.takeWhenV2(resendClick)) - .filter { it.isFacebookLogin } - .flatMap { - resendCodeWithFacebook(it.fbAccessToken) - } - .subscribe() - .addToDisposable(disposables) - - analytics.trackTwoFactorAuthPageViewed() - } - - override fun onCleared() { - disposables.clear() - super.onCleared() - } - } - - class Factory(private val environment: Environment, private val intent: Intent) : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - return TwoFactorViewModel(environment, intent) as T - } - } -} diff --git a/app/src/test/java/com/kickstarter/libs/utils/extensions/IntentExtTest.kt b/app/src/test/java/com/kickstarter/libs/utils/extensions/IntentExtTest.kt index b312ae3389..c9f6d13a18 100644 --- a/app/src/test/java/com/kickstarter/libs/utils/extensions/IntentExtTest.kt +++ b/app/src/test/java/com/kickstarter/libs/utils/extensions/IntentExtTest.kt @@ -113,16 +113,16 @@ class IntentExtTest : KSRobolectricTestCase() { @Test fun testLoginActivityIntent() { val intent = Intent().getLoginActivityIntent(context()) - assertEquals(intent.component?.className, "com.kickstarter.ui.activities.LoginActivity") + assertEquals(intent.component?.className, "com.kickstarter.ui.activities.LoginToutActivity") assertEquals(intent.extras?.get(IntentKey.EMAIL), null) val intent1 = Intent().getLoginActivityIntent(context(), loginReason = LoginReason.RESET_PASSWORD) - assertEquals(intent1.component?.className, "com.kickstarter.ui.activities.LoginActivity") + assertEquals(intent1.component?.className, "com.kickstarter.ui.activities.LoginToutActivity") assertEquals(intent1.extras?.get(IntentKey.EMAIL), null) assertEquals(intent1.extras?.get(IntentKey.LOGIN_REASON), LoginReason.RESET_PASSWORD) val intent2 = Intent().getLoginActivityIntent(context(), email = "test@kickstarter.com") - assertEquals(intent2.component?.className, "com.kickstarter.ui.activities.LoginActivity") + assertEquals(intent2.component?.className, "com.kickstarter.ui.activities.LoginToutActivity") assertEquals(intent2.extras?.get(IntentKey.EMAIL), "test@kickstarter.com") assertEquals(intent2.extras?.get(IntentKey.LOGIN_REASON), null) } diff --git a/app/src/test/java/com/kickstarter/ui/activities/compose/LoginScreenTest.kt b/app/src/test/java/com/kickstarter/ui/activities/compose/LoginScreenTest.kt deleted file mode 100644 index 40e5d40267..0000000000 --- a/app/src/test/java/com/kickstarter/ui/activities/compose/LoginScreenTest.kt +++ /dev/null @@ -1,222 +0,0 @@ -package com.kickstarter.ui.activities.compose - -import androidx.compose.material.rememberScaffoldState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertIsEnabled -import androidx.compose.ui.test.assertIsNotEnabled -import androidx.compose.ui.test.assertTextEquals -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.performClick -import androidx.compose.ui.test.performTextClearance -import androidx.compose.ui.test.performTextInput -import androidx.test.platform.app.InstrumentationRegistry -import com.kickstarter.KSRobolectricTestCase -import com.kickstarter.R -import com.kickstarter.ui.activities.compose.login.LoginDropdownTestTag -import com.kickstarter.ui.activities.compose.login.LoginScreen -import com.kickstarter.ui.activities.compose.login.LoginTestTag -import com.kickstarter.ui.compose.designsystem.KSTheme -import org.junit.Test - -class LoginScreenTest : KSRobolectricTestCase() { - - val context = InstrumentationRegistry.getInstrumentation().targetContext - - private val optionsMenu = composeTestRule.onNodeWithTag(LoginDropdownTestTag.OPTIONS_MENU.name) - private val optionsTerms = - composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_TERMS.name, - useUnmergedTree = true - ) - private val optionsPrivacyPolicy = composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_PRIVACY_POLICY.name, - useUnmergedTree = true - ) - private val optionsCookie = - composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_COOKIE.name, - useUnmergedTree = true - ) - private val optionsHelp = - composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_HELP.name, - useUnmergedTree = true - ) - private val pageTitle = composeTestRule.onNodeWithTag(LoginTestTag.PAGE_TITLE.name) - private val backButton = composeTestRule.onNodeWithTag(LoginTestTag.BACK_BUTTON.name) - private val optionsIcon = composeTestRule.onNodeWithTag(LoginTestTag.OPTIONS_ICON.name) - private val loading = composeTestRule.onNodeWithTag(LoginTestTag.LOADING.name) - private val email = composeTestRule.onNodeWithTag(LoginTestTag.EMAIL.name) - private val password = composeTestRule.onNodeWithTag(LoginTestTag.PASSWORD.name) - private val loginButton = composeTestRule.onNodeWithTag(LoginTestTag.LOGIN_BUTTON.name) - private val forgotPasswordText = - composeTestRule.onNodeWithTag(LoginTestTag.FORGOT_PASSWORD_TEXT.name) - - @Test - fun testComponentsVisible() { - composeTestRule.setContent { - KSTheme { - LoginScreen( - scaffoldState = rememberScaffoldState(), - isLoading = false, - onBackClicked = {}, - onLoginClicked = { _, _ -> }, - onTermsOfUseClicked = {}, - onPrivacyPolicyClicked = {}, - onCookiePolicyClicked = {}, - onHelpClicked = {}, - onForgotPasswordClicked = {}, - resetPasswordDialogMessage = "", - showDialog = false, - setShowDialog = {} - ) - } - } - - pageTitle.assertIsDisplayed() - val titleText = context.getString(R.string.login_navbar_title) - pageTitle.assertTextEquals(titleText) - - backButton.assertIsDisplayed() - optionsIcon.assertIsDisplayed() - optionsMenu.assertDoesNotExist() - loading.assertDoesNotExist() - email.assertIsDisplayed() - password.assertIsDisplayed() - loginButton.assertIsDisplayed() - loginButton.assertIsNotEnabled() - - forgotPasswordText.assertIsDisplayed() - val forgotPasswordString = context.getString(R.string.forgot_password_title) - forgotPasswordText.assertTextEquals(forgotPasswordString) - } - - @Test - fun testLoginButtonEnable() { - composeTestRule.setContent { - KSTheme { - LoginScreen( - scaffoldState = rememberScaffoldState(), - isLoading = false, - onBackClicked = {}, - onLoginClicked = { _, _ -> }, - onTermsOfUseClicked = {}, - onPrivacyPolicyClicked = {}, - onCookiePolicyClicked = {}, - onHelpClicked = {}, - onForgotPasswordClicked = {}, - resetPasswordDialogMessage = "", - showDialog = false, - setShowDialog = {} - ) - } - } - - loginButton.assertIsNotEnabled() - - email.performTextInput("notAvalidEmail") - password.performTextInput("12345") - - loginButton.assertIsNotEnabled() - email.performTextClearance() - password.performTextClearance() - - email.performTextInput("valid@email.com") - password.performTextInput("123456") - - loginButton.assertIsEnabled() - } - - @Test - fun testClickableObjects() { - var termsClickedCount = 0 - var privacyClickedCount = 0 - var cookieClickedCount = 0 - var helpClickedCount = 0 - var backClickedCount = 0 - var forgotPasswordClickedCount = 0 - composeTestRule.setContent { - KSTheme { - LoginScreen( - scaffoldState = rememberScaffoldState(), - isLoading = false, - onBackClicked = { backClickedCount++ }, - onLoginClicked = { _, _ -> }, - onTermsOfUseClicked = { termsClickedCount++ }, - onPrivacyPolicyClicked = { privacyClickedCount++ }, - onCookiePolicyClicked = { cookieClickedCount++ }, - onHelpClicked = { helpClickedCount++ }, - onForgotPasswordClicked = { forgotPasswordClickedCount++ }, - resetPasswordDialogMessage = "", - showDialog = false, - setShowDialog = { } - ) - } - } - - backButton.performClick() - assertEquals(backClickedCount, 1) - - optionsIcon.performClick() - optionsMenu.assertIsDisplayed() - optionsTerms.assertIsDisplayed() - optionsPrivacyPolicy.assertIsDisplayed() - optionsCookie.assertIsDisplayed() - optionsHelp.assertIsDisplayed() - - optionsTerms.performClick() - assertEquals(termsClickedCount, 1) - optionsMenu.assertDoesNotExist() - - optionsIcon.performClick() - optionsPrivacyPolicy.performClick() - assertEquals(privacyClickedCount, 1) - optionsMenu.assertDoesNotExist() - - optionsIcon.performClick() - optionsCookie.performClick() - assertEquals(cookieClickedCount, 1) - optionsMenu.assertDoesNotExist() - - optionsIcon.performClick() - optionsHelp.performClick() - assertEquals(helpClickedCount, 1) - optionsMenu.assertDoesNotExist() - - forgotPasswordText.performClick() - assertEquals(forgotPasswordClickedCount, 1) - } - - @Test - fun testLoadingDisplay() { - composeTestRule.setContent { - KSTheme { - var isloading by remember { mutableStateOf(false) } - LoginScreen( - scaffoldState = rememberScaffoldState(), - isLoading = isloading, - onBackClicked = { isloading = !isloading }, - onLoginClicked = { _, _ -> }, - onTermsOfUseClicked = {}, - onPrivacyPolicyClicked = {}, - onCookiePolicyClicked = {}, - onHelpClicked = {}, - onForgotPasswordClicked = {}, - resetPasswordDialogMessage = "", - showDialog = false, - setShowDialog = {} - ) - } - } - - loading.assertDoesNotExist() - backButton.performClick() - loading.assertIsDisplayed() - backButton.performClick() - loading.assertDoesNotExist() - } -} diff --git a/app/src/test/java/com/kickstarter/ui/activities/compose/LoginToutScreenTest.kt b/app/src/test/java/com/kickstarter/ui/activities/compose/LoginToutScreenTest.kt index 4aeba3deab..f3f44fd15f 100644 --- a/app/src/test/java/com/kickstarter/ui/activities/compose/LoginToutScreenTest.kt +++ b/app/src/test/java/com/kickstarter/ui/activities/compose/LoginToutScreenTest.kt @@ -38,10 +38,6 @@ class LoginToutScreenTest : KSRobolectricTestCase() { composeTestRule.onNodeWithTag(LoginToutTestTag.FACEBOOK_BUTTON.name) private val facebookDisclaimer = composeTestRule.onNodeWithTag(LoginToutTestTag.FACEBOOK_DISCLAIMER.name) - private val emailLogInButton = - composeTestRule.onNodeWithTag(LoginToutTestTag.EMAIL_LOG_IN_BUTTON.name) - private val emailSignUpButton = - composeTestRule.onNodeWithTag(LoginToutTestTag.EMAIL_SIGN_UP_BUTTON.name) private val touPpCookieDisclaimer = composeTestRule.onNodeWithTag(LoginToutTestTag.TOU_PP_COOKIE_DISCLAIMER.name) private val signUpOrLogInButton = @@ -54,13 +50,10 @@ class LoginToutScreenTest : KSRobolectricTestCase() { LoginToutScreen( onBackClicked = { }, onFacebookButtonClicked = { }, - onEmailLoginClicked = { }, - onEmailSignupClicked = { }, onTermsOfUseClicked = { }, onPrivacyPolicyClicked = { }, onCookiePolicyClicked = { }, onHelpClicked = { }, - featureFlagState = false, {} ) } @@ -93,8 +86,6 @@ class LoginToutScreenTest : KSRobolectricTestCase() { logoTitle.assertIsDisplayed() facebookButton.assertIsDisplayed() facebookDisclaimer.assertIsDisplayed() - emailLogInButton.assertIsDisplayed() - emailSignUpButton.assertIsDisplayed() touPpCookieDisclaimer.assertIsDisplayed() } @@ -104,13 +95,10 @@ class LoginToutScreenTest : KSRobolectricTestCase() { LoginToutScreen( onBackClicked = { }, onFacebookButtonClicked = { }, - onEmailLoginClicked = { }, - onEmailSignupClicked = { }, onTermsOfUseClicked = { }, onPrivacyPolicyClicked = { }, onCookiePolicyClicked = { }, onHelpClicked = { }, - featureFlagState = true, {} ) } @@ -143,8 +131,6 @@ class LoginToutScreenTest : KSRobolectricTestCase() { logoTitle.assertIsDisplayed() facebookButton.assertIsDisplayed() facebookDisclaimer.assertIsDisplayed() - emailLogInButton.assertDoesNotExist() - emailSignUpButton.assertDoesNotExist() touPpCookieDisclaimer.assertIsDisplayed() signUpOrLogInButton.assertIsDisplayed() } @@ -156,13 +142,10 @@ class LoginToutScreenTest : KSRobolectricTestCase() { LoginToutScreen( onBackClicked = { }, onFacebookButtonClicked = { }, - onEmailLoginClicked = { }, - onEmailSignupClicked = { }, onTermsOfUseClicked = { }, onPrivacyPolicyClicked = { }, onCookiePolicyClicked = { }, onHelpClicked = { }, - featureFlagState = false, {} ) } @@ -194,11 +177,9 @@ class LoginToutScreenTest : KSRobolectricTestCase() { } @Test - fun testClickActionsWork_featureFlag_Off() { + fun testClickActionsWork() { var backClickedCount = 0 var facebookClickedCount = 0 - var emailLoginClickedCount = 0 - var emailSignUpClickedCount = 0 var termsOfUseClickedCount = 0 var privacyPolicyClickedCount = 0 var cookiePolicyClickedCount = 0 @@ -210,13 +191,10 @@ class LoginToutScreenTest : KSRobolectricTestCase() { LoginToutScreen( onBackClicked = { backClickedCount++ }, onFacebookButtonClicked = { facebookClickedCount++ }, - onEmailLoginClicked = { emailLoginClickedCount++ }, - onEmailSignupClicked = { emailSignUpClickedCount++ }, onTermsOfUseClicked = { termsOfUseClickedCount++ }, onPrivacyPolicyClicked = { privacyPolicyClickedCount++ }, onCookiePolicyClicked = { cookiePolicyClickedCount++ }, onHelpClicked = { helpClickedCount++ }, - featureFlagState = false, onSignUpOrLogInClicked = { signUpOrLogInClicked++ } ) } @@ -228,75 +206,6 @@ class LoginToutScreenTest : KSRobolectricTestCase() { facebookButton.performClick() assertEquals(facebookClickedCount, 1) - emailLogInButton.performClick() - assertEquals(emailLoginClickedCount, 1) - - emailSignUpButton.performClick() - assertEquals(emailSignUpClickedCount, 1) - - signUpOrLogInButton.assertDoesNotExist() - assertEquals(signUpOrLogInClicked, 0) - - optionsIcon.performClick() - optionsTerms.performClick() - assertEquals(termsOfUseClickedCount, 1) - - optionsIcon.performClick() - optionsPrivacyPolicy.performClick() - assertEquals(privacyPolicyClickedCount, 1) - - optionsIcon.performClick() - optionsCookie.performClick() - assertEquals(cookiePolicyClickedCount, 1) - - optionsIcon.performClick() - optionsHelp.performClick() - assertEquals(helpClickedCount, 1) - - // TODO find a way to click hyperlinks in touPpCookieDisclaimer text reliably to test - } - - @Test - fun testClickActionsWork_featureFlag_On() { - var backClickedCount = 0 - var facebookClickedCount = 0 - var emailLoginClickedCount = 0 - var emailSignUpClickedCount = 0 - var termsOfUseClickedCount = 0 - var privacyPolicyClickedCount = 0 - var cookiePolicyClickedCount = 0 - var helpClickedCount = 0 - var signUpOrLogInClicked = 0 - - composeTestRule.setContent { - KSTheme { - LoginToutScreen( - onBackClicked = { backClickedCount++ }, - onFacebookButtonClicked = { facebookClickedCount++ }, - onEmailLoginClicked = { emailLoginClickedCount++ }, - onEmailSignupClicked = { emailSignUpClickedCount++ }, - onTermsOfUseClicked = { termsOfUseClickedCount++ }, - onPrivacyPolicyClicked = { privacyPolicyClickedCount++ }, - onCookiePolicyClicked = { cookiePolicyClickedCount++ }, - onHelpClicked = { helpClickedCount++ }, - featureFlagState = true, - onSignUpOrLogInClicked = { signUpOrLogInClicked++ } - ) - } - } - - backButton.performClick() - assertEquals(backClickedCount, 1) - - facebookButton.performClick() - assertEquals(facebookClickedCount, 1) - - emailLogInButton.assertDoesNotExist() - assertEquals(emailLoginClickedCount, 0) - - emailSignUpButton.assertDoesNotExist() - assertEquals(emailSignUpClickedCount, 0) - signUpOrLogInButton.performClick() assertEquals(signUpOrLogInClicked, 1) diff --git a/app/src/test/java/com/kickstarter/ui/activities/compose/TwoFactorScreenTest.kt b/app/src/test/java/com/kickstarter/ui/activities/compose/TwoFactorScreenTest.kt deleted file mode 100644 index 9f047aff7e..0000000000 --- a/app/src/test/java/com/kickstarter/ui/activities/compose/TwoFactorScreenTest.kt +++ /dev/null @@ -1,206 +0,0 @@ -package com.kickstarter.ui.activities.compose - -import androidx.compose.material.rememberScaffoldState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertIsEnabled -import androidx.compose.ui.test.assertIsNotEnabled -import androidx.compose.ui.test.assertTextEquals -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.performClick -import androidx.compose.ui.test.performTextClearance -import androidx.compose.ui.test.performTextInput -import androidx.test.platform.app.InstrumentationRegistry -import com.kickstarter.KSRobolectricTestCase -import com.kickstarter.R -import com.kickstarter.ui.activities.compose.login.LoginDropdownTestTag -import com.kickstarter.ui.activities.compose.login.TwoFactorScreen -import com.kickstarter.ui.activities.compose.login.TwoFactorScreenTestTag -import com.kickstarter.ui.compose.designsystem.KSTheme -import org.junit.Test - -class TwoFactorScreenTest : KSRobolectricTestCase() { - - val context = InstrumentationRegistry.getInstrumentation().targetContext - - private val backButton = composeTestRule.onNodeWithTag(TwoFactorScreenTestTag.BACK_BUTTON.name) - private val pageTitle = - composeTestRule.onNodeWithTag(TwoFactorScreenTestTag.PAGE_TITLE.name) - private val optionsIcon = - composeTestRule.onNodeWithTag( - TwoFactorScreenTestTag.OPTIONS_ICON.name, - ) - - private val optionsMenu = composeTestRule.onNodeWithTag(LoginDropdownTestTag.OPTIONS_MENU.name) - private val optionsTerms = - composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_TERMS.name, - useUnmergedTree = true - ) - private val optionsPrivacyPolicy = composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_PRIVACY_POLICY.name, - useUnmergedTree = true - ) - private val optionsCookie = - composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_COOKIE.name, - useUnmergedTree = true - ) - private val optionsHelp = - composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_HELP.name, - useUnmergedTree = true - ) - - private val loading = composeTestRule.onNodeWithTag(TwoFactorScreenTestTag.LOADING.name) - private val headline = composeTestRule.onNodeWithTag(TwoFactorScreenTestTag.HEADLINE.name) - private val code = composeTestRule.onNodeWithTag(TwoFactorScreenTestTag.CODE.name) - private val resend = composeTestRule.onNodeWithTag(TwoFactorScreenTestTag.RESEND.name) - private val submit = composeTestRule.onNodeWithTag(TwoFactorScreenTestTag.SUBMIT.name) - - @Test - fun testComponentsVisible() { - composeTestRule.setContent { - KSTheme { - TwoFactorScreen( - scaffoldState = rememberScaffoldState(), - onBackClicked = {}, - onTermsOfUseClicked = {}, - onPrivacyPolicyClicked = {}, - onCookiePolicyClicked = {}, - onHelpClicked = {}, - onResendClicked = {}, - onSubmitClicked = {}, - isLoading = false - ) - } - } - - backButton.assertIsDisplayed() - - val titleText = context.getString(R.string.two_factor_title) - pageTitle.assertIsDisplayed() - pageTitle.assertTextEquals(titleText) - - optionsIcon.assertIsDisplayed() - optionsMenu.assertDoesNotExist() - loading.assertDoesNotExist() - - val headlineText = context.getString(R.string.two_factor_message) - headline.assertIsDisplayed() - headline.assertTextEquals(headlineText) - - code.assertIsDisplayed() - resend.assertIsDisplayed() - submit.assertIsDisplayed() - submit.assertIsNotEnabled() - } - - @Test - fun testSubmitEnabledCondition() { - composeTestRule.setContent { - KSTheme { - TwoFactorScreen( - scaffoldState = rememberScaffoldState(), - onBackClicked = {}, - onTermsOfUseClicked = {}, - onPrivacyPolicyClicked = {}, - onCookiePolicyClicked = {}, - onHelpClicked = {}, - onResendClicked = {}, - onSubmitClicked = {}, - isLoading = false - ) - } - } - - submit.assertIsNotEnabled() - code.performTextInput("testing") - submit.assertIsEnabled() - code.performTextClearance() - submit.assertIsNotEnabled() - } - - @Test - fun testClickActions() { - var backClickedCount = 0 - var termsClickedCount = 0 - var privacyClickedCount = 0 - var cookieClickedCount = 0 - var helpClickedCount = 0 - var resendClickedCount = 0 - var submitClickedCount = 0 - composeTestRule.setContent { - KSTheme { - TwoFactorScreen( - scaffoldState = rememberScaffoldState(), - onBackClicked = { backClickedCount++ }, - onTermsOfUseClicked = { termsClickedCount++ }, - onPrivacyPolicyClicked = { privacyClickedCount++ }, - onCookiePolicyClicked = { cookieClickedCount++ }, - onHelpClicked = { helpClickedCount++ }, - onResendClicked = { resendClickedCount++ }, - onSubmitClicked = { code -> submitClickedCount++ }, - isLoading = false - ) - } - } - - backButton.performClick() - assertEquals(backClickedCount, 1) - - optionsIcon.performClick() - optionsMenu.assertIsDisplayed() - optionsTerms.performClick() - optionsMenu.assertDoesNotExist() - assertEquals(termsClickedCount, 1) - - optionsIcon.performClick() - optionsPrivacyPolicy.performClick() - assertEquals(privacyClickedCount, 1) - - optionsIcon.performClick() - optionsCookie.performClick() - assertEquals(cookieClickedCount, 1) - - optionsIcon.performClick() - optionsHelp.performClick() - assertEquals(helpClickedCount, 1) - - resend.performClick() - assertEquals(resendClickedCount, 1) - - code.performTextInput("anything") - submit.performClick() - assertEquals(submitClickedCount, 1) - } - - @Test - fun testLoading() { - composeTestRule.setContent { - var showLoading by remember { mutableStateOf(false) } - KSTheme { - TwoFactorScreen( - scaffoldState = rememberScaffoldState(), - onBackClicked = { showLoading = !showLoading }, - onTermsOfUseClicked = {}, - onPrivacyPolicyClicked = {}, - onCookiePolicyClicked = {}, - onHelpClicked = {}, - onResendClicked = {}, - onSubmitClicked = {}, - isLoading = showLoading - ) - } - } - - loading.assertDoesNotExist() - backButton.performClick() - loading.assertIsDisplayed() - backButton.performClick() - loading.assertDoesNotExist() - } -} diff --git a/app/src/test/java/com/kickstarter/ui/activities/compose/login/SignupScreenKtTest.kt b/app/src/test/java/com/kickstarter/ui/activities/compose/login/SignupScreenKtTest.kt deleted file mode 100644 index d20735ebb6..0000000000 --- a/app/src/test/java/com/kickstarter/ui/activities/compose/login/SignupScreenKtTest.kt +++ /dev/null @@ -1,303 +0,0 @@ -package com.kickstarter.ui.activities.compose.login - -import androidx.compose.material.rememberScaffoldState -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertIsEnabled -import androidx.compose.ui.test.assertIsNotEnabled -import androidx.compose.ui.test.assertIsToggleable -import androidx.compose.ui.test.assertTextEquals -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.performClick -import androidx.compose.ui.test.performTextInput -import androidx.compose.ui.test.performTouchInput -import androidx.compose.ui.test.swipeLeft -import androidx.compose.ui.test.swipeRight -import androidx.test.platform.app.InstrumentationRegistry -import com.kickstarter.KSRobolectricTestCase -import com.kickstarter.R -import com.kickstarter.ui.compose.designsystem.KSTheme -import org.junit.Test - -class SignupScreenKtTest : KSRobolectricTestCase() { - val context = InstrumentationRegistry.getInstrumentation().targetContext - - private val backButton = - composeTestRule.onNodeWithTag(SignupScreenTestTag.BACK_BUTTON.name) - private val pageTitle = - composeTestRule.onNodeWithTag(SignupScreenTestTag.PAGE_TITLE.name) - private val signupButton = - composeTestRule.onNodeWithTag(SignupScreenTestTag.SIGNUP_BUTTON.name) - - private val progressBar = - composeTestRule.onNodeWithTag(SignupScreenTestTag.PROGRESS_BAR.name) - private val newsletterOptInText = - composeTestRule.onNodeWithTag(SignupScreenTestTag.NEWSLETTER_OPT_IN_TEXT.name) - private val newsletterOptInSwitch = - composeTestRule.onNodeWithTag(SignupScreenTestTag.NEWSLETTER_OPT_IN_SWITCH.name) - private val nameEditText = - composeTestRule.onNodeWithTag(SignupScreenTestTag.NAME_EDIT_TEXT.name) - private val emailEditText = - composeTestRule.onNodeWithTag(SignupScreenTestTag.EMAIL_EDIT_TEXT.name) - private val passwordEditText = - composeTestRule.onNodeWithTag(SignupScreenTestTag.PASSWORD_EDIT_TEXT.name) - - private val optionsIcon = - composeTestRule.onNodeWithTag( - SignupScreenTestTag.OPTIONS_ICON.name, - ) - - private val optionsMenu = composeTestRule.onNodeWithTag(LoginDropdownTestTag.OPTIONS_MENU.name) - private val optionsTerms = - composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_TERMS.name, - useUnmergedTree = true - ) - private val optionsPrivacyPolicy = composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_PRIVACY_POLICY.name, - useUnmergedTree = true - ) - private val optionsCookie = - composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_COOKIE.name, - useUnmergedTree = true - ) - private val optionsHelp = - composeTestRule.onNodeWithTag( - LoginDropdownTestTag.OPTIONS_HELP.name, - useUnmergedTree = true - ) - - @Test - fun testComponentsVisible() { - composeTestRule.setContent { - KSTheme { - SignupScreen( - onBackClicked = {}, - onSignupButtonClicked = { _, _, _, _ -> }, - showProgressBar = false, - isFormSubmitting = false, - onTermsOfUseClicked = { }, - onPrivacyPolicyClicked = { }, - onCookiePolicyClicked = { }, - onHelpClicked = { }, - scaffoldState = rememberScaffoldState() - ) - } - } - - backButton.assertIsDisplayed() - - val titleText = context.resources.getString(R.string.Sign_up) - pageTitle.assertTextEquals(titleText) - pageTitle.assertIsDisplayed() - - emailEditText.assertIsDisplayed() - nameEditText.assertIsDisplayed() - passwordEditText.assertIsDisplayed() - progressBar.assertDoesNotExist() - - val newsletterOptInString = - context.resources.getString(R.string.signup_newsletter_full_opt_out) - newsletterOptInText.assertTextEquals(newsletterOptInString) - newsletterOptInText.assertIsDisplayed() - - newsletterOptInSwitch.assertIsDisplayed() - newsletterOptInSwitch.assertIsEnabled() - } - - @Test - fun testSignupButton() { - var signupButtonClicked = 0 - - composeTestRule.setContent { - KSTheme { - SignupScreen( - onBackClicked = {}, - onSignupButtonClicked = { _, _, _, _ -> signupButtonClicked++ }, - showProgressBar = false, - isFormSubmitting = false, - onTermsOfUseClicked = { }, - onPrivacyPolicyClicked = { }, - onCookiePolicyClicked = { }, - onHelpClicked = { }, - scaffoldState = rememberScaffoldState() - ) - } - } - - emailEditText.assertIsDisplayed() - nameEditText.assertIsDisplayed() - passwordEditText.assertIsDisplayed() - progressBar.assertDoesNotExist() - signupButton.assertIsNotEnabled() - signupButton.performClick() - assertEquals(signupButtonClicked, 0) - - emailEditText.performTextInput("leigh@gmail.com") - nameEditText.performTextInput("test") - passwordEditText.performTextInput("sfdgdfgdgdhghgd") - signupButton.assertIsEnabled() - - signupButton.performClick() - assertEquals(signupButtonClicked, 1) - } - - @Test - fun testSendNewsletterSwitch() { - var sendNewsletter = false - var signupButtonClicked = 0 - - composeTestRule.setContent { - KSTheme { - SignupScreen( - onBackClicked = {}, - onSignupButtonClicked = { _, _, _, sendNewsletterClick -> - sendNewsletter = sendNewsletterClick - signupButtonClicked++ - }, - showProgressBar = false, - isFormSubmitting = false, - onTermsOfUseClicked = { }, - onPrivacyPolicyClicked = { }, - onCookiePolicyClicked = { }, - onHelpClicked = { }, - scaffoldState = rememberScaffoldState() - ) - } - } - - backButton.assertIsDisplayed() - - val titleText = context.resources.getString(R.string.Sign_up) - pageTitle.assertTextEquals(titleText) - pageTitle.assertIsDisplayed() - - emailEditText.assertIsDisplayed() - nameEditText.assertIsDisplayed() - passwordEditText.assertIsDisplayed() - progressBar.assertDoesNotExist() - - val newsletterOptInString = - context.resources.getString(R.string.signup_newsletter_full_opt_out) - newsletterOptInText.assertTextEquals(newsletterOptInString) - newsletterOptInText.assertIsDisplayed() - - newsletterOptInSwitch.assertIsDisplayed() - newsletterOptInSwitch.assertIsEnabled() - newsletterOptInSwitch.assertIsToggleable() - - emailEditText.performTextInput("leigh@gmail.com") - nameEditText.performTextInput("test") - passwordEditText.performTextInput("sfdgdfgdgdhghgd") - signupButton.assertIsEnabled() - - newsletterOptInSwitch.performTouchInput { swipeRight() } - signupButton.performClick() - assertEquals(signupButtonClicked, 1) - assertEquals(sendNewsletter, true) - - newsletterOptInSwitch.performTouchInput { swipeLeft() } - signupButton.performClick() - assertEquals(sendNewsletter, false) - } - - fun testBackButton() { - var backClickedCount = 0 - - composeTestRule.setContent { - KSTheme { - SignupScreen( - onBackClicked = { backClickedCount++ }, - onSignupButtonClicked = { _, _, _, _ -> }, - showProgressBar = false, - isFormSubmitting = false, - onTermsOfUseClicked = { }, - onPrivacyPolicyClicked = { }, - onCookiePolicyClicked = { }, - onHelpClicked = { }, - scaffoldState = rememberScaffoldState() - ) - } - } - - backButton.assertIsDisplayed() - backButton.performClick() - assertEquals(1, backClickedCount) - } - - fun testProgressBar() { - var backClickedCount = 0 - - composeTestRule.setContent { - KSTheme { - SignupScreen( - onBackClicked = { backClickedCount++ }, - onSignupButtonClicked = { _, _, _, _ -> }, - showProgressBar = false, - isFormSubmitting = false, - onTermsOfUseClicked = { }, - onPrivacyPolicyClicked = { }, - onCookiePolicyClicked = { }, - onHelpClicked = { }, - scaffoldState = rememberScaffoldState() - ) - } - } - - progressBar.assertDoesNotExist() - signupButton.assertIsNotEnabled() - signupButton.performClick() - - emailEditText.performTextInput("leigh@gmail.com") - nameEditText.performTextInput("test") - passwordEditText.performTextInput("sfdgdfgdgdhghgd") - signupButton.assertIsEnabled() - - signupButton.performClick() - progressBar.assertExists() - progressBar.assertIsDisplayed() - } - - @Test - fun testDropDownButtonClicks() { - var termsClickedCount = 0 - var privacyClickedCount = 0 - var cookieClickedCount = 0 - var helpClickedCount = 0 - - composeTestRule.setContent { - KSTheme { - SignupScreen( - onBackClicked = { }, - onSignupButtonClicked = { _, _, _, _ -> }, - showProgressBar = false, - isFormSubmitting = false, - onTermsOfUseClicked = { termsClickedCount++ }, - onPrivacyPolicyClicked = { privacyClickedCount++ }, - onCookiePolicyClicked = { cookieClickedCount++ }, - onHelpClicked = { helpClickedCount++ }, - scaffoldState = rememberScaffoldState() - ) - } - } - - optionsIcon.performClick() - optionsMenu.assertIsDisplayed() - - optionsTerms.performClick() - optionsMenu.assertDoesNotExist() - assertEquals(termsClickedCount, 1) - - optionsIcon.performClick() - optionsPrivacyPolicy.performClick() - assertEquals(privacyClickedCount, 1) - - optionsIcon.performClick() - optionsCookie.performClick() - assertEquals(cookieClickedCount, 1) - - optionsIcon.performClick() - optionsHelp.performClick() - assertEquals(helpClickedCount, 1) - } -} diff --git a/app/src/test/java/com/kickstarter/viewmodels/LoginToutViewModelTest.kt b/app/src/test/java/com/kickstarter/viewmodels/LoginToutViewModelTest.kt index 7efa48b1f7..7f6d27fe99 100644 --- a/app/src/test/java/com/kickstarter/viewmodels/LoginToutViewModelTest.kt +++ b/app/src/test/java/com/kickstarter/viewmodels/LoginToutViewModelTest.kt @@ -30,7 +30,6 @@ class LoginToutViewModelTest : KSRobolectricTestCase() { private val finishOathWithSuccessfulResult = TestSubscriber() private val loginError = TestSubscriber() private val startLoginActivity = TestSubscriber() - private val startSignupActivity = TestSubscriber() private val currentUser = TestSubscriber() private val showDisclaimerActivity = TestSubscriber() private val startResetPasswordActivity = TestSubscriber() @@ -43,8 +42,6 @@ class LoginToutViewModelTest : KSRobolectricTestCase() { vm.outputs.finishWithSuccessfulResult().subscribe { finishWithSuccessfulResult.onNext(it) } .addToDisposable(disposables) vm.loginError.subscribe { loginError.onNext(it) }.addToDisposable(disposables) - vm.outputs.startSignupActivity().subscribe { startSignupActivity.onNext(it) } - .addToDisposable(disposables) vm.outputs.startLoginActivity().subscribe { startLoginActivity.onNext(it) } .addToDisposable(disposables) vm.outputs.showFacebookErrorDialog().subscribe { showFacebookErrorDialog.onNext(it) } @@ -62,33 +59,6 @@ class LoginToutViewModelTest : KSRobolectricTestCase() { vm.provideLoginReason(loginReason) } - @Test - fun testLoginButtonClicked() { - val environment = environment() - - setUpEnvironment(environment, LoginReason.DEFAULT) - - startLoginActivity.assertNoValues() - - vm.inputs.loginClick() - - startLoginActivity.assertValueCount(1) - segmentTrack.assertValues(EventName.PAGE_VIEWED.eventName, EventName.CTA_CLICKED.eventName) - } - - @Test - fun testSignupButtonClicked() { - val environment = environment() - - setUpEnvironment(environment, LoginReason.DEFAULT) - startSignupActivity.assertNoValues() - - vm.inputs.signupClick() - - startSignupActivity.assertValueCount(1) - segmentTrack.assertValues(EventName.PAGE_VIEWED.eventName, EventName.CTA_CLICKED.eventName) - } - @Test fun facebookLogin_success() { val currentUser = MockCurrentUserV2() diff --git a/app/src/test/java/com/kickstarter/viewmodels/LoginViewModelTest.kt b/app/src/test/java/com/kickstarter/viewmodels/LoginViewModelTest.kt deleted file mode 100644 index bd96f90856..0000000000 --- a/app/src/test/java/com/kickstarter/viewmodels/LoginViewModelTest.kt +++ /dev/null @@ -1,405 +0,0 @@ -package com.kickstarter.viewmodels - -import android.app.Activity -import android.content.Intent -import com.kickstarter.KSRobolectricTestCase -import com.kickstarter.libs.ActivityRequestCodes -import com.kickstarter.libs.Environment -import com.kickstarter.libs.utils.EventName -import com.kickstarter.libs.utils.extensions.addToDisposable -import com.kickstarter.mock.MockCurrentConfig -import com.kickstarter.mock.MockCurrentConfigV2 -import com.kickstarter.mock.factories.ApiExceptionFactory -import com.kickstarter.mock.factories.ConfigFactory.config -import com.kickstarter.mock.factories.UserFactory -import com.kickstarter.mock.services.MockApiClientV2 -import com.kickstarter.mock.services.MockApolloClientV2 -import com.kickstarter.models.User -import com.kickstarter.models.UserPrivacy -import com.kickstarter.services.ApiException -import com.kickstarter.services.apiresponses.AccessTokenEnvelope -import com.kickstarter.services.apiresponses.ErrorEnvelope.Companion.builder -import com.kickstarter.ui.IntentKey -import com.kickstarter.ui.data.ActivityResult -import com.kickstarter.ui.data.LoginReason -import com.kickstarter.viewmodels.LoginViewModel.LoginViewModel -import io.reactivex.Observable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.subscribers.TestSubscriber -import okhttp3.ResponseBody.Companion.toResponseBody -import org.junit.After -import org.junit.Test -import retrofit2.Response - -class LoginViewModelTest : KSRobolectricTestCase() { - - private lateinit var vm: LoginViewModel - private val genericLoginError = TestSubscriber() - private val invalidLoginError = TestSubscriber() - private val logInButtonIsEnabled = TestSubscriber() - private val loginSuccess = TestSubscriber() - private val preFillEmail = TestSubscriber() - private val showChangedPasswordSnackbar = TestSubscriber() - private val showResetPasswordSuccessDialog = TestSubscriber() - private val tfaChallenge = TestSubscriber() - - private val disposables = CompositeDisposable() - - @After - fun cleanUp() { - disposables.clear() - } - - fun setUpEnvironment(environment: Environment, intent: Intent) { - this.vm = LoginViewModel(environment, intent) - - this.vm.outputs.genericLoginError().subscribe { this.genericLoginError.onNext(it) }.addToDisposable(disposables) - this.vm.outputs.invalidLoginError().subscribe { this.invalidLoginError.onNext(it) }.addToDisposable(disposables) - this.vm.outputs.isLoading().subscribe { this.logInButtonIsEnabled.onNext(it) }.addToDisposable(disposables) - this.vm.outputs.loginSuccess().subscribe { this.loginSuccess.onNext(it) }.addToDisposable(disposables) - this.vm.outputs.prefillEmail().subscribe { this.preFillEmail.onNext(it) }.addToDisposable(disposables) - this.vm.outputs.showChangedPasswordSnackbar().subscribe { - this.showChangedPasswordSnackbar.onNext(true) - }.addToDisposable(disposables) - this.vm.outputs.showResetPasswordSuccessDialog() - .map { showAndEmail -> showAndEmail.first } - .subscribe { this.showResetPasswordSuccessDialog.onNext(it) }.addToDisposable(disposables) - this.vm.outputs.tfaChallenge().subscribe { this.tfaChallenge.onNext(it) }.addToDisposable(disposables) - } - - @Test - fun testLoginButtonDisabledOnClick() { - val apiClient = object : MockApiClientV2() { - override fun login(email: String, password: String): Observable { - return Observable.error(ApiExceptionFactory.badRequestException()) - } - } - - val mockConfig = MockCurrentConfig() - mockConfig.config(config()) - - val environment = environment().toBuilder() - .currentConfig(mockConfig) - .apiClientV2(apiClient) - .build() - - setUpEnvironment(environment, Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com")) - - this.vm.inputs.email("hello@kickstarter.com") - this.vm.inputs.password("codeisawesome") - - this.vm.inputs.loginClick() - - this.logInButtonIsEnabled.assertValues(false, true) - } - - @Test - fun testLoginApiError() { - val apiClient = object : MockApiClientV2() { - override fun login(email: String, password: String): Observable { - return Observable.error(ApiExceptionFactory.badRequestException()) - } - } - - val mockConfig = MockCurrentConfig() - mockConfig.config(config()) - - val environment = environment().toBuilder() - .currentConfig(mockConfig) - .apiClientV2(apiClient) - .build() - - setUpEnvironment(environment, Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com")) - - this.vm.inputs.email("incorrect@kickstarter.com") - this.vm.inputs.password("lisaiscool") - - this.vm.inputs.loginClick() - - this.loginSuccess.assertNoValues() - this.genericLoginError.assertValueCount(1) - this.segmentTrack.assertValues(EventName.PAGE_VIEWED.eventName, EventName.CTA_CLICKED.eventName) - } - - @Test - fun testLoginApiValidationError() { - val apiClient = object : MockApiClientV2() { - override fun login(email: String, password: String): Observable { - return Observable.error(ApiExceptionFactory.invalidLoginException()) - } - } - - val mockConfig = MockCurrentConfig() - mockConfig.config(config()) - - val environment = environment().toBuilder() - .currentConfig(mockConfig) - .apiClientV2(apiClient) - .build() - - setUpEnvironment(environment, Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com")) - - this.vm.inputs.email("typo@kickstartr.com") - this.vm.inputs.password("julieiscool") - - this.vm.inputs.loginClick() - - this.loginSuccess.assertNoValues() - this.invalidLoginError.assertValueCount(1) - this.segmentTrack.assertValues(EventName.PAGE_VIEWED.eventName, EventName.CTA_CLICKED.eventName) - } - - @Test - fun testLoginTfaChallenge() { - val apiClient = object : MockApiClientV2() { - override fun login(email: String, password: String): Observable { - return Observable.error(ApiExceptionFactory.tfaRequired()) - } - } - - val mockConfig = MockCurrentConfigV2() - mockConfig.config(config()) - - val environment = environment().toBuilder() - .currentConfig2(mockConfig) - .apiClientV2(apiClient) - .build() - - setUpEnvironment(environment, Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com")) - - this.vm.inputs.email("hello@kickstarter.com") - this.vm.inputs.password("androidiscool") - - this.vm.inputs.loginClick() - - this.loginSuccess.assertNoValues() - this.tfaChallenge.assertValueCount(1) - this.segmentTrack.assertValues(EventName.PAGE_VIEWED.eventName, EventName.CTA_CLICKED.eventName) - } - - fun testLoginErrorNotExpected() { - val envelope = builder() - .errorMessages(null) - .httpCode(403) - .build() - val body = "".toResponseBody(null) - val response = Response.error>(403, body) - - val exception = ApiException(envelope, response) - - val apiClient = object : MockApiClientV2() { - override fun login(email: String, password: String): Observable { - return Observable.error(exception) - } - } - - val mockConfig = MockCurrentConfigV2() - mockConfig.config(config()) - - val environment = environment().toBuilder() - .currentConfig2(mockConfig) - .apiClientV2(apiClient) - .build() - - setUpEnvironment(environment, Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com")) - - this.vm.inputs.email("hello@kickstarter.com") - this.vm.inputs.password("androidiscool") - - this.vm.inputs.loginClick() - - this.loginSuccess.assertNoValues() - this.genericLoginError.assertValue("") - this.segmentTrack.assertValues(EventName.PAGE_VIEWED.eventName, EventName.CTA_CLICKED.eventName) - } - - @Test - fun testPrefillEmail() { - setUpEnvironment(environment(), Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com")) - - this.preFillEmail.assertValue("hello@kickstarter.com") - this.showResetPasswordSuccessDialog.assertNoValues() - this.showChangedPasswordSnackbar.assertNoValues() - } - - @Test - fun testPrefillEmailAndDialog() { - // Start the view model with an email to prefill. - setUpEnvironment(environment(), Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com").putExtra(IntentKey.LOGIN_REASON, LoginReason.RESET_PASSWORD)) - - this.preFillEmail.assertValue("hello@kickstarter.com") - this.showChangedPasswordSnackbar.assertNoValues() - this.showResetPasswordSuccessDialog.assertValue(true) - - // Dismiss the confirmation dialog. - this.vm.inputs.resetPasswordConfirmationDialogDismissed() - this.showChangedPasswordSnackbar.assertNoValues() - this.showResetPasswordSuccessDialog.assertValues(true, false) - - // Simulate success result after presenting ResetPassword Activity flow - this.vm.inputs.activityResult( - ActivityResult.create( - requestCode = ActivityRequestCodes.RESET_FLOW, - resultCode = Activity.RESULT_OK, - intent = Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com") - ) - ) - - val rotatedPrefillEmail = TestSubscriber() - this.vm.outputs.prefillEmail().subscribe { rotatedPrefillEmail.onNext(it) }.addToDisposable(disposables) - val rotatedShowChangedPasswordSnackbar = TestSubscriber() - this.vm.outputs.showChangedPasswordSnackbar().subscribe { - rotatedShowChangedPasswordSnackbar.onNext(it) - }.addToDisposable(disposables) - - val rotatedShowResetPasswordSuccessDialog = TestSubscriber() - this.vm.outputs.showResetPasswordSuccessDialog() - .map { showAndEmail -> showAndEmail.first } - .subscribe { - rotatedShowResetPasswordSuccessDialog.onNext(it) - } - .addToDisposable(disposables) - - // Email should still be pre-filled. - rotatedPrefillEmail.assertValue("hello@kickstarter.com") - - // Dialog should not be shown again – the user has already dismissed it. - rotatedShowResetPasswordSuccessDialog.assertValue(false) - - // Snackbar should not be shown. - rotatedShowChangedPasswordSnackbar.assertNoValues() - } - - @Test - fun testPrefillEmailAndSnackbar() { - setUpEnvironment(environment(), Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com").putExtra(IntentKey.LOGIN_REASON, LoginReason.CHANGE_PASSWORD)) - - this.preFillEmail.assertValue("hello@kickstarter.com") - this.showResetPasswordSuccessDialog.assertNoValues() - - this.vm.activityResult( - ActivityResult.create( - requestCode = ActivityRequestCodes.RESET_FLOW, - resultCode = Activity.RESULT_OK, - intent = Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com").putExtra(IntentKey.LOGIN_REASON, LoginReason.CHANGE_PASSWORD) - ) - ) - - // Create new test subscribers – this emulates a new activity subscribing to the vm's outputs. - val rotatedPrefillEmail = TestSubscriber() - this.vm.outputs.prefillEmail().subscribe { rotatedPrefillEmail.onNext(it) }.addToDisposable(disposables) - val rotatedShowChangedPasswordSnackbar = TestSubscriber() - this.vm.outputs.showChangedPasswordSnackbar().subscribe { - rotatedShowChangedPasswordSnackbar.onNext(Unit) - }.addToDisposable(disposables) - val rotatedShowResetPasswordSuccessDialog = TestSubscriber() - this.vm.outputs.showResetPasswordSuccessDialog() - .map { showAndEmail -> showAndEmail.first } - .subscribe { rotatedShowResetPasswordSuccessDialog.onNext(it) } - .addToDisposable(disposables) - - // Email should still be pre-filled. - rotatedPrefillEmail.assertValue("hello@kickstarter.com") - rotatedShowChangedPasswordSnackbar.assertValueCount(1) - - // Dialog should not be shown. - rotatedShowResetPasswordSuccessDialog.assertNoValues() - } - - @Test - fun testPrefillEmailAndResetPassword() { - setUpEnvironment(environment(), Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com")) - // Start the view model with an email to prefill. - this.vm.email("test@kickstarter.com") - this.vm.loginClick() - - // Start the view model with an email to prefill. - this.vm.inputs.activityResult( - ActivityResult( - ActivityRequestCodes.RESET_FLOW, - Activity.RESULT_OK, - Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com").putExtra(IntentKey.LOGIN_REASON, LoginReason.RESET_PASSWORD) - ) - ) - - this.preFillEmail.assertValue("hello@kickstarter.com") - this.showChangedPasswordSnackbar.assertNoValues() - this.showResetPasswordSuccessDialog.assertValue(true) - - // Dismiss the confirmation dialog. - this.vm.inputs.resetPasswordConfirmationDialogDismissed() - this.showChangedPasswordSnackbar.assertNoValues() - this.showResetPasswordSuccessDialog.assertValues(true, false) - - // Simulate rotating the device, first by sending a new intent (similar to what happens after rotation). - this.vm.inputs.activityResult( - ActivityResult( - ActivityRequestCodes.RESET_FLOW, - Activity.RESULT_OK, - Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com") - ) - ) - - // Create new test subscribers – this emulates a new activity subscribing to the vm's outputs. - val rotatedPrefillEmail = TestSubscriber() - this.vm.outputs.prefillEmail().subscribe { rotatedPrefillEmail.onNext(it) }.addToDisposable(disposables) - val rotatedShowChangedPasswordSnackbar = TestSubscriber() - this.vm.outputs.showChangedPasswordSnackbar().subscribe { - rotatedShowChangedPasswordSnackbar.onNext(Unit) - }.addToDisposable(disposables) - val rotatedShowResetPasswordSuccessDialog = TestSubscriber() - this.vm.outputs.showResetPasswordSuccessDialog() - .map { showAndEmail -> showAndEmail.first } - .subscribe { rotatedShowResetPasswordSuccessDialog.onNext(it) } - .addToDisposable(disposables) - - // Email should still be pre-filled. - rotatedPrefillEmail.assertValue("hello@kickstarter.com") - - // Dialog should not be shown again – the user has already dismissed it. - rotatedShowResetPasswordSuccessDialog.assertValue(false) - - // Snackbar should not be shown. - rotatedShowChangedPasswordSnackbar.assertNoValues() - } - - @Test - fun testSuccessfulLogin() { - val user = UserFactory.user() - val token = AccessTokenEnvelope.builder() - .user(user) - .accessToken("token") - .build() - - val apiClient = object : MockApiClientV2() { - override fun login(email: String, password: String): Observable { - return Observable.just(token) - } - } - - val apolloClient = object : MockApolloClientV2() { - override fun userPrivacy(): Observable { - return Observable.just( - UserPrivacy(user.name(), "hello@kickstarter.com", true, true, true, true, "USD") - ) - } - } - - val environment = environment().toBuilder() - .apolloClientV2(apolloClient) - .apiClientV2(apiClient) - .build() - - setUpEnvironment(environment, Intent().putExtra(IntentKey.EMAIL, "hello@kickstarter.com")) - - this.vm.inputs.email("hello@kickstarter.com") - this.vm.inputs.password("codeisawesome") - - this.vm.inputs.loginClick() - - this.loginSuccess.assertValueCount(1) - this.loginSuccess.assertValue(Unit) - - this.segmentTrack.assertValues(EventName.PAGE_VIEWED.eventName, EventName.CTA_CLICKED.eventName) - } -} diff --git a/app/src/test/java/com/kickstarter/viewmodels/SignupViewModelTest.kt b/app/src/test/java/com/kickstarter/viewmodels/SignupViewModelTest.kt deleted file mode 100644 index 8e72439e49..0000000000 --- a/app/src/test/java/com/kickstarter/viewmodels/SignupViewModelTest.kt +++ /dev/null @@ -1,182 +0,0 @@ -package com.kickstarter.viewmodels - -import com.kickstarter.KSRobolectricTestCase -import com.kickstarter.libs.utils.EventName -import com.kickstarter.libs.utils.extensions.addToDisposable -import com.kickstarter.mock.factories.ApiExceptionFactory -import com.kickstarter.mock.factories.ConfigFactory.config -import com.kickstarter.mock.factories.UserFactory -import com.kickstarter.mock.services.MockApiClientV2 -import com.kickstarter.mock.services.MockApolloClientV2 -import com.kickstarter.models.UserPrivacy -import com.kickstarter.services.ApiClientTypeV2 -import com.kickstarter.services.apiresponses.AccessTokenEnvelope -import com.kickstarter.services.apiresponses.ErrorEnvelope.Companion.builder -import io.reactivex.Observable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.subscribers.TestSubscriber -import org.junit.After -import org.junit.Test - -class SignupViewModelTest : KSRobolectricTestCase() { - - private val disposables = CompositeDisposable() - - @Test - fun testSignupViewModel_SuccessfulSignup() { - val apiClient: ApiClientTypeV2 = object : MockApiClientV2() { - override fun signup( - name: String, - email: String, - password: String, - passwordConfirmation: String, - sendNewsletters: Boolean - ): Observable { - return Observable.just( - AccessTokenEnvelope.builder() - .accessToken("") - .user(UserFactory.user()) - .build() - ) - } - } - - val apolloClient = object : MockApolloClientV2() { - override fun userPrivacy(): Observable { - return Observable.just( - UserPrivacy(UserFactory.user().name(), "hello@kickstarter.com", true, true, true, true, "USD") - ) - } - } - - val environment = environment().toBuilder() - .apiClientV2(apiClient) - .apolloClientV2(apolloClient) - .build() - - environment.currentConfigV2()?.config(config()) - - val vm = SignupViewModel.SignupViewModel(environment) - - val signupSuccessTest = TestSubscriber() - vm.outputs.signupSuccess().subscribe { signupSuccessTest.onNext(it) }.addToDisposable(disposables) - - val formSubmittingTest = TestSubscriber() - vm.outputs.formSubmitting().subscribe { formSubmittingTest.onNext(it) }.addToDisposable(disposables) - - val progressIndicatorVisible = TestSubscriber() - vm.outputs.progressBarIsVisible().subscribe { progressIndicatorVisible.onNext(it) }.addToDisposable(disposables) - - vm.inputs.name("brandon") - vm.inputs.email("hello@kickstarter.com") - vm.inputs.email("incorrect@kickstarter") - vm.inputs.password("danisawesome") - vm.inputs.sendNewsletters(true) - vm.inputs.signupClick() - - progressIndicatorVisible.assertValues(true, false) - formSubmittingTest.assertValues(true, false) - signupSuccessTest.assertValueCount(1) - environment.currentUserV2()?.observable()?.subscribe { - assertEquals("hello@kickstarter.com", it.getValue()?.email()) - }?.addToDisposable(disposables) - - segmentTrack.assertValues(EventName.PAGE_VIEWED.eventName, EventName.CTA_CLICKED.eventName) - } - - @Test - fun testSignupViewModel_ApiValidationError() { - val apiClient: ApiClientTypeV2 = object : MockApiClientV2() { - override fun signup( - name: String, - email: String, - password: String, - passwordConfirmation: String, - sendNewsletters: Boolean - ): Observable { - return Observable.error( - ApiExceptionFactory.apiError( - builder().httpCode(422).errorMessages(listOf("Unprocessable Content")).build() - ) - ) - } - } - - val environment = environment().toBuilder().apiClientV2(apiClient).build() - val vm = SignupViewModel.SignupViewModel(environment) - val signupSuccessTest = TestSubscriber() - vm.outputs.signupSuccess().subscribe { signupSuccessTest.onNext(it) }.addToDisposable(disposables) - - val signupErrorTest = TestSubscriber() - vm.outputs.errorString().subscribe { signupErrorTest.onNext(it) }.addToDisposable(disposables) - - val formSubmittingTest = TestSubscriber() - vm.outputs.formSubmitting().subscribe { formSubmittingTest.onNext(it) }.addToDisposable(disposables) - - val progressIndicatorVisible = TestSubscriber() - vm.outputs.progressBarIsVisible().subscribe { progressIndicatorVisible.onNext(it) }.addToDisposable(disposables) - - vm.inputs.name("brandon") - vm.inputs.email("hello@kickstarter.com") - vm.inputs.email("incorrect@kickstarter") - vm.inputs.password("danisawesome") - vm.inputs.sendNewsletters(true) - vm.inputs.signupClick() - - progressIndicatorVisible.assertValues(true, false) - formSubmittingTest.assertValues(true, false) - signupSuccessTest.assertValueCount(0) - signupErrorTest.assertValueCount(1) - - segmentTrack.assertValues(EventName.PAGE_VIEWED.eventName, EventName.CTA_CLICKED.eventName) - } - - @Test - fun testSignupViewModel_ApiError() { - val apiClient: ApiClientTypeV2 = object : MockApiClientV2() { - override fun signup( - name: String, - email: String, - password: String, - passwordConfirmation: String, - sendNewsletters: Boolean - ): Observable { - return Observable.error(ApiExceptionFactory.badRequestException()) - } - } - - val environment = environment().toBuilder().apiClientV2(apiClient).build() - val vm = SignupViewModel.SignupViewModel(environment) - val signupSuccessTest = TestSubscriber() - - vm.outputs.signupSuccess().subscribe { signupSuccessTest.onNext(it) }.addToDisposable(disposables) - - val signupErrorTest = TestSubscriber() - vm.outputs.errorString().subscribe { signupErrorTest.onNext(it) }.addToDisposable(disposables) - - val formSubmittingTest = TestSubscriber() - vm.outputs.formSubmitting().subscribe { formSubmittingTest.onNext(it) }.addToDisposable(disposables) - - val progressIndicatorVisible = TestSubscriber() - vm.outputs.progressBarIsVisible().subscribe { progressIndicatorVisible.onNext(it) }.addToDisposable(disposables) - - vm.inputs.name("brandon") - vm.inputs.email("hello@kickstarter.com") - vm.inputs.email("incorrect@kickstarter") - vm.inputs.password("danisawesome") - vm.inputs.sendNewsletters(true) - vm.inputs.signupClick() - - progressIndicatorVisible.assertValues(true, false) - formSubmittingTest.assertValues(true, false) - signupSuccessTest.assertValueCount(0) - signupErrorTest.assertValueCount(1) - - segmentTrack.assertValues(EventName.PAGE_VIEWED.eventName, EventName.CTA_CLICKED.eventName) - } - - @After - fun cleanUp() { - disposables.clear() - } -} diff --git a/app/src/test/java/com/kickstarter/viewmodels/TwoFactorViewModelTest.kt b/app/src/test/java/com/kickstarter/viewmodels/TwoFactorViewModelTest.kt deleted file mode 100644 index d3dca22fc5..0000000000 --- a/app/src/test/java/com/kickstarter/viewmodels/TwoFactorViewModelTest.kt +++ /dev/null @@ -1,266 +0,0 @@ -package com.kickstarter.viewmodels - -import android.content.Intent -import com.kickstarter.KSRobolectricTestCase -import com.kickstarter.libs.utils.EventName -import com.kickstarter.libs.utils.extensions.addToDisposable -import com.kickstarter.mock.factories.ApiExceptionFactory -import com.kickstarter.mock.factories.UserFactory -import com.kickstarter.mock.services.MockApiClientV2 -import com.kickstarter.mock.services.MockApolloClientV2 -import com.kickstarter.models.User -import com.kickstarter.models.UserPrivacy -import com.kickstarter.services.ApiClientTypeV2 -import com.kickstarter.services.apiresponses.AccessTokenEnvelope -import com.kickstarter.services.apiresponses.ErrorEnvelope.Companion.builder -import com.kickstarter.ui.IntentKey -import io.reactivex.Observable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.subscribers.TestSubscriber -import org.junit.After -import org.junit.Test - -class TwoFactorViewModelTest : KSRobolectricTestCase() { - - private lateinit var vm: TwoFactorViewModel.TwoFactorViewModel - - private val formIsValid = TestSubscriber() - private val formSubmitting = TestSubscriber() - private val genericTfaError = TestSubscriber() - private val showResendCodeConfirmation = TestSubscriber() - private val tfaCodeMismatchError = TestSubscriber() - private val tfaSuccess = TestSubscriber() - private val userTest = TestSubscriber() - private val disposables = CompositeDisposable() - - @Test - fun testTwoFactorViewModel_TfaSuccess() { - val intent = Intent() - - intent.putExtra(IntentKey.EMAIL, "gina@kickstarter.com") - intent.putExtra(IntentKey.PASSWORD, "hello") - intent.putExtra(IntentKey.FACEBOOK_LOGIN, false) - intent.putExtra(IntentKey.FACEBOOK_TOKEN, "") - - val user = UserFactory.user() - val token = AccessTokenEnvelope.builder() - .user(user) - .accessToken("token") - .build() - val apiClient = object : MockApiClientV2() { - override fun login(email: String, password: String, code: String): Observable { - return Observable.just(token) - } - } - val apolloClient = object : MockApolloClientV2() { - override fun userPrivacy(): Observable { - return Observable.just( - UserPrivacy(user.name(), "gina@kickstarter.com", true, true, true, true, "USD") - ) - } - } - - val environment = environment().toBuilder() - .apiClientV2(apiClient) - .apolloClientV2(apolloClient) - .build() - - vm = TwoFactorViewModel.TwoFactorViewModel(environment, intent) - - vm.outputs.tfaSuccess().subscribe { tfaSuccess.onNext(it) }.addToDisposable(disposables) - vm.outputs.formSubmitting() - .subscribe { formSubmitting.onNext(it) } - .addToDisposable(disposables) - vm.inputs.code("88888") - vm.inputs.loginClick() - - formSubmitting.assertValues(true, false) - tfaSuccess.assertValueCount(1) - environment.currentUserV2()?.observable()?.subscribe { - assertEquals("hello@kickstarter.com", it.getValue()?.email()) - }?.addToDisposable(disposables) - - segmentTrack.assertValue(EventName.PAGE_VIEWED.eventName) - } - - @Test - fun testTwoFactorViewModel_TfaSuccessFacebook() { - val intent = Intent() - - intent.putExtra(IntentKey.EMAIL, "gina@kickstarter.com") - intent.putExtra(IntentKey.PASSWORD, "hello") - intent.putExtra(IntentKey.FACEBOOK_LOGIN, true) - intent.putExtra(IntentKey.FACEBOOK_TOKEN, "pajamas1234") - - val user = UserFactory.user() - val token = AccessTokenEnvelope.builder() - .user(user) - .accessToken("token") - .build() - val apiClient = object : MockApiClientV2() { - override fun loginWithFacebook(fbAccessToken: String, code: String): Observable { - return Observable.just(token) - } - } - val apolloClient = object : MockApolloClientV2() { - override fun userPrivacy(): Observable { - return Observable.just( - UserPrivacy(user.name(), "gina@kickstarter.com", true, true, true, true, "USD") - ) - } - } - - val environment = environment().toBuilder() - .apiClientV2(apiClient) - .apolloClientV2(apolloClient) - .build() - - vm = TwoFactorViewModel.TwoFactorViewModel(environment, intent) - - vm.outputs.tfaSuccess().subscribe { tfaSuccess.onNext(it) }.addToDisposable(disposables) - vm.outputs.formSubmitting() - .subscribe { formSubmitting.onNext(it) } - .addToDisposable(disposables) - - vm.inputs.code("88888") - vm.inputs.loginClick() - - formSubmitting.assertValues(true, false) - tfaSuccess.assertValueCount(1) - environment.currentUserV2()?.observable()?.subscribe { - assertEquals("hello@kickstarter.com", it.getValue()?.email()) - }?.addToDisposable(disposables) - - segmentTrack.assertValue(EventName.PAGE_VIEWED.eventName) - } - - @Test - fun testTwoFactorViewModel_ResendCode() { - val intent = Intent() - - intent.putExtra(IntentKey.EMAIL, "gina@kickstarter.com") - intent.putExtra(IntentKey.PASSWORD, "hello") - intent.putExtra(IntentKey.FACEBOOK_LOGIN, false) - intent.putExtra(IntentKey.FACEBOOK_TOKEN, "") - - vm = TwoFactorViewModel.TwoFactorViewModel(environment(), intent) - - vm.outputs.showResendCodeConfirmation() - .subscribe { showResendCodeConfirmation.onNext(it) } - .addToDisposable(disposables) - - vm.inputs.resendClick() - - showResendCodeConfirmation.assertValueCount(1) - segmentTrack.assertValue(EventName.PAGE_VIEWED.eventName) - } - - @Test - fun testTwoFactorViewModel_ResendCodeFacebook() { - val intent = Intent() - - intent.putExtra(IntentKey.EMAIL, "gina@kickstarter.com") - intent.putExtra(IntentKey.PASSWORD, "hello") - intent.putExtra(IntentKey.FACEBOOK_LOGIN, true) - intent.putExtra(IntentKey.FACEBOOK_TOKEN, "pajamas1234") - - vm = TwoFactorViewModel.TwoFactorViewModel(environment(), intent) - - vm.outputs.showResendCodeConfirmation() - .subscribe { showResendCodeConfirmation.onNext(it) } - .addToDisposable(disposables) - - vm.inputs.resendClick() - - showResendCodeConfirmation.assertValueCount(1) - segmentTrack.assertValue(EventName.PAGE_VIEWED.eventName) - } - - @Test - fun testTwoFactorViewModel_GenericError() { - val apiClient: ApiClientTypeV2 = object : MockApiClientV2() { - override fun login( - email: String, - password: String, - code: String - ): Observable { - return Observable.error( - ApiExceptionFactory.apiError( - builder().httpCode(400).errorMessages(listOf("Generic Error")).build() - ) - ) - } - } - - val environment = environment().toBuilder().apiClientV2(apiClient).build() - - val intent = Intent() - - intent.putExtra(IntentKey.EMAIL, "gina@kickstarter.com") - intent.putExtra(IntentKey.PASSWORD, "hello") - intent.putExtra(IntentKey.FACEBOOK_LOGIN, false) - intent.putExtra(IntentKey.FACEBOOK_TOKEN, "") - - vm = TwoFactorViewModel.TwoFactorViewModel(environment, intent) - - vm.outputs.tfaSuccess().subscribe { tfaSuccess.onNext(it) }.addToDisposable(disposables) - vm.outputs.formSubmitting() - .subscribe { formSubmitting.onNext(it) } - .addToDisposable(disposables) - vm.outputs.genericTfaError() - .subscribe { genericTfaError.onNext(it) } - .addToDisposable(disposables) - - vm.inputs.code("88888") - vm.inputs.loginClick() - - formSubmitting.assertValues(true, false) - tfaSuccess.assertNoValues() - genericTfaError.assertValueCount(1) - segmentTrack.assertValue(EventName.PAGE_VIEWED.eventName) - } - - @Test - fun testTwoFactorViewModel_CodeMismatchError() { - val apiClient: ApiClientTypeV2 = object : MockApiClientV2() { - override fun login( - email: String, - password: String, - code: String - ): Observable { - return Observable.error(ApiExceptionFactory.tfaFailed()) - } - } - - val environment = environment().toBuilder().apiClientV2(apiClient).build() - val intent = Intent() - - intent.putExtra(IntentKey.EMAIL, "gina@kickstarter.com") - intent.putExtra(IntentKey.PASSWORD, "hello") - intent.putExtra(IntentKey.FACEBOOK_LOGIN, false) - intent.putExtra(IntentKey.FACEBOOK_TOKEN, "") - - vm = TwoFactorViewModel.TwoFactorViewModel(environment, intent) - - vm.outputs.tfaSuccess().subscribe { tfaSuccess.onNext(it) }.addToDisposable(disposables) - vm.outputs.formSubmitting() - .subscribe { formSubmitting.onNext(it) } - .addToDisposable(disposables) - vm.outputs.tfaCodeMismatchError() - .subscribe { tfaCodeMismatchError.onNext(it) } - .addToDisposable(disposables) - - vm.inputs.code("88888") - vm.inputs.loginClick() - - formSubmitting.assertValues(true, false) - tfaSuccess.assertNoValues() - tfaCodeMismatchError.assertValueCount(1) - segmentTrack.assertValue(EventName.PAGE_VIEWED.eventName) - } - - @After - fun cleanUp() { - disposables.clear() - } -}