Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

70 añadir unit tests detailviewmodel #99

Merged
merged 18 commits into from
Mar 12, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ class ConnectivityInterceptor(
} ?: false
}

class InternetConnectivityException: IOException()
class InternetConnectivityException: IOException()
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ fun ArchDialog(
confirmButton = {},
dismissButton = {}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ fun ErrorScreen(modifier: Modifier = Modifier) {
text = "Something went wrong"
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ import com.architects.pokearch.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ArchTopAppBar(
archTopAppBarType: ArchTopAppBarType,
onBackButtonClicked: () -> Unit,
modifier: Modifier = Modifier,
text: String = "",
scrollBehavior: TopAppBarScrollBehavior? = null,
archTopAppBarType: ArchTopAppBarType,
onBackButtonClicked: () -> Unit,
onTextChange: ((String) -> Unit),
) {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.architects.pokearch.ui.features.details.di

import androidx.lifecycle.SavedStateHandle
import com.architects.pokearch.ui.features.details.di.annotations.PokemonId
import com.architects.pokearch.ui.navigation.NavArg
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped

@Module
@InstallIn(ViewModelComponent::class)
class DetailViewModelModule {

@Provides
@ViewModelScoped
@PokemonId
fun providePokemonId(savedStateHandle: SavedStateHandle): Int =
savedStateHandle.get<Int>(NavArg.PokemonId.key) ?: 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.architects.pokearch.ui.features.details.di.annotations

import javax.inject.Qualifier

@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class PokemonId
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,13 @@ fun DetailScreen(
modifier: Modifier = Modifier,
viewModel: DetailViewModel = hiltViewModel(),
) {
val uiState by viewModel.pokemonDetailInfo.collectAsStateWithLifecycle()
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val dialogState by viewModel.dialogState.collectAsStateWithLifecycle()

LaunchedEffect(true) {
viewModel.getPokemonDetails()
}

Box(
modifier = Modifier.fillMaxSize()
) {
Expand All @@ -97,7 +101,6 @@ fun DetailScreen(
}

is Success -> {
viewModel.playCry()
DetailSuccessScreen(
modifier = modifier,
state = state
Expand All @@ -120,8 +123,8 @@ fun DetailScreen(

@Composable
private fun DetailSuccessScreen(
modifier: Modifier = Modifier,
state: Success,
modifier: Modifier = Modifier,
) {

val (image, colors) = getGradientImageAndColors(pokemonInfo = state.pokemonInfo)
Expand Down Expand Up @@ -195,10 +198,10 @@ private fun getGradientImageAndColors(

@Composable
private fun PokemonCard(
modifier: Modifier = Modifier,
pokemonImage: AsyncImagePainter,
imageSize: Dp = 240.dp,
gradientColors: List<Color>,
modifier: Modifier = Modifier,
imageSize: Dp = 240.dp,
) {
Box(
modifier = modifier
Expand All @@ -224,8 +227,8 @@ private fun PokemonCard(

@Composable
private fun Title(
modifier: Modifier = Modifier,
text: String,
modifier: Modifier = Modifier,
style: TextStyle = MaterialTheme.typography.headlineLarge,
) {
Text(
Expand All @@ -237,8 +240,8 @@ private fun Title(

@Composable
private fun TypeRow(
modifier: Modifier = Modifier,
pokemonInfo: PokemonInfo,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier,
Expand All @@ -257,9 +260,9 @@ private fun TypeRow(

@Composable
private fun TypeItem(
modifier: Modifier = Modifier,
name: String,
containerColor: Color,
modifier: Modifier = Modifier,
) {
Text(
text = name,
Expand All @@ -282,8 +285,8 @@ private fun TypeItem(

@Composable
private fun PokemonInfos(
modifier: Modifier = Modifier,
pokemonInfo: PokemonInfo,
modifier: Modifier = Modifier,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
Expand Down Expand Up @@ -372,8 +375,8 @@ private fun PokemonInfos(

@Composable
fun BaseStatsTitle(
modifier: Modifier = Modifier,
text: String,
modifier: Modifier = Modifier,
style: TextStyle = MaterialTheme.typography.headlineSmall,
) {
Text(
Expand All @@ -385,8 +388,8 @@ fun BaseStatsTitle(

@Composable
private fun PokemonStats(
modifier: Modifier = Modifier,
pokemonInfo: PokemonInfo,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier.fillMaxWidth()
Expand All @@ -402,9 +405,9 @@ private fun PokemonStats(

@Composable
private fun StatItem(
stats: Stats,
modifier: Modifier = Modifier,
height: Dp = 22.dp,
stats: Stats,
) {

val animationProgress = remember {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
package com.architects.pokearch.ui.features.details.viewModel

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.architects.pokearch.core.di.annotations.IO
import com.architects.pokearch.domain.model.error.Failure
import com.architects.pokearch.domain.repository.MediaPlayerRepositoryContract
import com.architects.pokearch.ui.features.details.di.annotations.PokemonId
import com.architects.pokearch.ui.features.details.state.DetailUiState
import com.architects.pokearch.ui.features.details.state.DetailUiState.Error
import com.architects.pokearch.ui.features.details.state.DetailUiState.Loading
import com.architects.pokearch.ui.features.details.state.DetailUiState.Success
import com.architects.pokearch.ui.mapper.DialogData
import com.architects.pokearch.ui.mapper.ErrorDialogManager
import com.architects.pokearch.ui.navigation.NavArg
import com.architects.pokearch.usecases.FetchCry
import com.architects.pokearch.usecases.FetchPokemonDetails
import com.architects.pokearch.usecases.PlayCry
import com.architects.pokearch.usecases.UpdatePokemonInfo
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -30,55 +32,52 @@ import javax.inject.Inject
class DetailViewModel @Inject constructor(
private val fetchPokemonDetails: FetchPokemonDetails,
private val fetchCry: FetchCry,
private val mediaPlayerRepository: MediaPlayerRepositoryContract,
private val playCry: PlayCry,
private val updatePokemonInfo: UpdatePokemonInfo,
private val errorDialogManager: ErrorDialogManager,
@IO val dispatcher: CoroutineDispatcher,
savedStateHandle: SavedStateHandle,
@PokemonId private val pokemonId: Int,
) : ViewModel() {

private val pokemonId = savedStateHandle.get<Int>(NavArg.PokemonId.key) ?: 0

private val _pokemonDetailInfo: MutableStateFlow<DetailUiState> =
private val _uiState: MutableStateFlow<DetailUiState> =
MutableStateFlow(Loading)
val pokemonDetailInfo: MutableStateFlow<DetailUiState> = _pokemonDetailInfo
val uiState: StateFlow<DetailUiState> = _uiState.asStateFlow()

private val _dialogState: MutableStateFlow<DialogData?> = MutableStateFlow(null)
val dialogState: StateFlow<DialogData?> = _dialogState

private var once = false

init {
fun getPokemonDetails() {
viewModelScope.launch(dispatcher) {
getPokemonDetails(pokemonId)
}
}

private suspend fun getPokemonDetails(pokemonId: Int) {
fetchPokemonDetails(pokemonId).collectLatest { result ->
result.fold(
ifLeft = { failure ->
_pokemonDetailInfo.value = Error
if (failure is Failure.NetworkError) _dialogState.value =
errorDialogManager.transform(
errorType = failure.errorType,
onDismiss = { _dialogState.update { null } }
)
},
ifRight = { pokemonInfo ->
_pokemonDetailInfo.value = Success(pokemonInfo)
fetchPokemonDetails(pokemonId)
.onStart { _uiState.update { Loading } }
.onEach { result ->
result.fold(
ifLeft = { failure ->
_uiState.update { Error }
if (failure is Failure.NetworkError) _dialogState.update {
errorDialogManager.transform(
errorType = failure.errorType,
onDismiss = { _dialogState.update { null } }
)
}
},
ifRight = { pokemonInfo ->
_uiState.update { Success(pokemonInfo) }
}
)
}
)
.collectLatest {
_uiState.update { it }
}
playCry()
}
}

fun playCry() {
private fun playCry() {
viewModelScope.launch(dispatcher) {
with(_pokemonDetailInfo.value) {
//TODO: Move to usecase to avoid calling the repository here
if (this is Success && !once) {
mediaPlayerRepository.playCry(getCryUrl(pokemonInfo.name.replaceFirstChar { it.lowercase() }))
once = true
with(_uiState.value) {
if (this is Success) {
playCry(getCryUrl(pokemonInfo.name.replaceFirstChar { it.lowercase() }))
}
}
}
Expand All @@ -91,17 +90,16 @@ class DetailViewModel @Inject constructor(

fun toggleFavorite() {
viewModelScope.launch {
if (_pokemonDetailInfo.value is Success) {
_pokemonDetailInfo.update {
if (_uiState.value is Success) {
_uiState.update {
(it as Success).copy(
pokemonInfo = it.pokemonInfo.copy(
team = !(_pokemonDetailInfo.value as Success).pokemonInfo.team
team = !(_uiState.value as Success).pokemonInfo.team
)
)
}
updatePokemonInfo((_pokemonDetailInfo.value as Success).pokemonInfo)
updatePokemonInfo((_uiState.value as Success).pokemonInfo)
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ data class DialogData(
@StringRes val title: Int,
@StringRes val message: Int,
val onDissmiss: () -> Unit,
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ class RealEntityMapperTest {

result shouldBeEqualTo expectedStatsList
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -323,4 +323,4 @@ class PokemonServerDataSourceTest {
pokedexService = pokedexService,
cryService = cryService
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ class ConnectivityInterceptorTest {

connectivityInterceptor.intercept(chain)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@ class AccelerometerSensorTest {
context = context
)

}
}
Loading
Loading