mirror of
https://github.com/bitwarden/android.git
synced 2026-05-01 04:38:52 -05:00
PM-21397: Create initial View Send scaffold (#5163)
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
package com.x8bit.bitwarden.ui.tools.feature.send.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents different types of sends that can be added/viewed.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
enum class SendItemType {
|
||||||
|
FILE,
|
||||||
|
TEXT,
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.x8bit.bitwarden.ui.tools.feature.send.viewsend
|
||||||
|
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavOptions
|
||||||
|
import androidx.navigation.toRoute
|
||||||
|
import com.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||||
|
import com.x8bit.bitwarden.ui.tools.feature.send.model.SendItemType
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type-safe route for the view send screen.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class ViewSendRoute(
|
||||||
|
val sendId: String,
|
||||||
|
val sendType: SendItemType,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to retrieve vault item arguments from the [SavedStateHandle].
|
||||||
|
*/
|
||||||
|
data class ViewSendArgs(
|
||||||
|
val sendId: String,
|
||||||
|
val sendType: SendItemType,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a [ViewSendArgs] from the [SavedStateHandle] and internal route data.
|
||||||
|
*/
|
||||||
|
fun SavedStateHandle.toViewSendArgs(): ViewSendArgs {
|
||||||
|
val route = this.toRoute<ViewSendRoute>()
|
||||||
|
return ViewSendArgs(sendId = route.sendId, sendType = route.sendType)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the view send screen to the nav graph.
|
||||||
|
*/
|
||||||
|
fun NavGraphBuilder.viewSendDestination(
|
||||||
|
onNavigateBack: () -> Unit,
|
||||||
|
onNavigateToEditSend: (sendId: String) -> Unit,
|
||||||
|
) {
|
||||||
|
composableWithSlideTransitions<ViewSendRoute> {
|
||||||
|
ViewSendScreen(
|
||||||
|
onNavigateBack = onNavigateBack,
|
||||||
|
onNavigateToEditSend = onNavigateToEditSend,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the view send screen.
|
||||||
|
*/
|
||||||
|
fun NavController.navigateToViewSend(
|
||||||
|
route: ViewSendRoute,
|
||||||
|
navOptions: NavOptions? = null,
|
||||||
|
) {
|
||||||
|
this.navigate(route = route, navOptions = navOptions)
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package com.x8bit.bitwarden.ui.tools.feature.send.viewsend
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.scaleIn
|
||||||
|
import androidx.compose.animation.scaleOut
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.x8bit.bitwarden.R
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.appbar.NavigationIcon
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenLoadingContent
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.fab.BitwardenFloatingActionButton
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays view send screen.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ViewSendScreen(
|
||||||
|
viewModel: ViewSendViewModel = hiltViewModel(),
|
||||||
|
onNavigateBack: () -> Unit,
|
||||||
|
onNavigateToEditSend: (sendId: String) -> Unit,
|
||||||
|
) {
|
||||||
|
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
EventsEffect(viewModel = viewModel) { event ->
|
||||||
|
when (event) {
|
||||||
|
is ViewSendEvent.NavigateBack -> onNavigateBack()
|
||||||
|
is ViewSendEvent.NavigateToEdit -> onNavigateToEditSend(event.sendId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
BitwardenScaffold(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
topBar = {
|
||||||
|
BitwardenTopAppBar(
|
||||||
|
title = state.screenDisplayName(),
|
||||||
|
navigationIcon = NavigationIcon(
|
||||||
|
navigationIcon = rememberVectorPainter(id = R.drawable.ic_close),
|
||||||
|
navigationIconContentDescription = stringResource(id = R.string.close),
|
||||||
|
onNavigationIconClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(ViewSendAction.CloseClick) }
|
||||||
|
},
|
||||||
|
),
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = state.isFabVisible,
|
||||||
|
enter = scaleIn(),
|
||||||
|
exit = scaleOut(),
|
||||||
|
) {
|
||||||
|
BitwardenFloatingActionButton(
|
||||||
|
onClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(ViewSendAction.EditClick) }
|
||||||
|
},
|
||||||
|
painter = rememberVectorPainter(id = R.drawable.ic_pencil),
|
||||||
|
contentDescription = stringResource(id = R.string.edit_send),
|
||||||
|
modifier = Modifier.testTag(tag = "EditItemButton"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
ViewSendScreenContent(
|
||||||
|
state = state,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ViewSendScreenContent(
|
||||||
|
state: ViewSendState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
when (val viewState = state.viewState) {
|
||||||
|
ViewSendState.ViewState.Content -> {
|
||||||
|
// TODO: Build out the UI (PM-21135)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ViewSendState.ViewState.Error -> {
|
||||||
|
BitwardenErrorContent(
|
||||||
|
message = viewState.message(),
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewSendState.ViewState.Loading -> {
|
||||||
|
BitwardenLoadingContent(modifier = modifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package com.x8bit.bitwarden.ui.tools.feature.send.viewsend
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
import com.bitwarden.ui.platform.base.BaseViewModel
|
||||||
|
import com.bitwarden.ui.util.Text
|
||||||
|
import com.bitwarden.ui.util.asText
|
||||||
|
import com.x8bit.bitwarden.R
|
||||||
|
import com.x8bit.bitwarden.ui.tools.feature.send.model.SendItemType
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val KEY_STATE = "state"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View model for the view send screen.
|
||||||
|
*/
|
||||||
|
@HiltViewModel
|
||||||
|
class ViewSendViewModel @Inject constructor(
|
||||||
|
savedStateHandle: SavedStateHandle,
|
||||||
|
) : BaseViewModel<ViewSendState, ViewSendEvent, ViewSendAction>(
|
||||||
|
// We load the state from the savedStateHandle for testing purposes.
|
||||||
|
initialState = savedStateHandle[KEY_STATE] ?: run {
|
||||||
|
val args = savedStateHandle.toViewSendArgs()
|
||||||
|
ViewSendState(
|
||||||
|
sendType = args.sendType,
|
||||||
|
sendId = args.sendId,
|
||||||
|
viewState = ViewSendState.ViewState.Loading,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
override fun handleAction(action: ViewSendAction) {
|
||||||
|
when (action) {
|
||||||
|
ViewSendAction.CloseClick -> handleCloseClick()
|
||||||
|
ViewSendAction.EditClick -> handleEditClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleCloseClick() {
|
||||||
|
sendEvent(ViewSendEvent.NavigateBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEditClick() {
|
||||||
|
sendEvent(ViewSendEvent.NavigateToEdit(sendType = state.sendType, sendId = state.sendId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models state for the new send screen.
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data class ViewSendState(
|
||||||
|
val sendType: SendItemType,
|
||||||
|
val sendId: String,
|
||||||
|
val viewState: ViewState,
|
||||||
|
) : Parcelable {
|
||||||
|
/**
|
||||||
|
* Helper to determine the screen display name.
|
||||||
|
*/
|
||||||
|
val screenDisplayName: Text
|
||||||
|
get() = when (sendType) {
|
||||||
|
SendItemType.FILE -> R.string.view_file_send.asText()
|
||||||
|
SendItemType.TEXT -> R.string.view_text_send.asText()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the fab is visible.
|
||||||
|
*/
|
||||||
|
val isFabVisible: Boolean get() = viewState is ViewState.Content
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the specific view states for the view send screen.
|
||||||
|
*/
|
||||||
|
sealed class ViewState : Parcelable {
|
||||||
|
/**
|
||||||
|
* Represents an error state for the view send screen.
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data class Error(val message: Text) : ViewState()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loading state for the view send screen, signifying that the content is being processed.
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data object Loading : ViewState()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a loaded content state for the view send screen.
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data object Content : ViewState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models events for the view send screen.
|
||||||
|
*/
|
||||||
|
sealed class ViewSendEvent {
|
||||||
|
/**
|
||||||
|
* Navigate back.
|
||||||
|
*/
|
||||||
|
data object NavigateBack : ViewSendEvent()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the edit send screen for the current send.
|
||||||
|
*/
|
||||||
|
data class NavigateToEdit(
|
||||||
|
val sendType: SendItemType,
|
||||||
|
val sendId: String,
|
||||||
|
) : ViewSendEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models actions for the view send screen.
|
||||||
|
*/
|
||||||
|
sealed class ViewSendAction {
|
||||||
|
/**
|
||||||
|
* The user has clicked the close button.
|
||||||
|
*/
|
||||||
|
data object CloseClick : ViewSendAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user has clicked the edit button.
|
||||||
|
*/
|
||||||
|
data object EditClick : ViewSendAction()
|
||||||
|
}
|
||||||
@@ -1266,4 +1266,6 @@ Do you want to switch to this account?</string>
|
|||||||
<string name="cannot_delete_your_account">Cannot delete your account</string>
|
<string name="cannot_delete_your_account">Cannot delete your account</string>
|
||||||
<string name="cannot_delete_your_account_explanation">This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details.</string>
|
<string name="cannot_delete_your_account_explanation">This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details.</string>
|
||||||
<string name="this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden">This account will soon be deleted. Log in at %1$s to continue using Bitwarden.</string>
|
<string name="this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden">This account will soon be deleted. Log in at %1$s to continue using Bitwarden.</string>
|
||||||
|
<string name="view_file_send">View file Send</string>
|
||||||
|
<string name="view_text_send">View text Send</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package com.x8bit.bitwarden.ui.tools.feature.send.viewsend
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.assertCountEquals
|
||||||
|
import androidx.compose.ui.test.assertIsDisplayed
|
||||||
|
import androidx.compose.ui.test.assertIsNotDisplayed
|
||||||
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
|
import androidx.compose.ui.test.performClick
|
||||||
|
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||||
|
import com.bitwarden.ui.util.asText
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||||
|
import com.x8bit.bitwarden.ui.tools.feature.send.model.SendItemType
|
||||||
|
import com.x8bit.bitwarden.ui.util.isProgressBar
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.runs
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class ViewSendScreenTest : BaseComposeTest() {
|
||||||
|
private var onNavigateBackCalled: Boolean = false
|
||||||
|
private var onNavigateToEditData: String? = null
|
||||||
|
private val mutableEventFlow = bufferedMutableSharedFlow<ViewSendEvent>()
|
||||||
|
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||||
|
private val viewModel = mockk<ViewSendViewModel> {
|
||||||
|
every { eventFlow } returns mutableEventFlow
|
||||||
|
every { stateFlow } returns mutableStateFlow
|
||||||
|
every { trySendAction(action = any()) } just runs
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
setContent {
|
||||||
|
ViewSendScreen(
|
||||||
|
viewModel = viewModel,
|
||||||
|
onNavigateBack = { onNavigateBackCalled = true },
|
||||||
|
onNavigateToEditSend = { onNavigateToEditData = it },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on NavigateBack event should call onNavigateBack`() {
|
||||||
|
mutableEventFlow.tryEmit(ViewSendEvent.NavigateBack)
|
||||||
|
assertTrue(onNavigateBackCalled)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on NavigateToEdit event should call onNavigateToEdit`() {
|
||||||
|
val sendType = SendItemType.TEXT
|
||||||
|
val sendId = "send_id"
|
||||||
|
mutableEventFlow.tryEmit(ViewSendEvent.NavigateToEdit(sendType = sendType, sendId = sendId))
|
||||||
|
assertEquals(sendId, onNavigateToEditData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on close click should send CloseClick`() {
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithContentDescription(label = "Close")
|
||||||
|
.performClick()
|
||||||
|
verify(exactly = 1) {
|
||||||
|
viewModel.trySendAction(ViewSendAction.CloseClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on edit click should send EditClick`() {
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithContentDescription(label = "Edit Send")
|
||||||
|
.performClick()
|
||||||
|
verify(exactly = 1) {
|
||||||
|
viewModel.trySendAction(ViewSendAction.EditClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `progress bar should be displayed based on ViewState`() {
|
||||||
|
mutableStateFlow.update { it.copy(viewState = ViewSendState.ViewState.Loading) }
|
||||||
|
// There are 2 because of the pull-to-refresh
|
||||||
|
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
|
||||||
|
|
||||||
|
mutableStateFlow.update { it.copy(viewState = DEFAULT_STATE.viewState) }
|
||||||
|
// Only pull-to-refresh remains
|
||||||
|
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `error should be displayed based on ViewState`() {
|
||||||
|
val errorMessage = "Fail!"
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(viewState = ViewSendState.ViewState.Error(message = errorMessage.asText()))
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithText(text = errorMessage).assertIsDisplayed()
|
||||||
|
|
||||||
|
mutableStateFlow.update { it.copy(viewState = DEFAULT_STATE.viewState) }
|
||||||
|
composeTestRule.onNodeWithText(text = errorMessage).assertIsNotDisplayed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val DEFAULT_STATE = ViewSendState(
|
||||||
|
sendType = SendItemType.TEXT,
|
||||||
|
sendId = "send_id",
|
||||||
|
viewState = ViewSendState.ViewState.Content,
|
||||||
|
)
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.x8bit.bitwarden.ui.tools.feature.send.viewsend
|
||||||
|
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
import app.cash.turbine.test
|
||||||
|
import com.bitwarden.ui.platform.base.BaseViewModelTest
|
||||||
|
import com.x8bit.bitwarden.ui.tools.feature.send.model.SendItemType
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.unmockkStatic
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class ViewSendViewModelTest : BaseViewModelTest() {
|
||||||
|
@BeforeEach
|
||||||
|
fun setup() {
|
||||||
|
mockkStatic(
|
||||||
|
SavedStateHandle::toViewSendArgs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkStatic(
|
||||||
|
SavedStateHandle::toViewSendArgs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `initial state should be correct`() {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
assertEquals(
|
||||||
|
DEFAULT_STATE.copy(viewState = ViewSendState.ViewState.Loading),
|
||||||
|
viewModel.stateFlow.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on CloseClick should send NavigateBack`() = runTest {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(ViewSendAction.CloseClick)
|
||||||
|
assertEquals(ViewSendEvent.NavigateBack, awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on EditClick should send NavigateToEdit`() = runTest {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(ViewSendAction.EditClick)
|
||||||
|
assertEquals(
|
||||||
|
ViewSendEvent.NavigateToEdit(
|
||||||
|
sendType = DEFAULT_STATE.sendType,
|
||||||
|
sendId = DEFAULT_STATE.sendId,
|
||||||
|
),
|
||||||
|
awaitItem(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createViewModel(
|
||||||
|
state: ViewSendState? = null,
|
||||||
|
): ViewSendViewModel = ViewSendViewModel(
|
||||||
|
savedStateHandle = SavedStateHandle().apply {
|
||||||
|
set(key = "state", value = state)
|
||||||
|
every { toViewSendArgs() } returns ViewSendArgs(
|
||||||
|
sendId = (state ?: DEFAULT_STATE).sendId,
|
||||||
|
sendType = (state ?: DEFAULT_STATE).sendType,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val DEFAULT_STATE = ViewSendState(
|
||||||
|
sendType = SendItemType.TEXT,
|
||||||
|
sendId = "send_id",
|
||||||
|
viewState = ViewSendState.ViewState.Content,
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user