Merge pull request #3359 from element-hq/feature/bma/signOutOnIdentityConfirmation

Add a way to sign out when the user is asked to verify the session.
This commit is contained in:
Benoit Marty 2024-08-30 16:42:23 +02:00 committed by GitHub
commit a74dde96e0
19 changed files with 145 additions and 23 deletions

View file

@ -43,6 +43,7 @@ dependencies {
implementation(projects.libraries.designsystem) implementation(projects.libraries.designsystem)
implementation(projects.libraries.preferences.api) implementation(projects.libraries.preferences.api)
implementation(projects.libraries.uiStrings) implementation(projects.libraries.uiStrings)
implementation(projects.features.logout.api)
api(libs.statemachine) api(libs.statemachine)
api(projects.features.verifysession.api) api(projects.features.verifysession.api)
@ -52,6 +53,7 @@ dependencies {
testImplementation(libs.test.robolectric) testImplementation(libs.test.robolectric)
testImplementation(libs.test.truth) testImplementation(libs.test.truth)
testImplementation(libs.test.turbine) testImplementation(libs.test.turbine)
testImplementation(projects.features.logout.test)
testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.preferences.test)
testImplementation(projects.tests.testutils) testImplementation(projects.tests.testutils)

View file

@ -16,8 +16,10 @@
package io.element.android.features.verifysession.impl package io.element.android.features.verifysession.impl
import android.app.Activity
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.Plugin
@ -25,6 +27,8 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.logout.api.util.onSuccessLogout
import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.features.verifysession.api.VerifySessionEntryPoint
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@ -39,12 +43,15 @@ class VerifySelfSessionNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
val activity = LocalContext.current as Activity
val isDark = ElementTheme.isLightTheme.not()
VerifySelfSessionView( VerifySelfSessionView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onEnterRecoveryKey = callback::onEnterRecoveryKey, onEnterRecoveryKey = callback::onEnterRecoveryKey,
onResetKey = callback::onResetKey, onResetKey = callback::onResetKey,
onFinish = callback::onDone, onFinish = callback::onDone,
onSuccessLogout = { onSuccessLogout(activity, isDark, it) },
) )
} }
} }

View file

@ -20,14 +20,19 @@ package io.element.android.features.verifysession.impl
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import com.freeletics.flowredux.compose.rememberStateAndDispatch import com.freeletics.flowredux.compose.rememberStateAndDispatch
import io.element.android.features.logout.api.LogoutUseCase
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.encryption.RecoveryState
@ -49,6 +54,7 @@ class VerifySelfSessionPresenter @Inject constructor(
private val stateMachine: VerifySelfSessionStateMachine, private val stateMachine: VerifySelfSessionStateMachine,
private val buildMeta: BuildMeta, private val buildMeta: BuildMeta,
private val sessionPreferencesStore: SessionPreferencesStore, private val sessionPreferencesStore: SessionPreferencesStore,
private val logoutUseCase: LogoutUseCase,
) : Presenter<VerifySelfSessionState> { ) : Presenter<VerifySelfSessionState> {
@Composable @Composable
override fun present(): VerifySelfSessionState { override fun present(): VerifySelfSessionState {
@ -61,6 +67,9 @@ class VerifySelfSessionPresenter @Inject constructor(
val stateAndDispatch = stateMachine.rememberStateAndDispatch() val stateAndDispatch = stateMachine.rememberStateAndDispatch()
val skipVerification by sessionPreferencesStore.isSessionVerificationSkipped().collectAsState(initial = false) val skipVerification by sessionPreferencesStore.isSessionVerificationSkipped().collectAsState(initial = false)
val needsVerification by sessionVerificationService.needsSessionVerification.collectAsState(initial = true) val needsVerification by sessionVerificationService.needsSessionVerification.collectAsState(initial = true)
val signOutAction = remember {
mutableStateOf<AsyncAction<String?>>(AsyncAction.Uninitialized)
}
val verificationFlowStep by remember { val verificationFlowStep by remember {
derivedStateOf { derivedStateOf {
when { when {
@ -85,6 +94,7 @@ class VerifySelfSessionPresenter @Inject constructor(
VerifySelfSessionViewEvents.DeclineVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.DeclineChallenge) VerifySelfSessionViewEvents.DeclineVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.DeclineChallenge)
VerifySelfSessionViewEvents.Cancel -> stateAndDispatch.dispatchAction(StateMachineEvent.Cancel) VerifySelfSessionViewEvents.Cancel -> stateAndDispatch.dispatchAction(StateMachineEvent.Cancel)
VerifySelfSessionViewEvents.Reset -> stateAndDispatch.dispatchAction(StateMachineEvent.Reset) VerifySelfSessionViewEvents.Reset -> stateAndDispatch.dispatchAction(StateMachineEvent.Reset)
VerifySelfSessionViewEvents.SignOut -> coroutineScope.signOut(signOutAction)
VerifySelfSessionViewEvents.SkipVerification -> coroutineScope.launch { VerifySelfSessionViewEvents.SkipVerification -> coroutineScope.launch {
sessionPreferencesStore.setSkipSessionVerification(true) sessionPreferencesStore.setSkipSessionVerification(true)
} }
@ -92,6 +102,7 @@ class VerifySelfSessionPresenter @Inject constructor(
} }
return VerifySelfSessionState( return VerifySelfSessionState(
verificationFlowStep = verificationFlowStep, verificationFlowStep = verificationFlowStep,
signOutAction = signOutAction.value,
displaySkipButton = buildMeta.isDebuggable, displaySkipButton = buildMeta.isDebuggable,
eventSink = ::handleEvents, eventSink = ::handleEvents,
) )
@ -160,4 +171,10 @@ class VerifySelfSessionPresenter @Inject constructor(
} }
}.launchIn(this) }.launchIn(this)
} }
private fun CoroutineScope.signOut(signOutAction: MutableState<AsyncAction<String?>>) = launch {
suspend {
logoutUseCase.logout(ignoreSdkError = true)
}.runCatchingUpdatingState(signOutAction)
}
} }

View file

@ -18,12 +18,14 @@ package io.element.android.features.verifysession.impl
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.SessionVerificationData
@Immutable @Immutable
data class VerifySelfSessionState( data class VerifySelfSessionState(
val verificationFlowStep: VerificationStep, val verificationFlowStep: VerificationStep,
val signOutAction: AsyncAction<String?>,
val displaySkipButton: Boolean, val displaySkipButton: Boolean,
val eventSink: (VerifySelfSessionViewEvents) -> Unit, val eventSink: (VerifySelfSessionViewEvents) -> Unit,
) { ) {

View file

@ -18,6 +18,7 @@ package io.element.android.features.verifysession.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.SessionVerificationData
import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.matrix.api.verification.VerificationEmoji
@ -54,6 +55,10 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider<VerifySelfS
verificationFlowStep = VerificationStep.Completed, verificationFlowStep = VerificationStep.Completed,
displaySkipButton = true, displaySkipButton = true,
), ),
aVerifySelfSessionState(
signOutAction = AsyncAction.Loading,
displaySkipButton = true,
),
// Add other state here // Add other state here
) )
} }
@ -72,12 +77,14 @@ private fun aDecimalsSessionVerificationData(
internal fun aVerifySelfSessionState( internal fun aVerifySelfSessionState(
verificationFlowStep: VerificationStep = VerificationStep.Initial(canEnterRecoveryKey = false), verificationFlowStep: VerificationStep = VerificationStep.Initial(canEnterRecoveryKey = false),
signOutAction: AsyncAction<String?> = AsyncAction.Uninitialized,
displaySkipButton: Boolean = false, displaySkipButton: Boolean = false,
eventSink: (VerifySelfSessionViewEvents) -> Unit = {}, eventSink: (VerifySelfSessionViewEvents) -> Unit = {},
) = VerifySelfSessionState( ) = VerifySelfSessionState(
verificationFlowStep = verificationFlowStep, verificationFlowStep = verificationFlowStep,
displaySkipButton = displaySkipButton, displaySkipButton = displaySkipButton,
eventSink = eventSink, eventSink = eventSink,
signOutAction = signOutAction,
) )
private fun aVerificationEmojiList() = listOf( private fun aVerificationEmojiList() = listOf(

View file

@ -37,6 +37,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@ -46,11 +47,13 @@ import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.verifysession.impl.emoji.toEmojiResource import io.element.android.features.verifysession.impl.emoji.toEmojiResource
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.BigIcon
import io.element.android.libraries.designsystem.components.PageTitle import io.element.android.libraries.designsystem.components.PageTitle
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Button
@ -70,11 +73,13 @@ fun VerifySelfSessionView(
onEnterRecoveryKey: () -> Unit, onEnterRecoveryKey: () -> Unit,
onResetKey: () -> Unit, onResetKey: () -> Unit,
onFinish: () -> Unit, onFinish: () -> Unit,
onSuccessLogout: (String?) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
fun resetFlow() { fun resetFlow() {
state.eventSink(VerifySelfSessionViewEvents.Reset) state.eventSink(VerifySelfSessionViewEvents.Reset)
} }
val latestOnFinish by rememberUpdatedState(newValue = onFinish) val latestOnFinish by rememberUpdatedState(newValue = onFinish)
LaunchedEffect(state.verificationFlowStep, latestOnFinish) { LaunchedEffect(state.verificationFlowStep, latestOnFinish) {
if (state.verificationFlowStep is FlowStep.Skipped) { if (state.verificationFlowStep is FlowStep.Skipped) {
@ -97,17 +102,25 @@ fun VerifySelfSessionView(
HeaderFooterPage( HeaderFooterPage(
modifier = modifier, modifier = modifier,
topBar = { topBar = {
TopAppBar( TopAppBar(
title = {}, title = {},
actions = { actions = {
if (state.displaySkipButton && state.verificationFlowStep != FlowStep.Completed) { if (state.verificationFlowStep != FlowStep.Completed &&
TextButton( state.displaySkipButton &&
text = stringResource(CommonStrings.action_skip), LocalInspectionMode.current.not()) {
onClick = { state.eventSink(VerifySelfSessionViewEvents.SkipVerification) } TextButton(
) text = stringResource(CommonStrings.action_skip),
} onClick = { state.eventSink(VerifySelfSessionViewEvents.SkipVerification) }
} )
) }
if (state.verificationFlowStep is FlowStep.Initial) {
TextButton(
text = stringResource(CommonStrings.action_signout),
onClick = { state.eventSink(VerifySelfSessionViewEvents.SignOut) }
)
}
}
)
}, },
header = { header = {
HeaderContent(verificationFlowStep = verificationFlowStep) HeaderContent(verificationFlowStep = verificationFlowStep)
@ -124,6 +137,21 @@ fun VerifySelfSessionView(
) { ) {
Content(flowState = verificationFlowStep) Content(flowState = verificationFlowStep)
} }
when (state.signOutAction) {
AsyncAction.Loading -> {
ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content))
}
is AsyncAction.Success -> {
val latestOnSuccessLogout by rememberUpdatedState(onSuccessLogout)
LaunchedEffect(state) {
latestOnSuccessLogout(state.signOutAction.data)
}
}
AsyncAction.Confirming,
is AsyncAction.Failure,
AsyncAction.Uninitialized -> Unit
}
} }
@Composable @Composable
@ -367,5 +395,6 @@ internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionSta
onEnterRecoveryKey = {}, onEnterRecoveryKey = {},
onResetKey = {}, onResetKey = {},
onFinish = {}, onFinish = {},
onSuccessLogout = {},
) )
} }

View file

@ -23,5 +23,6 @@ sealed interface VerifySelfSessionViewEvents {
data object DeclineVerification : VerifySelfSessionViewEvents data object DeclineVerification : VerifySelfSessionViewEvents
data object Cancel : VerifySelfSessionViewEvents data object Cancel : VerifySelfSessionViewEvents
data object Reset : VerifySelfSessionViewEvents data object Reset : VerifySelfSessionViewEvents
data object SignOut : VerifySelfSessionViewEvents
data object SkipVerification : VerifySelfSessionViewEvents data object SkipVerification : VerifySelfSessionViewEvents
} }

View file

@ -28,4 +28,5 @@
<string name="screen_session_verification_they_match">"They match"</string> <string name="screen_session_verification_they_match">"They match"</string>
<string name="screen_session_verification_waiting_to_accept_subtitle">"Accept the request to start the verification process in your other session to continue."</string> <string name="screen_session_verification_waiting_to_accept_subtitle">"Accept the request to start the verification process in your other session to continue."</string>
<string name="screen_session_verification_waiting_to_accept_title">"Waiting to accept request"</string> <string name="screen_session_verification_waiting_to_accept_title">"Waiting to accept request"</string>
<string name="screen_signout_in_progress_dialog_content">"Signing out…"</string>
</resources> </resources>

View file

@ -21,6 +21,8 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.ReceiveTurbine import app.cash.turbine.ReceiveTurbine
import app.cash.turbine.test import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.logout.api.LogoutUseCase
import io.element.android.features.logout.test.FakeLogoutUseCase
import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
@ -36,6 +38,8 @@ import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
@ -309,6 +313,31 @@ class VerifySelfSessionPresenterTest {
} }
} }
@Test
fun `present - When user request to sign out, the sign out use case is invoked`() = runTest {
val service = FakeSessionVerificationService().apply {
givenNeedsSessionVerification(false)
givenVerifiedStatus(SessionVerifiedStatus.Verified)
givenVerificationFlowState(VerificationFlowState.Finished)
}
val signOutLambda = lambdaRecorder<Boolean, String?> { "aUrl" }
val presenter = createVerifySelfSessionPresenter(
service,
logoutUseCase = FakeLogoutUseCase(signOutLambda)
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val initialItem = awaitItem()
initialItem.eventSink(VerifySelfSessionViewEvents.SignOut)
val finalItem = awaitItem()
assertThat(finalItem.signOutAction.isSuccess()).isTrue()
assertThat(finalItem.signOutAction.dataOrNull()).isEqualTo("aUrl")
signOutLambda.assertions().isCalledOnce().with(value(true))
}
}
private suspend fun ReceiveTurbine<VerifySelfSessionState>.requestVerificationAndAwaitVerifyingState( private suspend fun ReceiveTurbine<VerifySelfSessionState>.requestVerificationAndAwaitVerifyingState(
fakeService: FakeSessionVerificationService, fakeService: FakeSessionVerificationService,
sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()), sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()),
@ -344,6 +373,7 @@ class VerifySelfSessionPresenterTest {
encryptionService: EncryptionService = FakeEncryptionService(), encryptionService: EncryptionService = FakeEncryptionService(),
buildMeta: BuildMeta = aBuildMeta(), buildMeta: BuildMeta = aBuildMeta(),
sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(), sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(),
logoutUseCase: LogoutUseCase = FakeLogoutUseCase(),
): VerifySelfSessionPresenter { ): VerifySelfSessionPresenter {
return VerifySelfSessionPresenter( return VerifySelfSessionPresenter(
sessionVerificationService = service, sessionVerificationService = service,
@ -351,6 +381,7 @@ class VerifySelfSessionPresenterTest {
stateMachine = VerifySelfSessionStateMachine(service, encryptionService), stateMachine = VerifySelfSessionStateMachine(service, encryptionService),
buildMeta = buildMeta, buildMeta = buildMeta,
sessionPreferencesStore = sessionPreferencesStore, sessionPreferencesStore = sessionPreferencesStore,
logoutUseCase = logoutUseCase,
) )
} }
} }

View file

@ -20,6 +20,7 @@ import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EnsureNeverCalled
@ -27,6 +28,7 @@ import io.element.android.tests.testutils.EnsureNeverCalledWithParam
import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.ensureCalledOnceWithParam
import io.element.android.tests.testutils.pressBackKey import io.element.android.tests.testutils.pressBackKey
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -213,11 +215,26 @@ class VerifySelfSessionViewTest {
} }
} }
@Test
fun `on success logout - onFinished callback is called immediately`() {
val aUrl = "aUrl"
ensureCalledOnceWithParam<String?>(aUrl) { callback ->
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
signOutAction = AsyncAction.Success(aUrl),
eventSink = EnsureNeverCalledWithParam(),
),
onSuccessLogout = callback,
)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setVerifySelfSessionView( private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setVerifySelfSessionView(
state: VerifySelfSessionState, state: VerifySelfSessionState,
onEnterRecoveryKey: () -> Unit = EnsureNeverCalled(), onEnterRecoveryKey: () -> Unit = EnsureNeverCalled(),
onFinished: () -> Unit = EnsureNeverCalled(), onFinished: () -> Unit = EnsureNeverCalled(),
onResetKey: () -> Unit = EnsureNeverCalled(), onResetKey: () -> Unit = EnsureNeverCalled(),
onSuccessLogout: (String?) -> Unit = EnsureNeverCalledWithParam(),
) { ) {
setContent { setContent {
VerifySelfSessionView( VerifySelfSessionView(
@ -225,6 +242,7 @@ class VerifySelfSessionViewTest {
onEnterRecoveryKey = onEnterRecoveryKey, onEnterRecoveryKey = onEnterRecoveryKey,
onFinish = onFinished, onFinish = onFinished,
onResetKey = onResetKey, onResetKey = onResetKey,
onSuccessLogout = onSuccessLogout,
) )
} }
} }

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:711074ce017e2909ae1cb610925bf7be96e182095cb08de5ca1d57b4dd206c92 oid sha256:205fc4655fed7b11ef133e0df13b8175676ff38d398fde45a2e6a3b140a216e2
size 30834 size 31440

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1f7797415de4a52095a47dc7beb5b51c7528a90e9c8daf5e4d3eff030b22f3f7
size 32596

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c242503ced2d16da2fd8c738e198689b60d00d40d423b52820aa01a622acd39a oid sha256:205fc4655fed7b11ef133e0df13b8175676ff38d398fde45a2e6a3b140a216e2
size 29695 size 31440

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d8d6fedd34ea54f9492bdc74f784aac902717328e3eda83f97d06e1eef970424 oid sha256:2a8350fe244b42863ee1fb60c403aa2c695a5152755f73bf27598e71e033ef0b
size 24201 size 25962

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:df4be3e3577698e939dc02ebc0819768c04525a6b6cc67386e33501610dff5b2 oid sha256:4307ad367eb24d8f7590dfadf4e4b728ec4845ba4720b72824f5b2c60175f8fd
size 29959 size 30555

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a66446c1dc2ff1e32fd06ee722aaaed578369e6de0a0024d435104bcadc47d49
size 31012

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a229523ad1f17f957adaffb47e8a64679bc3636ab94641bc492f98b7f5d3fe3d oid sha256:4307ad367eb24d8f7590dfadf4e4b728ec4845ba4720b72824f5b2c60175f8fd
size 28861 size 30555

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b090b019f8d0a98b9e0ec4e4bd53628af15737eb441f36f40ba4abceb4c75608 oid sha256:b0da42a76838a54311a5ea6ec54725f3bb9c91c4ff7bc09ff571d6cd7b6796e1
size 23622 size 25369

View file

@ -55,6 +55,7 @@
"name" : ":features:verifysession:impl", "name" : ":features:verifysession:impl",
"includeRegex" : [ "includeRegex" : [
"screen_session_verification_.*", "screen_session_verification_.*",
"screen_signout_in_progress_dialog_content",
"screen_identity_.*" "screen_identity_.*"
] ]
}, },