mirror of
https://github.com/bitwarden/android.git
synced 2025-02-26 02:29:20 +03:00
[PM-11270] Hide all new UI behind onboarding flow flag. (#3810)
This commit is contained in:
parent
b56a21b6e5
commit
82d3b44712
11 changed files with 476 additions and 51 deletions
|
@ -81,6 +81,9 @@ fun NavGraphBuilder.authGraph(
|
||||||
)
|
)
|
||||||
checkEmailDestination(
|
checkEmailDestination(
|
||||||
onNavigateBack = { navController.popBackStack() },
|
onNavigateBack = { navController.popBackStack() },
|
||||||
|
onNavigateBackToLanding = {
|
||||||
|
navController.popBackStack(route = LANDING_ROUTE, inclusive = false)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
completeRegistrationDestination(
|
completeRegistrationDestination(
|
||||||
onNavigateBack = { navController.popBackStack() },
|
onNavigateBack = { navController.popBackStack() },
|
||||||
|
|
|
@ -36,6 +36,7 @@ data class CheckEmailArgs(
|
||||||
*/
|
*/
|
||||||
fun NavGraphBuilder.checkEmailDestination(
|
fun NavGraphBuilder.checkEmailDestination(
|
||||||
onNavigateBack: () -> Unit,
|
onNavigateBack: () -> Unit,
|
||||||
|
onNavigateBackToLanding: () -> Unit,
|
||||||
) {
|
) {
|
||||||
composableWithSlideTransitions(
|
composableWithSlideTransitions(
|
||||||
route = CHECK_EMAIL_ROUTE,
|
route = CHECK_EMAIL_ROUTE,
|
||||||
|
@ -45,6 +46,7 @@ fun NavGraphBuilder.checkEmailDestination(
|
||||||
) {
|
) {
|
||||||
CheckEmailScreen(
|
CheckEmailScreen(
|
||||||
onNavigateBack = onNavigateBack,
|
onNavigateBack = onNavigateBack,
|
||||||
|
onNavigateBackToLanding = onNavigateBackToLanding,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.text.ClickableText
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
@ -20,13 +21,17 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import androidx.compose.ui.semantics.onClick
|
||||||
|
import androidx.compose.ui.semantics.role
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
@ -35,6 +40,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
|
import com.x8bit.bitwarden.ui.auth.feature.checkemail.handlers.rememberCheckEmailHandler
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.createAnnotatedString
|
import com.x8bit.bitwarden.ui.platform.base.util.createAnnotatedString
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||||
|
@ -47,6 +53,8 @@ import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||||
|
|
||||||
|
private const val TAG_URL = "URL"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top level composable for the check email screen.
|
* Top level composable for the check email screen.
|
||||||
*/
|
*/
|
||||||
|
@ -55,19 +63,23 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||||
@Composable
|
@Composable
|
||||||
fun CheckEmailScreen(
|
fun CheckEmailScreen(
|
||||||
onNavigateBack: () -> Unit,
|
onNavigateBack: () -> Unit,
|
||||||
|
onNavigateBackToLanding: () -> Unit,
|
||||||
intentManager: IntentManager = LocalIntentManager.current,
|
intentManager: IntentManager = LocalIntentManager.current,
|
||||||
viewModel: CheckEmailViewModel = hiltViewModel(),
|
viewModel: CheckEmailViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||||
|
val handler = rememberCheckEmailHandler(viewModel = viewModel)
|
||||||
EventsEffect(viewModel) { event ->
|
EventsEffect(viewModel) { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
is CheckEmailEvent.NavigateBack -> {
|
is CheckEmailEvent.NavigateBack -> {
|
||||||
onNavigateBack.invoke()
|
onNavigateBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
is CheckEmailEvent.NavigateToEmailApp -> {
|
is CheckEmailEvent.NavigateToEmailApp -> {
|
||||||
intentManager.startDefaultEmailApplication()
|
intentManager.startDefaultEmailApplication()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckEmailEvent.NavigateBackToLanding -> onNavigateBackToLanding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,9 +94,7 @@ fun CheckEmailScreen(
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
navigationIcon = rememberVectorPainter(id = R.drawable.ic_back),
|
navigationIcon = rememberVectorPainter(id = R.drawable.ic_back),
|
||||||
navigationIconContentDescription = stringResource(id = R.string.back),
|
navigationIconContentDescription = stringResource(id = R.string.back),
|
||||||
onNavigationIconClick = remember(viewModel) {
|
onNavigationIconClick = handler.onBackClick,
|
||||||
{ viewModel.trySendAction(CheckEmailAction.BackClick) }
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
|
@ -95,20 +105,21 @@ fun CheckEmailScreen(
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
) {
|
) {
|
||||||
|
if (state.showNewOnboardingUi) {
|
||||||
CheckEmailContent(
|
CheckEmailContent(
|
||||||
email = state.email,
|
email = state.email,
|
||||||
onOpenEmailAppClick = remember(viewModel) {
|
onOpenEmailAppClick = handler.onOpenEmailAppClick,
|
||||||
{
|
onChangeEmailClick = handler.onChangeEmailClick,
|
||||||
viewModel.trySendAction(CheckEmailAction.OpenEmailClick)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onChangeEmailClick = remember(viewModel) {
|
|
||||||
{
|
|
||||||
viewModel.trySendAction(CheckEmailAction.ChangeEmailClick)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier.standardHorizontalMargin(),
|
modifier = Modifier.standardHorizontalMargin(),
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
CheckEmailLegacyContent(
|
||||||
|
email = state.email,
|
||||||
|
onOpenEmailAppClick = handler.onOpenEmailAppClick,
|
||||||
|
onChangeEmailClick = handler.onChangeEmailClick,
|
||||||
|
onLoginClick = handler.onLoginClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,9 +209,136 @@ private fun CheckEmailContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("LongMethod")
|
||||||
|
@Composable
|
||||||
|
private fun CheckEmailLegacyContent(
|
||||||
|
email: String,
|
||||||
|
onOpenEmailAppClick: () -> Unit,
|
||||||
|
onChangeEmailClick: () -> Unit,
|
||||||
|
onLoginClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
Image(
|
||||||
|
painter = rememberVectorPainter(id = R.drawable.email_check),
|
||||||
|
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary),
|
||||||
|
contentDescription = null,
|
||||||
|
contentScale = ContentScale.FillHeight,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.height(112.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.check_your_email),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
.wrapContentHeight()
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
val descriptionAnnotatedString = createAnnotatedString(
|
||||||
|
mainString = stringResource(
|
||||||
|
id = R.string.follow_the_instructions_in_the_email_sent_to_x_to_continue_creating_your_account,
|
||||||
|
email,
|
||||||
|
),
|
||||||
|
highlights = listOf(email),
|
||||||
|
highlightStyle = SpanStyle(
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
),
|
||||||
|
tag = "EMAIL",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = descriptionAnnotatedString,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight(),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
BitwardenFilledButton(
|
||||||
|
label = stringResource(id = R.string.open_email_app),
|
||||||
|
onClick = onOpenEmailAppClick,
|
||||||
|
modifier = Modifier
|
||||||
|
.testTag("OpenEmailApp")
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
val goBackAnnotatedString = createAnnotatedString(
|
||||||
|
mainString = stringResource(
|
||||||
|
id = R.string.no_email_go_back_to_edit_your_email_address,
|
||||||
|
),
|
||||||
|
highlights = listOf(stringResource(id = R.string.go_back)),
|
||||||
|
tag = TAG_URL,
|
||||||
|
)
|
||||||
|
ClickableText(
|
||||||
|
text = goBackAnnotatedString,
|
||||||
|
onClick = {
|
||||||
|
goBackAnnotatedString
|
||||||
|
.getStringAnnotations(TAG_URL, it, it)
|
||||||
|
.firstOrNull()?.let {
|
||||||
|
onChangeEmailClick()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.semantics {
|
||||||
|
role = Role.Button
|
||||||
|
onClick {
|
||||||
|
onChangeEmailClick()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
val logInAnnotatedString = createAnnotatedString(
|
||||||
|
mainString = stringResource(
|
||||||
|
id = R.string.or_log_in_you_may_already_have_an_account,
|
||||||
|
),
|
||||||
|
highlights = listOf(stringResource(id = R.string.log_in)),
|
||||||
|
tag = TAG_URL,
|
||||||
|
)
|
||||||
|
ClickableText(
|
||||||
|
text = logInAnnotatedString,
|
||||||
|
onClick = {
|
||||||
|
logInAnnotatedString
|
||||||
|
.getStringAnnotations(TAG_URL, it, it)
|
||||||
|
.firstOrNull()?.let {
|
||||||
|
onLoginClick()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.semantics {
|
||||||
|
role = Role.Button
|
||||||
|
onClick {
|
||||||
|
onLoginClick()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun CheckEmailScreenPreview() {
|
private fun CheckEmailScreenNewUi_preview() {
|
||||||
BitwardenTheme {
|
BitwardenTheme {
|
||||||
CheckEmailContent(
|
CheckEmailContent(
|
||||||
email = "email@fake.com",
|
email = "email@fake.com",
|
||||||
|
@ -210,3 +348,16 @@ private fun CheckEmailScreenPreview() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
private fun CheckEmailScreenLegacy_preview() {
|
||||||
|
BitwardenTheme {
|
||||||
|
CheckEmailLegacyContent(
|
||||||
|
email = "email@fake.com",
|
||||||
|
onOpenEmailAppClick = { },
|
||||||
|
onChangeEmailClick = { },
|
||||||
|
onLoginClick = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,10 +3,14 @@ package com.x8bit.bitwarden.ui.auth.feature.checkemail
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||||
|
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -17,11 +21,13 @@ private const val KEY_STATE = "state"
|
||||||
*/
|
*/
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class CheckEmailViewModel @Inject constructor(
|
class CheckEmailViewModel @Inject constructor(
|
||||||
|
featureFlagManager: FeatureFlagManager,
|
||||||
savedStateHandle: SavedStateHandle,
|
savedStateHandle: SavedStateHandle,
|
||||||
) : BaseViewModel<CheckEmailState, CheckEmailEvent, CheckEmailAction>(
|
) : BaseViewModel<CheckEmailState, CheckEmailEvent, CheckEmailAction>(
|
||||||
initialState = savedStateHandle[KEY_STATE]
|
initialState = savedStateHandle[KEY_STATE]
|
||||||
?: CheckEmailState(
|
?: CheckEmailState(
|
||||||
email = CheckEmailArgs(savedStateHandle).emailAddress,
|
email = CheckEmailArgs(savedStateHandle).emailAddress,
|
||||||
|
showNewOnboardingUi = featureFlagManager.getFeatureFlag(FlagKey.OnboardingFlow),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
|
@ -29,6 +35,14 @@ class CheckEmailViewModel @Inject constructor(
|
||||||
stateFlow
|
stateFlow
|
||||||
.onEach { savedStateHandle[KEY_STATE] = it }
|
.onEach { savedStateHandle[KEY_STATE] = it }
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
|
// Listen for changes on the onboarding feature flag.
|
||||||
|
featureFlagManager
|
||||||
|
.getFeatureFlagFlow(FlagKey.OnboardingFlow)
|
||||||
|
.map {
|
||||||
|
CheckEmailAction.Internal.OnboardingFeatureFlagUpdated(it)
|
||||||
|
}
|
||||||
|
.onEach(::sendAction)
|
||||||
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleAction(action: CheckEmailAction) {
|
override fun handleAction(action: CheckEmailAction) {
|
||||||
|
@ -36,6 +50,23 @@ class CheckEmailViewModel @Inject constructor(
|
||||||
CheckEmailAction.BackClick -> handleBackClick()
|
CheckEmailAction.BackClick -> handleBackClick()
|
||||||
CheckEmailAction.OpenEmailClick -> handleOpenEmailClick()
|
CheckEmailAction.OpenEmailClick -> handleOpenEmailClick()
|
||||||
CheckEmailAction.ChangeEmailClick -> handleChangeEmailClick()
|
CheckEmailAction.ChangeEmailClick -> handleChangeEmailClick()
|
||||||
|
is CheckEmailAction.Internal.OnboardingFeatureFlagUpdated -> {
|
||||||
|
handleOnboardingFeatureFlagUpdated(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckEmailAction.LoginClick -> handleLoginClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleLoginClick() {
|
||||||
|
sendEvent(CheckEmailEvent.NavigateBackToLanding)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleOnboardingFeatureFlagUpdated(
|
||||||
|
action: CheckEmailAction.Internal.OnboardingFeatureFlagUpdated,
|
||||||
|
) {
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(showNewOnboardingUi = action.newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +83,7 @@ class CheckEmailViewModel @Inject constructor(
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class CheckEmailState(
|
data class CheckEmailState(
|
||||||
val email: String,
|
val email: String,
|
||||||
|
val showNewOnboardingUi: Boolean,
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,6 +100,11 @@ sealed class CheckEmailEvent {
|
||||||
* Navigate to email app.
|
* Navigate to email app.
|
||||||
*/
|
*/
|
||||||
data object NavigateToEmailApp : CheckEmailEvent()
|
data object NavigateToEmailApp : CheckEmailEvent()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate back to Landing
|
||||||
|
*/
|
||||||
|
data object NavigateBackToLanding : CheckEmailEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,4 +125,19 @@ sealed class CheckEmailAction {
|
||||||
* User clicked open email.
|
* User clicked open email.
|
||||||
*/
|
*/
|
||||||
data object OpenEmailClick : CheckEmailAction()
|
data object OpenEmailClick : CheckEmailAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked log in.
|
||||||
|
*/
|
||||||
|
data object LoginClick : CheckEmailAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denotes an internal action.
|
||||||
|
*/
|
||||||
|
sealed class Internal : CheckEmailAction() {
|
||||||
|
/**
|
||||||
|
* Indicates updated value for onboarding feature flag.
|
||||||
|
*/
|
||||||
|
data class OnboardingFeatureFlagUpdated(val newValue: Boolean) : Internal()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.x8bit.bitwarden.ui.auth.feature.checkemail.handlers
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import com.x8bit.bitwarden.ui.auth.feature.checkemail.CheckEmailAction
|
||||||
|
import com.x8bit.bitwarden.ui.auth.feature.checkemail.CheckEmailViewModel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for [CheckEmailScreen] actions.
|
||||||
|
*/
|
||||||
|
class CheckEmailHandler(
|
||||||
|
val onOpenEmailAppClick: () -> Unit,
|
||||||
|
val onChangeEmailClick: () -> Unit,
|
||||||
|
val onBackClick: () -> Unit,
|
||||||
|
val onLoginClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create [CheckEmailHandler] with the given [viewModel] to send actions to.
|
||||||
|
*/
|
||||||
|
fun create(viewModel: CheckEmailViewModel) = CheckEmailHandler(
|
||||||
|
onChangeEmailClick = { viewModel.trySendAction(CheckEmailAction.ChangeEmailClick) },
|
||||||
|
onOpenEmailAppClick = { viewModel.trySendAction(CheckEmailAction.OpenEmailClick) },
|
||||||
|
onLoginClick = { viewModel.trySendAction(CheckEmailAction.LoginClick) },
|
||||||
|
onBackClick = { viewModel.trySendAction(CheckEmailAction.BackClick) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remember [CheckEmailHandler] with the given [viewModel] within a [Composable] scope.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun rememberCheckEmailHandler(viewModel: CheckEmailViewModel) =
|
||||||
|
remember(viewModel) {
|
||||||
|
CheckEmailHandler.create(viewModel)
|
||||||
|
}
|
|
@ -194,6 +194,7 @@ fun StartRegistrationScreen(
|
||||||
nameInput = state.nameInput,
|
nameInput = state.nameInput,
|
||||||
isReceiveMarketingEmailsToggled = state.isReceiveMarketingEmailsToggled,
|
isReceiveMarketingEmailsToggled = state.isReceiveMarketingEmailsToggled,
|
||||||
isContinueButtonEnabled = state.isContinueButtonEnabled,
|
isContinueButtonEnabled = state.isContinueButtonEnabled,
|
||||||
|
isNewOnboardingUiEnabled = state.showNewOnboardingUi,
|
||||||
handler = handler,
|
handler = handler,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||||
|
@ -210,10 +211,12 @@ private fun StartRegistrationContent(
|
||||||
isReceiveMarketingEmailsToggled: Boolean,
|
isReceiveMarketingEmailsToggled: Boolean,
|
||||||
isContinueButtonEnabled: Boolean,
|
isContinueButtonEnabled: Boolean,
|
||||||
handler: StartRegistrationHandler,
|
handler: StartRegistrationHandler,
|
||||||
|
isNewOnboardingUiEnabled: Boolean,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
if (isNewOnboardingUiEnabled) {
|
||||||
Image(
|
Image(
|
||||||
painter = rememberVectorPainter(id = R.drawable.vault),
|
painter = rememberVectorPainter(id = R.drawable.vault),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
@ -222,6 +225,7 @@ private fun StartRegistrationContent(
|
||||||
.align(Alignment.CenterHorizontally),
|
.align(Alignment.CenterHorizontally),
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(48.dp))
|
Spacer(modifier = Modifier.height(48.dp))
|
||||||
|
}
|
||||||
BitwardenTextField(
|
BitwardenTextField(
|
||||||
label = stringResource(id = R.string.name),
|
label = stringResource(id = R.string.name),
|
||||||
value = nameInput,
|
value = nameInput,
|
||||||
|
@ -233,13 +237,9 @@ private fun StartRegistrationContent(
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
BitwardenTextField(
|
BitwardenTextField(
|
||||||
label = if (emailInput.isEmpty()) {
|
label = stringResource(
|
||||||
stringResource(R.string.email_address_required)
|
|
||||||
} else {
|
|
||||||
stringResource(
|
|
||||||
id = R.string.email_address,
|
id = R.string.email_address,
|
||||||
)
|
),
|
||||||
},
|
|
||||||
placeholder = stringResource(R.string.email_address_required),
|
placeholder = stringResource(R.string.email_address_required),
|
||||||
value = emailInput,
|
value = emailInput,
|
||||||
onValueChange = handler.onEmailInputChange,
|
onValueChange = handler.onEmailInputChange,
|
||||||
|
@ -263,6 +263,7 @@ private fun StartRegistrationContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.testTag("RegionSelectorDropdown"),
|
.testTag("RegionSelectorDropdown"),
|
||||||
)
|
)
|
||||||
|
if (isNewOnboardingUiEnabled) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = handler.onServerGeologyHelpClick,
|
onClick = handler.onServerGeologyHelpClick,
|
||||||
// Align with design but keep accessible touch target of IconButton.
|
// Align with design but keep accessible touch target of IconButton.
|
||||||
|
@ -276,6 +277,7 @@ private fun StartRegistrationContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
if (selectedEnvironmentType != Environment.Type.SELF_HOSTED) {
|
if (selectedEnvironmentType != Environment.Type.SELF_HOSTED) {
|
||||||
|
@ -474,7 +476,7 @@ private fun ReceiveMarketingEmailsSwitch(
|
||||||
|
|
||||||
@PreviewScreenSizes
|
@PreviewScreenSizes
|
||||||
@Composable
|
@Composable
|
||||||
private fun StartRegistrationContentPreview_filledout() {
|
private fun StartRegistrationContentFilledOut_preview() {
|
||||||
BitwardenTheme {
|
BitwardenTheme {
|
||||||
StartRegistrationContent(
|
StartRegistrationContent(
|
||||||
emailInput = "e@mail.com",
|
emailInput = "e@mail.com",
|
||||||
|
@ -482,6 +484,7 @@ private fun StartRegistrationContentPreview_filledout() {
|
||||||
nameInput = "Test User",
|
nameInput = "Test User",
|
||||||
isReceiveMarketingEmailsToggled = true,
|
isReceiveMarketingEmailsToggled = true,
|
||||||
isContinueButtonEnabled = true,
|
isContinueButtonEnabled = true,
|
||||||
|
isNewOnboardingUiEnabled = false,
|
||||||
handler = StartRegistrationHandler(
|
handler = StartRegistrationHandler(
|
||||||
onEmailInputChange = {},
|
onEmailInputChange = {},
|
||||||
onNameInputChange = {},
|
onNameInputChange = {},
|
||||||
|
@ -500,7 +503,7 @@ private fun StartRegistrationContentPreview_filledout() {
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun StartRegistrationContentPreview_empty() {
|
private fun StartRegistrationContentEmpty_preview() {
|
||||||
BitwardenTheme {
|
BitwardenTheme {
|
||||||
StartRegistrationContent(
|
StartRegistrationContent(
|
||||||
emailInput = "",
|
emailInput = "",
|
||||||
|
@ -508,6 +511,34 @@ private fun StartRegistrationContentPreview_empty() {
|
||||||
nameInput = "",
|
nameInput = "",
|
||||||
isReceiveMarketingEmailsToggled = false,
|
isReceiveMarketingEmailsToggled = false,
|
||||||
isContinueButtonEnabled = false,
|
isContinueButtonEnabled = false,
|
||||||
|
isNewOnboardingUiEnabled = false,
|
||||||
|
handler = StartRegistrationHandler(
|
||||||
|
onEmailInputChange = {},
|
||||||
|
onNameInputChange = {},
|
||||||
|
onEnvironmentTypeSelect = {},
|
||||||
|
onContinueClick = {},
|
||||||
|
onTermsClick = {},
|
||||||
|
onPrivacyPolicyClick = {},
|
||||||
|
onReceiveMarketingEmailsToggle = {},
|
||||||
|
onUnsubscribeMarketingEmailsClick = {},
|
||||||
|
onServerGeologyHelpClick = {},
|
||||||
|
onBackClick = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
private fun StartRegistrationContentNewOnboardingUi_preview() {
|
||||||
|
BitwardenTheme {
|
||||||
|
StartRegistrationContent(
|
||||||
|
emailInput = "",
|
||||||
|
selectedEnvironmentType = Environment.Type.US,
|
||||||
|
nameInput = "",
|
||||||
|
isReceiveMarketingEmailsToggled = false,
|
||||||
|
isContinueButtonEnabled = false,
|
||||||
|
isNewOnboardingUiEnabled = true,
|
||||||
handler = StartRegistrationHandler(
|
handler = StartRegistrationHandler(
|
||||||
onEmailInputChange = {},
|
onEmailInputChange = {},
|
||||||
onNameInputChange = {},
|
onNameInputChange = {},
|
||||||
|
|
|
@ -7,6 +7,8 @@ import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||||
import com.x8bit.bitwarden.data.auth.repository.model.RegisterResult
|
import com.x8bit.bitwarden.data.auth.repository.model.RegisterResult
|
||||||
import com.x8bit.bitwarden.data.auth.repository.model.SendVerificationEmailResult
|
import com.x8bit.bitwarden.data.auth.repository.model.SendVerificationEmailResult
|
||||||
|
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||||
|
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment.Type
|
import com.x8bit.bitwarden.data.platform.repository.model.Environment.Type
|
||||||
|
@ -15,6 +17,7 @@ import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAc
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.EmailInputChange
|
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.EmailInputChange
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.EnvironmentTypeSelect
|
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.EnvironmentTypeSelect
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.ErrorDialogDismiss
|
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.ErrorDialogDismiss
|
||||||
|
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.Internal.OnboardingFeatureFlagUpdated
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.Internal.ReceiveSendVerificationEmailResult
|
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.Internal.ReceiveSendVerificationEmailResult
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.Internal.UpdatedEnvironmentReceive
|
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.Internal.UpdatedEnvironmentReceive
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.NameInputChange
|
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.NameInputChange
|
||||||
|
@ -29,6 +32,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.isValidEmail
|
||||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BasicDialogState
|
import com.x8bit.bitwarden.ui.platform.components.dialog.BasicDialogState
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -44,6 +48,7 @@ private const val KEY_STATE = "state"
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class StartRegistrationViewModel @Inject constructor(
|
class StartRegistrationViewModel @Inject constructor(
|
||||||
savedStateHandle: SavedStateHandle,
|
savedStateHandle: SavedStateHandle,
|
||||||
|
featureFlagManager: FeatureFlagManager,
|
||||||
private val authRepository: AuthRepository,
|
private val authRepository: AuthRepository,
|
||||||
private val environmentRepository: EnvironmentRepository,
|
private val environmentRepository: EnvironmentRepository,
|
||||||
) : BaseViewModel<StartRegistrationState, StartRegistrationEvent, StartRegistrationAction>(
|
) : BaseViewModel<StartRegistrationState, StartRegistrationEvent, StartRegistrationAction>(
|
||||||
|
@ -55,6 +60,7 @@ class StartRegistrationViewModel @Inject constructor(
|
||||||
isContinueButtonEnabled = false,
|
isContinueButtonEnabled = false,
|
||||||
selectedEnvironmentType = environmentRepository.environment.type,
|
selectedEnvironmentType = environmentRepository.environment.type,
|
||||||
dialog = null,
|
dialog = null,
|
||||||
|
showNewOnboardingUi = featureFlagManager.getFeatureFlag(FlagKey.OnboardingFlow),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -73,6 +79,14 @@ class StartRegistrationViewModel @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
|
// Listen for changes on the onboarding feature flag.
|
||||||
|
featureFlagManager
|
||||||
|
.getFeatureFlagFlow(FlagKey.OnboardingFlow)
|
||||||
|
.map {
|
||||||
|
OnboardingFeatureFlagUpdated(it)
|
||||||
|
}
|
||||||
|
.onEach(::sendAction)
|
||||||
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleAction(action: StartRegistrationAction) {
|
override fun handleAction(action: StartRegistrationAction) {
|
||||||
|
@ -99,6 +113,13 @@ class StartRegistrationViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerGeologyHelpClick -> handleServerGeologyHelpClick()
|
ServerGeologyHelpClick -> handleServerGeologyHelpClick()
|
||||||
|
is OnboardingFeatureFlagUpdated -> handleOnboardingFeatureFlagUpdated(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleOnboardingFeatureFlagUpdated(action: OnboardingFeatureFlagUpdated) {
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(showNewOnboardingUi = action.newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +290,7 @@ data class StartRegistrationState(
|
||||||
val isContinueButtonEnabled: Boolean,
|
val isContinueButtonEnabled: Boolean,
|
||||||
val selectedEnvironmentType: Type,
|
val selectedEnvironmentType: Type,
|
||||||
val dialog: StartRegistrationDialog?,
|
val dialog: StartRegistrationDialog?,
|
||||||
|
val showNewOnboardingUi: Boolean,
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -422,5 +444,10 @@ sealed class StartRegistrationAction {
|
||||||
data class UpdatedEnvironmentReceive(
|
data class UpdatedEnvironmentReceive(
|
||||||
val environment: Environment,
|
val environment: Environment,
|
||||||
) : Internal()
|
) : Internal()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates updated value for onboarding feature flag.
|
||||||
|
*/
|
||||||
|
data class OnboardingFeatureFlagUpdated(val newValue: Boolean) : Internal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package com.x8bit.bitwarden.ui.auth.feature.checkemail
|
package com.x8bit.bitwarden.ui.auth.feature.checkemail
|
||||||
|
|
||||||
|
import androidx.compose.ui.semantics.SemanticsActions
|
||||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
import androidx.compose.ui.test.onNodeWithText
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
import androidx.compose.ui.test.performScrollTo
|
import androidx.compose.ui.test.performScrollTo
|
||||||
|
import androidx.compose.ui.test.performSemanticsAction
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||||
|
@ -12,8 +14,8 @@ import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.runs
|
import io.mockk.runs
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import junit.framework.TestCase
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
|
@ -22,6 +24,7 @@ class CheckEmailScreenTest : BaseComposeTest() {
|
||||||
every { startDefaultEmailApplication() } just runs
|
every { startDefaultEmailApplication() } just runs
|
||||||
}
|
}
|
||||||
private var onNavigateBackCalled = false
|
private var onNavigateBackCalled = false
|
||||||
|
private var onNavigateToLandingCalled = false
|
||||||
|
|
||||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||||
private val mutableEventFlow = bufferedMutableSharedFlow<CheckEmailEvent>()
|
private val mutableEventFlow = bufferedMutableSharedFlow<CheckEmailEvent>()
|
||||||
|
@ -35,6 +38,7 @@ class CheckEmailScreenTest : BaseComposeTest() {
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent {
|
||||||
CheckEmailScreen(
|
CheckEmailScreen(
|
||||||
onNavigateBack = { onNavigateBackCalled = true },
|
onNavigateBack = { onNavigateBackCalled = true },
|
||||||
|
onNavigateBackToLanding = { onNavigateToLandingCalled = true },
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
intentManager = intentManager,
|
intentManager = intentManager,
|
||||||
)
|
)
|
||||||
|
@ -63,10 +67,16 @@ class CheckEmailScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `login button click should send LoginTap action`() {
|
||||||
|
mutableEventFlow.tryEmit(CheckEmailEvent.NavigateBackToLanding)
|
||||||
|
assertTrue(onNavigateToLandingCalled)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `NavigateBack should call onNavigateBack`() {
|
fun `NavigateBack should call onNavigateBack`() {
|
||||||
mutableEventFlow.tryEmit(CheckEmailEvent.NavigateBack)
|
mutableEventFlow.tryEmit(CheckEmailEvent.NavigateBack)
|
||||||
TestCase.assertTrue(onNavigateBackCalled)
|
assertTrue(onNavigateBackCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -77,8 +87,31 @@ class CheckEmailScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `go back and update email text click should send ChangeEmailClick action`() {
|
||||||
|
mutableStateFlow.value = DEFAULT_STATE.copy(showNewOnboardingUi = false)
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText("No email? Go back to edit your email address.")
|
||||||
|
.performScrollTo()
|
||||||
|
.performSemanticsAction(SemanticsActions.OnClick)
|
||||||
|
|
||||||
|
verify { viewModel.trySendAction(CheckEmailAction.ChangeEmailClick) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `already have account text click should send ChangeEmailClick action`() {
|
||||||
|
mutableStateFlow.value = DEFAULT_STATE.copy(showNewOnboardingUi = false)
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText("Or log in, you may already have an account.")
|
||||||
|
.performScrollTo()
|
||||||
|
.performSemanticsAction(SemanticsActions.OnClick)
|
||||||
|
|
||||||
|
verify { viewModel.trySendAction(CheckEmailAction.LoginClick) }
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `change email button click should send ChangeEmailClick action`() {
|
fun `change email button click should send ChangeEmailClick action`() {
|
||||||
|
mutableStateFlow.value = DEFAULT_STATE.copy(showNewOnboardingUi = true)
|
||||||
composeTestRule
|
composeTestRule
|
||||||
.onNodeWithText("Change email address")
|
.onNodeWithText("Change email address")
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
|
@ -91,6 +124,7 @@ class CheckEmailScreenTest : BaseComposeTest() {
|
||||||
private const val EMAIL = "test@gmail.com"
|
private const val EMAIL = "test@gmail.com"
|
||||||
private val DEFAULT_STATE = CheckEmailState(
|
private val DEFAULT_STATE = CheckEmailState(
|
||||||
email = EMAIL,
|
email = EMAIL,
|
||||||
|
showNewOnboardingUi = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,23 @@ package com.x8bit.bitwarden.ui.auth.feature.checkemail
|
||||||
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
|
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||||
|
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
class CheckEmailViewModelTest : BaseViewModelTest() {
|
class CheckEmailViewModelTest : BaseViewModelTest() {
|
||||||
|
private val mutableFeatureFlagFlow = MutableStateFlow(false)
|
||||||
|
private val featureFlagManager = mockk<FeatureFlagManager>(relaxed = true) {
|
||||||
|
every { getFeatureFlag(FlagKey.OnboardingFlow) } returns false
|
||||||
|
every { getFeatureFlagFlow(FlagKey.OnboardingFlow) } returns mutableFeatureFlagFlow
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state should be correct`() = runTest {
|
fun `initial state should be correct`() = runTest {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
|
@ -63,8 +74,31 @@ class CheckEmailViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `OnboardingFeatureFlagUpdated should update showNewOnboardingUi in state`() {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
mutableFeatureFlagFlow.value = true
|
||||||
|
val expectedState = DEFAULT_STATE.copy(
|
||||||
|
showNewOnboardingUi = true,
|
||||||
|
)
|
||||||
|
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `OnLoginClick action should send NavigateToLanding event`() = runTest {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(CheckEmailAction.LoginClick)
|
||||||
|
assertEquals(
|
||||||
|
CheckEmailEvent.NavigateBackToLanding,
|
||||||
|
awaitItem(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createViewModel(state: CheckEmailState? = null): CheckEmailViewModel =
|
private fun createViewModel(state: CheckEmailState? = null): CheckEmailViewModel =
|
||||||
CheckEmailViewModel(
|
CheckEmailViewModel(
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
savedStateHandle = SavedStateHandle().also {
|
savedStateHandle = SavedStateHandle().also {
|
||||||
it["email"] = EMAIL
|
it["email"] = EMAIL
|
||||||
it["state"] = state
|
it["state"] = state
|
||||||
|
@ -75,6 +109,7 @@ class CheckEmailViewModelTest : BaseViewModelTest() {
|
||||||
private const val EMAIL = "test@gmail.com"
|
private const val EMAIL = "test@gmail.com"
|
||||||
private val DEFAULT_STATE = CheckEmailState(
|
private val DEFAULT_STATE = CheckEmailState(
|
||||||
email = EMAIL,
|
email = EMAIL,
|
||||||
|
showNewOnboardingUi = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ class StartRegistrationScreenTest : BaseComposeTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `email input change should send EmailInputChange action`() {
|
fun `email input change should send EmailInputChange action`() {
|
||||||
composeTestRule.onNodeWithText("Email address (required)").performTextInput(TEST_INPUT)
|
composeTestRule.onNodeWithText("Email address").performTextInput(TEST_INPUT)
|
||||||
verify { viewModel.trySendAction(EmailInputChange(TEST_INPUT)) }
|
verify { viewModel.trySendAction(EmailInputChange(TEST_INPUT)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +180,7 @@ class StartRegistrationScreenTest : BaseComposeTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `clicking the server tool tip should send ServerGeologyHelpClickAction`() {
|
fun `clicking the server tool tip should send ServerGeologyHelpClickAction`() {
|
||||||
|
mutableStateFlow.value = DEFAULT_STATE.copy(showNewOnboardingUi = true)
|
||||||
composeTestRule
|
composeTestRule
|
||||||
.onNodeWithContentDescription("Help with server geolocations.")
|
.onNodeWithContentDescription("Help with server geolocations.")
|
||||||
.performScrollTo()
|
.performScrollTo()
|
||||||
|
@ -188,6 +189,14 @@ class StartRegistrationScreenTest : BaseComposeTest() {
|
||||||
verify { viewModel.trySendAction(StartRegistrationAction.ServerGeologyHelpClick) }
|
verify { viewModel.trySendAction(StartRegistrationAction.ServerGeologyHelpClick) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `server tool tip should not exist if not in new onboarding ui`() {
|
||||||
|
mutableStateFlow.value = DEFAULT_STATE.copy(showNewOnboardingUi = false)
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithContentDescription("Help with server geolocations.")
|
||||||
|
.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when NavigateToServerSelectionInfo is observed event should invoke intent manager`() {
|
fun `when NavigateToServerSelectionInfo is observed event should invoke intent manager`() {
|
||||||
mutableEventFlow.tryEmit(StartRegistrationEvent.NavigateToServerSelectionInfo)
|
mutableEventFlow.tryEmit(StartRegistrationEvent.NavigateToServerSelectionInfo)
|
||||||
|
@ -279,6 +288,7 @@ class StartRegistrationScreenTest : BaseComposeTest() {
|
||||||
isContinueButtonEnabled = false,
|
isContinueButtonEnabled = false,
|
||||||
selectedEnvironmentType = Environment.Type.US,
|
selectedEnvironmentType = Environment.Type.US,
|
||||||
dialog = null,
|
dialog = null,
|
||||||
|
showNewOnboardingUi = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||||
import com.x8bit.bitwarden.data.auth.repository.model.SendVerificationEmailResult
|
import com.x8bit.bitwarden.data.auth.repository.model.SendVerificationEmailResult
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
||||||
|
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||||
|
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
|
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.BackClick
|
import com.x8bit.bitwarden.ui.auth.feature.startregistration.StartRegistrationAction.BackClick
|
||||||
|
@ -33,6 +35,7 @@ import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
import io.mockk.unmockkStatic
|
import io.mockk.unmockkStatic
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.jupiter.api.AfterEach
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
@ -42,7 +45,11 @@ import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
@Suppress("LargeClass")
|
@Suppress("LargeClass")
|
||||||
class StartRegistrationViewModelTest : BaseViewModelTest() {
|
class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
|
private val mutableFeatureFlagFlow = MutableStateFlow(false)
|
||||||
|
private val featureFlagManager = mockk<FeatureFlagManager>(relaxed = true) {
|
||||||
|
every { getFeatureFlag(FlagKey.OnboardingFlow) } returns false
|
||||||
|
every { getFeatureFlagFlow(FlagKey.OnboardingFlow) } returns mutableFeatureFlagFlow
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Saved state handle that has valid inputs. Useful for tests that want to test things
|
* Saved state handle that has valid inputs. Useful for tests that want to test things
|
||||||
* after the user has entered all valid inputs.
|
* after the user has entered all valid inputs.
|
||||||
|
@ -71,6 +78,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value)
|
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value)
|
||||||
}
|
}
|
||||||
|
@ -84,12 +92,14 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
isContinueButtonEnabled = false,
|
isContinueButtonEnabled = false,
|
||||||
selectedEnvironmentType = Environment.Type.US,
|
selectedEnvironmentType = Environment.Type.US,
|
||||||
dialog = null,
|
dialog = null,
|
||||||
|
showNewOnboardingUi = false,
|
||||||
)
|
)
|
||||||
val handle = SavedStateHandle(mapOf("state" to savedState))
|
val handle = SavedStateHandle(mapOf("state" to savedState))
|
||||||
val viewModel = StartRegistrationViewModel(
|
val viewModel = StartRegistrationViewModel(
|
||||||
savedStateHandle = handle,
|
savedStateHandle = handle,
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
assertEquals(savedState, viewModel.stateFlow.value)
|
assertEquals(savedState, viewModel.stateFlow.value)
|
||||||
}
|
}
|
||||||
|
@ -100,6 +110,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
val input = "a"
|
val input = "a"
|
||||||
viewModel.trySendAction(EmailInputChange(input))
|
viewModel.trySendAction(EmailInputChange(input))
|
||||||
|
@ -125,6 +136,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
val input = " "
|
val input = " "
|
||||||
viewModel.trySendAction(EmailInputChange(input))
|
viewModel.trySendAction(EmailInputChange(input))
|
||||||
|
@ -162,6 +174,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = validInputHandle,
|
savedStateHandle = validInputHandle,
|
||||||
authRepository = repo,
|
authRepository = repo,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
turbineScope {
|
turbineScope {
|
||||||
val stateFlow = viewModel.stateFlow.testIn(backgroundScope)
|
val stateFlow = viewModel.stateFlow.testIn(backgroundScope)
|
||||||
|
@ -197,6 +210,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = validInputHandle,
|
savedStateHandle = validInputHandle,
|
||||||
authRepository = repo,
|
authRepository = repo,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.stateFlow.test {
|
viewModel.stateFlow.test {
|
||||||
assertEquals(VALID_INPUT_STATE, awaitItem())
|
assertEquals(VALID_INPUT_STATE, awaitItem())
|
||||||
|
@ -243,6 +257,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = validInputHandle,
|
savedStateHandle = validInputHandle,
|
||||||
authRepository = repo,
|
authRepository = repo,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.trySendAction(ContinueClick)
|
viewModel.trySendAction(ContinueClick)
|
||||||
|
@ -280,6 +295,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = validInputHandle,
|
savedStateHandle = validInputHandle,
|
||||||
authRepository = repo,
|
authRepository = repo,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.trySendAction(ContinueClick)
|
viewModel.trySendAction(ContinueClick)
|
||||||
|
@ -298,6 +314,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.trySendAction(BackClick)
|
viewModel.trySendAction(BackClick)
|
||||||
|
@ -311,6 +328,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.trySendAction(PrivacyPolicyClick)
|
viewModel.trySendAction(PrivacyPolicyClick)
|
||||||
|
@ -324,6 +342,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.trySendAction(TermsClick)
|
viewModel.trySendAction(TermsClick)
|
||||||
|
@ -337,6 +356,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.trySendAction(UnsubscribeMarketingEmailsClick)
|
viewModel.trySendAction(UnsubscribeMarketingEmailsClick)
|
||||||
|
@ -351,6 +371,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.stateFlow.test {
|
viewModel.stateFlow.test {
|
||||||
awaitItem()
|
awaitItem()
|
||||||
|
@ -373,6 +394,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.trySendAction(
|
viewModel.trySendAction(
|
||||||
|
@ -393,6 +415,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.trySendAction(EmailInputChange("input"))
|
viewModel.trySendAction(EmailInputChange("input"))
|
||||||
viewModel.stateFlow.test {
|
viewModel.stateFlow.test {
|
||||||
|
@ -412,6 +435,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.trySendAction(NameInputChange("input"))
|
viewModel.trySendAction(NameInputChange("input"))
|
||||||
viewModel.stateFlow.test {
|
viewModel.stateFlow.test {
|
||||||
|
@ -425,6 +449,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
viewModel.trySendAction(ReceiveMarketingEmailsToggle(false))
|
viewModel.trySendAction(ReceiveMarketingEmailsToggle(false))
|
||||||
viewModel.stateFlow.test {
|
viewModel.stateFlow.test {
|
||||||
|
@ -438,6 +463,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
savedStateHandle = SavedStateHandle(),
|
savedStateHandle = SavedStateHandle(),
|
||||||
authRepository = mockAuthRepository,
|
authRepository = mockAuthRepository,
|
||||||
environmentRepository = fakeEnvironmentRepository,
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
)
|
)
|
||||||
|
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
|
@ -446,6 +472,21 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `OnboardingFeatureFlagUpdated should update showNewOnboardingUi in state`() {
|
||||||
|
val viewModel = StartRegistrationViewModel(
|
||||||
|
savedStateHandle = SavedStateHandle(),
|
||||||
|
authRepository = mockAuthRepository,
|
||||||
|
environmentRepository = fakeEnvironmentRepository,
|
||||||
|
featureFlagManager = featureFlagManager,
|
||||||
|
)
|
||||||
|
mutableFeatureFlagFlow.value = true
|
||||||
|
assertEquals(
|
||||||
|
DEFAULT_STATE.copy(showNewOnboardingUi = true),
|
||||||
|
viewModel.stateFlow.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val EMAIL = "test@test.com"
|
private const val EMAIL = "test@test.com"
|
||||||
private const val NAME = "name"
|
private const val NAME = "name"
|
||||||
|
@ -456,6 +497,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
isContinueButtonEnabled = false,
|
isContinueButtonEnabled = false,
|
||||||
selectedEnvironmentType = Environment.Type.US,
|
selectedEnvironmentType = Environment.Type.US,
|
||||||
dialog = null,
|
dialog = null,
|
||||||
|
showNewOnboardingUi = false,
|
||||||
)
|
)
|
||||||
private val VALID_INPUT_STATE = StartRegistrationState(
|
private val VALID_INPUT_STATE = StartRegistrationState(
|
||||||
emailInput = EMAIL,
|
emailInput = EMAIL,
|
||||||
|
@ -464,6 +506,7 @@ class StartRegistrationViewModelTest : BaseViewModelTest() {
|
||||||
isContinueButtonEnabled = true,
|
isContinueButtonEnabled = true,
|
||||||
selectedEnvironmentType = Environment.Type.US,
|
selectedEnvironmentType = Environment.Type.US,
|
||||||
dialog = null,
|
dialog = null,
|
||||||
|
showNewOnboardingUi = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue