AccountDeactivationEvents -> AccountDeactivationEvent

This commit is contained in:
Benoit Marty
2026-08-25 09:52:00 +02:00
parent dff9af9ce0
commit e1e2718c54
7 changed files with 47 additions and 47 deletions
@@ -8,9 +8,9 @@
package io.element.android.features.logout.impl package io.element.android.features.logout.impl
sealed interface AccountDeactivationEvents { sealed interface AccountDeactivationEvent {
data class SetEraseData(val eraseData: Boolean) : AccountDeactivationEvents data class SetEraseData(val eraseData: Boolean) : AccountDeactivationEvent
data class SetPassword(val password: String) : AccountDeactivationEvents data class SetPassword(val password: String) : AccountDeactivationEvent
data class DeactivateAccount(val isRetry: Boolean) : AccountDeactivationEvents data class DeactivateAccount(val isRetry: Boolean) : AccountDeactivationEvent
data object CloseDialogs : AccountDeactivationEvents data object CloseDialogs : AccountDeactivationEvent
} }
@@ -34,19 +34,19 @@ class AccountDeactivationPresenter(
val formState = remember { mutableStateOf(DeactivateFormState.Default) } val formState = remember { mutableStateOf(DeactivateFormState.Default) }
fun handleEvent(event: AccountDeactivationEvents) { fun handleEvent(event: AccountDeactivationEvent) {
when (event) { when (event) {
is AccountDeactivationEvents.SetEraseData -> { is AccountDeactivationEvent.SetEraseData -> {
updateFormState(formState) { updateFormState(formState) {
copy(eraseData = event.eraseData) copy(eraseData = event.eraseData)
} }
} }
is AccountDeactivationEvents.SetPassword -> { is AccountDeactivationEvent.SetPassword -> {
updateFormState(formState) { updateFormState(formState) {
copy(password = event.password) copy(password = event.password)
} }
} }
is AccountDeactivationEvents.DeactivateAccount -> is AccountDeactivationEvent.DeactivateAccount ->
if (action.value.isConfirming() || event.isRetry) { if (action.value.isConfirming() || event.isRetry) {
localCoroutineScope.deactivateAccount( localCoroutineScope.deactivateAccount(
formState = formState.value, formState = formState.value,
@@ -55,7 +55,7 @@ class AccountDeactivationPresenter(
} else { } else {
action.value = AsyncAction.ConfirmingNoParams action.value = AsyncAction.ConfirmingNoParams
} }
AccountDeactivationEvents.CloseDialogs -> { AccountDeactivationEvent.CloseDialogs -> {
action.value = AsyncAction.Uninitialized action.value = AsyncAction.Uninitialized
} }
} }
@@ -15,7 +15,7 @@ import kotlinx.parcelize.Parcelize
data class AccountDeactivationState( data class AccountDeactivationState(
val deactivateFormState: DeactivateFormState, val deactivateFormState: DeactivateFormState,
val accountDeactivationAction: AsyncAction<Unit>, val accountDeactivationAction: AsyncAction<Unit>,
val eventSink: (AccountDeactivationEvents) -> Unit, val eventSink: (AccountDeactivationEvent) -> Unit,
) { ) {
val submitEnabled: Boolean val submitEnabled: Boolean
get() = accountDeactivationAction is AsyncAction.Uninitialized && get() = accountDeactivationAction is AsyncAction.Uninitialized &&
@@ -45,7 +45,7 @@ internal fun aDeactivateFormState(
internal fun anAccountDeactivationState( internal fun anAccountDeactivationState(
deactivateFormState: DeactivateFormState = aDeactivateFormState(), deactivateFormState: DeactivateFormState = aDeactivateFormState(),
accountDeactivationAction: AsyncAction<Unit> = AsyncAction.Uninitialized, accountDeactivationAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
eventSink: (AccountDeactivationEvents) -> Unit = {}, eventSink: (AccountDeactivationEvent) -> Unit = {},
) = AccountDeactivationState( ) = AccountDeactivationState(
deactivateFormState = deactivateFormState, deactivateFormState = deactivateFormState,
accountDeactivationAction = accountDeactivationAction, accountDeactivationAction = accountDeactivationAction,
@@ -102,14 +102,14 @@ fun AccountDeactivationView(
Content( Content(
state = state, state = state,
onSubmitClick = { onSubmitClick = {
eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
} }
) )
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(32.dp))
Buttons( Buttons(
state = state, state = state,
onSubmitClick = { onSubmitClick = {
eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
} }
) )
} }
@@ -117,13 +117,13 @@ fun AccountDeactivationView(
AccountDeactivationActionDialog( AccountDeactivationActionDialog(
state.accountDeactivationAction, state.accountDeactivationAction,
onConfirmClick = { onConfirmClick = {
eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
}, },
onRetryClick = { onRetryClick = {
eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = true)) eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = true))
}, },
onDismissDialog = { onDismissDialog = {
eventSink(AccountDeactivationEvents.CloseDialogs) eventSink(AccountDeactivationEvent.CloseDialogs)
}, },
) )
} }
@@ -239,7 +239,7 @@ private fun Content(
headline = stringResource(R.string.screen_deactivate_account_delete_all_messages), headline = stringResource(R.string.screen_deactivate_account_delete_all_messages),
value = eraseData, value = eraseData,
onChange = { onChange = {
eventSink(AccountDeactivationEvents.SetEraseData(it)) eventSink(AccountDeactivationEvent.SetEraseData(it))
}, },
enabled = !isLoading, enabled = !isLoading,
) )
@@ -275,7 +275,7 @@ private fun Content(
onValueChange = { onValueChange = {
val sanitized = it.sanitize() val sanitized = it.sanitize()
passwordFieldState = sanitized passwordFieldState = sanitized
eventSink(AccountDeactivationEvents.SetPassword(sanitized)) eventSink(AccountDeactivationEvent.SetPassword(sanitized))
}, },
placeholder = stringResource(CommonStrings.common_password), placeholder = stringResource(CommonStrings.common_password),
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
@@ -47,11 +47,11 @@ class AccountDeactivationPresenterTest {
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.deactivateFormState).isEqualTo(DeactivateFormState.Default) assertThat(initialState.deactivateFormState).isEqualTo(DeactivateFormState.Default)
initialState.eventSink(AccountDeactivationEvents.SetEraseData(true)) initialState.eventSink(AccountDeactivationEvent.SetEraseData(true))
val updatedState = awaitItem() val updatedState = awaitItem()
assertThat(updatedState.deactivateFormState).isEqualTo(DeactivateFormState.Default.copy(eraseData = true)) assertThat(updatedState.deactivateFormState).isEqualTo(DeactivateFormState.Default.copy(eraseData = true))
assertThat(updatedState.submitEnabled).isFalse() assertThat(updatedState.submitEnabled).isFalse()
updatedState.eventSink(AccountDeactivationEvents.SetPassword("password")) updatedState.eventSink(AccountDeactivationEvent.SetPassword("password"))
val updatedState2 = awaitItem() val updatedState2 = awaitItem()
assertThat(updatedState2.deactivateFormState).isEqualTo(DeactivateFormState(password = "password", eraseData = true)) assertThat(updatedState2.deactivateFormState).isEqualTo(DeactivateFormState(password = "password", eraseData = true))
assertThat(updatedState2.submitEnabled).isTrue() assertThat(updatedState2.submitEnabled).isTrue()
@@ -71,12 +71,12 @@ class AccountDeactivationPresenterTest {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(AccountDeactivationEvents.SetPassword("password")) initialState.eventSink(AccountDeactivationEvent.SetPassword("password"))
skipItems(1) skipItems(1)
initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) initialState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
val updatedState = awaitItem() val updatedState = awaitItem()
assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams) assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams)
updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) updatedState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
val updatedState2 = awaitItem() val updatedState2 = awaitItem()
assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading)
val finalState = awaitItem() val finalState = awaitItem()
@@ -98,20 +98,20 @@ class AccountDeactivationPresenterTest {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(AccountDeactivationEvents.SetPassword("password")) initialState.eventSink(AccountDeactivationEvent.SetPassword("password"))
initialState.eventSink(AccountDeactivationEvents.SetEraseData(true)) initialState.eventSink(AccountDeactivationEvent.SetEraseData(true))
skipItems(2) skipItems(2)
initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) initialState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
val updatedState = awaitItem() val updatedState = awaitItem()
assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams) assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams)
updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) updatedState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
val updatedState2 = awaitItem() val updatedState2 = awaitItem()
assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading)
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) assertThat(finalState.accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
recorder.assertions().isCalledOnce().with(value("password"), value(true)) recorder.assertions().isCalledOnce().with(value("password"), value(true))
// Retry // Retry
finalState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = true)) finalState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = true))
val finalState2 = awaitItem() val finalState2 = awaitItem()
assertThat(finalState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) assertThat(finalState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading)
assertThat(awaitItem().accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) assertThat(awaitItem().accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
@@ -131,20 +131,20 @@ class AccountDeactivationPresenterTest {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(AccountDeactivationEvents.SetPassword("password")) initialState.eventSink(AccountDeactivationEvent.SetPassword("password"))
initialState.eventSink(AccountDeactivationEvents.SetEraseData(true)) initialState.eventSink(AccountDeactivationEvent.SetEraseData(true))
skipItems(2) skipItems(2)
initialState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) initialState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
val updatedState = awaitItem() val updatedState = awaitItem()
assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams) assertThat(updatedState.accountDeactivationAction).isEqualTo(AsyncAction.ConfirmingNoParams)
updatedState.eventSink(AccountDeactivationEvents.DeactivateAccount(isRetry = false)) updatedState.eventSink(AccountDeactivationEvent.DeactivateAccount(isRetry = false))
val updatedState2 = awaitItem() val updatedState2 = awaitItem()
assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading) assertThat(updatedState2.accountDeactivationAction).isEqualTo(AsyncAction.Loading)
val finalState = awaitItem() val finalState = awaitItem()
assertThat(finalState.accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) assertThat(finalState.accountDeactivationAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
recorder.assertions().isCalledOnce().with(value("password"), value(true)) recorder.assertions().isCalledOnce().with(value("password"), value(true))
// Cancel // Cancel
finalState.eventSink(AccountDeactivationEvents.CloseDialogs) finalState.eventSink(AccountDeactivationEvent.CloseDialogs)
val finalState2 = awaitItem() val finalState2 = awaitItem()
assertThat(finalState2.accountDeactivationAction).isEqualTo(AsyncAction.Uninitialized) assertThat(finalState2.accountDeactivationAction).isEqualTo(AsyncAction.Uninitialized)
} }
@@ -35,7 +35,7 @@ import org.robolectric.annotation.Config
class AccountDeactivationViewTest : RobolectricTest() { class AccountDeactivationViewTest : RobolectricTest() {
@Test @Test
fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>(expectEvents = false) val eventsRecorder = EventsRecorder<AccountDeactivationEvent>(expectEvents = false)
ensureCalledOnce { ensureCalledOnce {
setAccountDeactivationView( setAccountDeactivationView(
state = anAccountDeactivationState(eventSink = eventsRecorder), state = anAccountDeactivationState(eventSink = eventsRecorder),
@@ -48,7 +48,7 @@ class AccountDeactivationViewTest : RobolectricTest() {
@Config(qualifiers = "h1024dp") @Config(qualifiers = "h1024dp")
@Test @Test
fun `clicking on Deactivate emits the expected Event`() = runAndroidComposeUiTest { fun `clicking on Deactivate emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>() val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
setAccountDeactivationView( setAccountDeactivationView(
state = anAccountDeactivationState( state = anAccountDeactivationState(
deactivateFormState = aDeactivateFormState( deactivateFormState = aDeactivateFormState(
@@ -58,12 +58,12 @@ class AccountDeactivationViewTest : RobolectricTest() {
), ),
) )
clickOn(CommonStrings.action_delete) clickOn(CommonStrings.action_delete)
eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(false)) eventsRecorder.assertSingle(AccountDeactivationEvent.DeactivateAccount(false))
} }
@Test @Test
fun `clicking on Deactivate on the confirmation dialog emits the expected Event`() = runAndroidComposeUiTest { fun `clicking on Deactivate on the confirmation dialog emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>() val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
setAccountDeactivationView( setAccountDeactivationView(
state = anAccountDeactivationState( state = anAccountDeactivationState(
deactivateFormState = aDeactivateFormState( deactivateFormState = aDeactivateFormState(
@@ -74,12 +74,12 @@ class AccountDeactivationViewTest : RobolectricTest() {
), ),
) )
pressTag(TestTags.dialogPositive.value) pressTag(TestTags.dialogPositive.value)
eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(false)) eventsRecorder.assertSingle(AccountDeactivationEvent.DeactivateAccount(false))
} }
@Test @Test
fun `clicking on retry on the confirmation dialog emits the expected Event`() = runAndroidComposeUiTest { fun `clicking on retry on the confirmation dialog emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>() val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
setAccountDeactivationView( setAccountDeactivationView(
state = anAccountDeactivationState( state = anAccountDeactivationState(
deactivateFormState = aDeactivateFormState( deactivateFormState = aDeactivateFormState(
@@ -90,24 +90,24 @@ class AccountDeactivationViewTest : RobolectricTest() {
), ),
) )
clickOn(CommonStrings.action_retry) clickOn(CommonStrings.action_retry)
eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(true)) eventsRecorder.assertSingle(AccountDeactivationEvent.DeactivateAccount(true))
} }
@Test @Test
fun `switching on the erase all switch emits the expected Event`() = runAndroidComposeUiTest { fun `switching on the erase all switch emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>() val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
setAccountDeactivationView( setAccountDeactivationView(
state = anAccountDeactivationState( state = anAccountDeactivationState(
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
) )
clickOn(R.string.screen_deactivate_account_delete_all_messages) clickOn(R.string.screen_deactivate_account_delete_all_messages)
eventsRecorder.assertSingle(AccountDeactivationEvents.SetEraseData(true)) eventsRecorder.assertSingle(AccountDeactivationEvent.SetEraseData(true))
} }
@Test @Test
fun `switching off the erase all switch emits the expected Event`() = runAndroidComposeUiTest { fun `switching off the erase all switch emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>() val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
setAccountDeactivationView( setAccountDeactivationView(
state = anAccountDeactivationState( state = anAccountDeactivationState(
deactivateFormState = aDeactivateFormState( deactivateFormState = aDeactivateFormState(
@@ -117,13 +117,13 @@ class AccountDeactivationViewTest : RobolectricTest() {
), ),
) )
clickOn(R.string.screen_deactivate_account_delete_all_messages) clickOn(R.string.screen_deactivate_account_delete_all_messages)
eventsRecorder.assertSingle(AccountDeactivationEvents.SetEraseData(false)) eventsRecorder.assertSingle(AccountDeactivationEvent.SetEraseData(false))
} }
@Config(qualifiers = "h1024dp") @Config(qualifiers = "h1024dp")
@Test @Test
fun `typing text in the password field emits the expected Event`() = runAndroidComposeUiTest { fun `typing text in the password field emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>() val eventsRecorder = EventsRecorder<AccountDeactivationEvent>()
setAccountDeactivationView( setAccountDeactivationView(
state = anAccountDeactivationState( state = anAccountDeactivationState(
deactivateFormState = aDeactivateFormState( deactivateFormState = aDeactivateFormState(
@@ -133,7 +133,7 @@ class AccountDeactivationViewTest : RobolectricTest() {
), ),
) )
onNodeWithTag(TestTags.loginPassword.value).performTextInput("A") onNodeWithTag(TestTags.loginPassword.value).performTextInput("A")
eventsRecorder.assertSingle(AccountDeactivationEvents.SetPassword("A$A_PASSWORD")) eventsRecorder.assertSingle(AccountDeactivationEvent.SetPassword("A$A_PASSWORD"))
} }
} }