Skip to content

Commit

Permalink
fix tests and cleanup code
Browse files Browse the repository at this point in the history
  • Loading branch information
nisrulz committed Feb 25, 2024
1 parent 0fd7900 commit 5da8c6b
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -43,8 +41,6 @@ fun BookmarkedLaunchesListComponent(
bookmark: SingleValueCallback<LaunchInfo> = {},
navigateBack: EmptyCallback = {}
) {
var isShowingBookmarks by remember { mutableStateOf(false) }

Box(
modifier = modifier
.fillMaxSize()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

Expand Down Expand Up @@ -40,28 +41,27 @@ constructor(
@VisibleForTesting
fun getListOfBookmarkedLaunches() = viewModelScope.launch(coroutineDispatcher) {
getAllBookmarkedLaunches()
.catch {
stopLoading()
setError("Error while fetching list of bookmarked launches: ${it.stackTrace}")
}
.collectLatest {
stopLoading()
.onEach {
handleListOfLaunches(it)
}
}

private fun handleListOfLaunches(list: List<LaunchInfo>) {
uiState.update {
it.copy(data = list)
}
}.catch {
setError(it.message ?: "Error")
}.collect()
}

fun bookmark(launchInfo: LaunchInfo) = viewModelScope.launch(coroutineDispatcher) {
bookmarkLaunchInfo(launchInfo)
}

private fun stopLoading() = uiState.update { it.copy(isLoading = false) }
private fun setError(message: String) = uiState.update { it.copy(error = message) }
private fun setError(message: String) {
uiState.update { it.copy(error = message) }
stopLoading()
}

private fun handleListOfLaunches(list: List<LaunchInfo>) {
uiState.update { it.copy(data = list) }
stopLoading()
}

fun showError(message: String) = sendEvent(UiEvent.ShowSnackBar(message))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fun LaunchInfoItem(
onClick: SingleValueCallback<String>
) {
ElevatedCard(
modifier = Modifier
modifier = modifier
.fillMaxWidth()
.height(200.dp)
.padding(MaterialTheme.dimens.small),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ fun TitleBar(
modifier = modifier
.fillMaxWidth()
) {
Image(
modifier = Modifier.weight(0.8f, true),
painter = painterResource(id = R.drawable.logo),
contentDescription = stringResource(id = R.string.title_main)
)

if (leftNavButtonIcon != -1) {
Image(
painter = painterResource(id = leftNavButtonIcon),
Expand All @@ -47,6 +41,12 @@ fun TitleBar(
)
}

Image(
modifier = Modifier.weight(0.8f, true),
painter = painterResource(id = R.drawable.logo),
contentDescription = stringResource(id = R.string.title_main)
)

if (rightNavButtonIcon != -1) {
Image(
painter = painterResource(id = rightNavButtonIcon),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ fun LaunchDetailSuccessComponent(
.fillMaxSize()
) {
TitleBar(
rightNavButtonIcon = R.drawable.back,
rightNavButtonAction = {
leftNavButtonIcon = R.drawable.back,
leftNavButtonAction = {
navigateBack()
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.nisrulz.example.spacexapi.common.contract.utils.EmptyCallback
import com.nisrulz.example.spacexapi.presentation.features.components.EmptyComponent
import com.nisrulz.example.spacexapi.presentation.features.components.LoadingComponent
import com.nisrulz.example.spacexapi.presentation.features.launchdetail.LaunchDetailViewModel.UiEvent.NavigateBack
import com.nisrulz.example.spacexapi.presentation.features.launchdetail.LaunchDetailViewModel.UiEvent.ShowSnackBar
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow
Expand All @@ -36,6 +37,8 @@ fun LaunchDetailScreen(
is ShowSnackBar -> {
snackbarHostState.showSnackbar(message = it.message)
}

NavigateBack -> onBackAction()
}
}
}
Expand All @@ -45,7 +48,7 @@ fun LaunchDetailScreen(
// Analytics
viewModel.trackOnBack()

onBackAction()
viewModel.navigateBack()
}

with(state) {
Expand All @@ -54,13 +57,13 @@ fun LaunchDetailScreen(
LoadingComponent()
} else if (data == null) {
EmptyComponent(message = "No launch info available") {
onBackAction()
viewModel.navigateBack()
}
} else {
LaunchDetailSuccessComponent(
state = state,
snackbarHostState = snackbarHostState,
navigateBack = { onBackAction() }
navigateBack = { viewModel.navigateBack() }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ constructor(
fun getLaunchInfoDetails(launchId: String?) = viewModelScope.launch(coroutineDispatcher) {
stopLoading()
if (launchId.isNullOrBlank()) {
showError("No Data")
setError("No Data")
} else {
update(getLaunchDetail(launchId))
}
Expand All @@ -58,6 +58,12 @@ constructor(

private fun stopLoading() = uiState.update { it.copy(isLoading = false) }

fun navigateBack() = sendEvent(UiEvent.NavigateBack)

private fun sendEvent(uiEvent: UiEvent) = viewModelScope.launch(coroutineDispatcher) {
eventFlow.send(uiEvent)
}

fun trackScreenEntered() = analytics.trackScreenLaunchDetail()
fun trackOnBack() = analytics.trackNavigateToListOfLaunches()

Expand All @@ -69,5 +75,7 @@ constructor(

sealed interface UiEvent {
data class ShowSnackBar(val message: String) : UiEvent

data object NavigateBack : UiEvent
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

Expand Down Expand Up @@ -50,47 +51,31 @@ constructor(
@VisibleForTesting
fun getListOfLaunches() = viewModelScope.launch(coroutineDispatcher) {
getAllLaunches()
.catch {
stopLoading()
uiState.update {
it.copy(error = "Error while fetching list of launches")
}
}
.collectLatest {
stopLoading()
.onEach {
handleListOfLaunches(it)
}
}

@VisibleForTesting
fun getListOfBookmarkedLaunches() = viewModelScope.launch(coroutineDispatcher) {
getAllBookmarkedLaunches()
.catch {
stopLoading()
uiState.update {
it.copy(error = "Error while fetching list of bookmarked launches")
}
}
.collectLatest {
stopLoading()
handleListOfLaunches(it)
}
}.catch {
setError(it.message ?: "Error")
}.collect()
}

fun bookmark(launchInfo: LaunchInfo) = viewModelScope.launch(coroutineDispatcher) {
bookmarkLaunchInfo(launchInfo)
}

private fun handleListOfLaunches(list: List<LaunchInfo>) {
uiState.update {
it.copy(data = list)
}
uiState.update { it.copy(data = list) }
stopLoading()
}

fun onClickBookmarkToolbarIcon() {
navigateToBookmarks()
}

private fun setError(message: String) {
uiState.update { it.copy(error = message) }
stopLoading()
}

fun showError(message: String) = sendEvent(ShowSnackBar(message))

fun navigateToDetails(launchId: String) = sendEvent(NavigateToDetails(launchId))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.nisrulz.example.spacexapi.presentation.features.bookmarkedlaunches

import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import com.nisrulz.example.spacexapi.domain.usecase.GetAllBookmarkedLaunches
import com.nisrulz.example.spacexapi.domain.usecase.ToggleBookmarkLaunchInfo
import com.nisrulz.example.spacexapi.presentation.features.bookmarkedlaunches.BookmarkedLaunchesViewModel.UiEvent.NavigateBack
import com.nisrulz.example.spacexapi.presentation.features.bookmarkedlaunches.BookmarkedLaunchesViewModel.UiEvent.NavigateToDetails
import com.nisrulz.example.spacexapi.presentation.features.bookmarkedlaunches.BookmarkedLaunchesViewModel.UiEvent.ShowSnackBar
import com.nisrulz.example.spacexapi.presentation.util.TestFactory
import com.nisrulz.example.spacexapi.presentation.util.runUnconfinedTest
import com.nisrulz.example.spacexapi.presentation.util.testDispatcher
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.receiveAsFlow
import org.junit.Before
import org.junit.Test

class BookmarkedLaunchesViewModelTest {
private lateinit var sut: BookmarkedLaunchesViewModel
private lateinit var bookmarkLaunchInfo: ToggleBookmarkLaunchInfo
private lateinit var getAllBookmarkedLaunches: GetAllBookmarkedLaunches

@Before
fun setup() {
bookmarkLaunchInfo = mockk()
getAllBookmarkedLaunches = mockk()

sut = BookmarkedLaunchesViewModel(
coroutineDispatcher = testDispatcher,
bookmarkLaunchInfo = bookmarkLaunchInfo,
getAllBookmarkedLaunches = getAllBookmarkedLaunches
)
}

@Test
fun `getListOfBookmarkedLaunches() should update uiState with list of bookmarked launches`() =
runUnconfinedTest {
// Given
val list = TestFactory.buildListOfBookmarkedLaunchInfo()
coEvery { getAllBookmarkedLaunches() } returns flowOf(list)

sut.uiState.test {
// When
sut.getListOfBookmarkedLaunches()

// Then
assertThat(awaitItem().isLoading).isTrue()
assertThat(awaitItem().data).isEqualTo(list)
}
}

@Test
fun `bookmark() should call bookmarkLaunchInfo()`() = runUnconfinedTest {
// Given
val launchInfo = TestFactory.buildLaunchInfo()
coEvery { bookmarkLaunchInfo(any()) } returns Unit

// When
sut.bookmark(launchInfo)

// Then
coVerify {
bookmarkLaunchInfo(launchInfo)
}
}

@Test
fun `showError() should update uiEvent with ShowSnackBar`() = runUnconfinedTest {
// Given
val message = "Test Error Message"

// Then
sut.eventFlow.receiveAsFlow().test {
// When
sut.showError(message)

// Then
assertThat(awaitItem()).isEqualTo(ShowSnackBar(message))
}
}

@Test
fun `navigateToDetails() should update uiEvent with NavigateToDetails`() = runUnconfinedTest {
// Given
val launchId = "TestLaunchId"

// Then
sut.eventFlow.receiveAsFlow().test {
// When
sut.navigateToDetails(launchId)

// Then
assertThat(awaitItem()).isEqualTo(NavigateToDetails(launchId))
}
}

@Test
fun `navigateBack() should update uiEvent with NavigateBack`() = runUnconfinedTest {
// Then
sut.eventFlow.receiveAsFlow().test {
// When
sut.navigateBack()

// Then
assertThat(awaitItem()).isEqualTo(NavigateBack)
}
}
}
Loading

0 comments on commit 5da8c6b

Please sign in to comment.