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

[#69] Create HomeScreen UnitTest #71

Merged
merged 3 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ android {
xmlReport = true
xmlOutput = file("build/reports/lint/lint-result.xml")
}

testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}

kapt {
Expand Down Expand Up @@ -158,6 +164,8 @@ dependencies {
testImplementation("io.mockk:mockk:${Versions.TEST_MOCKK_VERSION}")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.TEST_COROUTINES_VERSION}")
testImplementation("app.cash.turbine:turbine:${Versions.TEST_TURBINE_VERSION}")
testImplementation ("androidx.compose.ui:ui-test-junit4:${Versions.COMPOSE_VERSION}")
testImplementation ("org.robolectric:robolectric:${Versions.TEST_ROBOLECTRIC_VERSION}")

kaptTest("com.google.dagger:hilt-android-compiler:${Versions.HILT_VERSION}")
testAnnotationProcessor("com.google.dagger:hilt-android-compiler:${Versions.HILT_VERSION}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
Expand All @@ -38,13 +39,19 @@ import co.nimblehq.compose.crypto.ui.theme.Style.textColor
import co.nimblehq.compose.crypto.ui.uimodel.CoinItemUiModel
import coil.compose.rememberAsyncImagePainter

const val TestTagCoinItemSymbol = "CoinItemCoinSymbol"
const val TestTagCoinItemCoinName = "CoinItemCoinName"
const val TestTagCoinItemPrice = "CoinItemPrice"
const val TestTagCoinItemPriceChange = "CoinItemPriceChange"

@Composable
fun CoinItem(
modifier: Modifier = Modifier,
coinItem: CoinItemUiModel,
onItemClick: () -> Unit
) {
ConstraintLayout(
modifier = Modifier
modifier = modifier
.wrapContentWidth()
.clip(RoundedCornerShape(Dp12))
.clickable { onItemClick.invoke() }
Expand Down Expand Up @@ -76,7 +83,8 @@ fun CoinItem(
.constrainAs(coinSymbol) {
top.linkTo(parent.top)
start.linkTo(anchor = logo.end, margin = Dp16)
},
}
.testTag(tag = TestTagCoinItemSymbol),
text = coinItem.symbol.uppercase(),
color = MaterialTheme.colors.textColor,
style = Style.semiBold16()
Expand All @@ -89,7 +97,8 @@ fun CoinItem(
start.linkTo(coinSymbol.start)
top.linkTo(coinSymbol.bottom)
width = Dimension.preferredWrapContent
},
}
.testTag(tag = TestTagCoinItemCoinName),
text = coinItem.coinName,
color = MaterialTheme.colors.coinNameColor,
style = Style.medium14()
Expand All @@ -101,7 +110,8 @@ fun CoinItem(
start.linkTo(logo.start)
top.linkTo(anchor = coinName.bottom, margin = Dp14)
width = Dimension.preferredWrapContent
},
}
.testTag(tag = TestTagCoinItemPrice),
text = stringResource(
R.string.coin_currency,
coinItem.currentPrice.toFormattedString()
Expand All @@ -119,6 +129,7 @@ fun CoinItem(
bottom.linkTo(parent.bottom)
width = Dimension.preferredWrapContent
}
.testTag(tag = TestTagCoinItemPriceChange)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
Expand Down Expand Up @@ -42,21 +43,23 @@ import timber.log.Timber

private const val LIST_ITEM_LOAD_MORE_THRESHOLD = 0

const val TestTagHomeTitle = "HomeTitle"
const val TestTagTrendingItem = "TrendingItem"
const val TestTagCoinItem = "CoinItem"
const val TestTagCoinsLoader = "CoinsLoader"

@Composable
fun HomeScreen(
viewModel: HomeViewModel = hiltViewModel(),
navigator: (destination: AppDestination) -> Unit
) {
val context = LocalContext.current
var rememberRefreshing by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
viewModel.output.error.collect { error ->
val message = error.userReadableMessage(context)
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}

LaunchedEffect(viewModel) {
viewModel.output.navigator.collect { destination -> navigator(destination) }
viewModel.output.navigator.collect { destination ->
navigator(destination)
}
}
LaunchedEffect(viewModel.showLoading) {
viewModel.showLoading.collect { isRefreshing ->
Expand All @@ -68,6 +71,16 @@ fun HomeScreen(
val showTrendingCoinsLoading: LoadingState by viewModel.output.showTrendingCoinsLoading.collectAsState()
val myCoins: List<CoinItemUiModel> by viewModel.output.myCoins.collectAsState()
val trendingCoins: List<CoinItemUiModel> by viewModel.output.trendingCoins.collectAsState()
val myCoinsError: Throwable? by viewModel.output.myCoinsError.collectAsState()
val trendingCoinsError: Throwable? by viewModel.output.trendingCoinsError.collectAsState()

myCoinsError?.let { error ->
Toast.makeText(context, error.userReadableMessage(context), Toast.LENGTH_SHORT).show()
}

trendingCoinsError?.let { error ->
Toast.makeText(context, error.userReadableMessage(context), Toast.LENGTH_SHORT).show()
}

HomeScreenContent(
showMyCoinsLoading = showMyCoinsLoading,
Expand Down Expand Up @@ -117,7 +130,8 @@ private fun HomeScreenContent(
Text(
modifier = Modifier
.fillMaxWidth()
.padding(top = Dp16),
.padding(top = Dp16)
.testTag(TestTagHomeTitle),
text = stringResource(id = R.string.home_title),
textAlign = TextAlign.Center,
style = Style.semiBold24(),
Expand Down Expand Up @@ -173,7 +187,8 @@ private fun HomeScreenContent(
CircularProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.wrapContentWidth(align = Alignment.CenterHorizontally),
.wrapContentWidth(align = Alignment.CenterHorizontally)
.testTag(tag = TestTagCoinsLoader),
)
}
} else {
Expand All @@ -191,6 +206,7 @@ private fun HomeScreenContent(
)
) {
TrendingItem(
modifier = Modifier.testTag(tag = TestTagTrendingItem),
coinItem = coin,
onItemClick = { onTrendingItemClick.invoke(coin) }
)
Expand All @@ -206,6 +222,7 @@ private fun HomeScreenContent(
.fillMaxWidth()
.wrapContentWidth(align = Alignment.CenterHorizontally)
.padding(bottom = Dp16)
.testTag(tag = TestTagCoinsLoader),
)
}
}
Expand Down Expand Up @@ -269,7 +286,8 @@ private fun MyCoins(
.constrainAs(myCoins) {
top.linkTo(myCoinsTitle.bottom, margin = Dp16)
linkTo(start = parent.start, end = parent.end)
},
}
.testTag(tag = TestTagCoinsLoader),
)
} else {
LazyRow(
Expand All @@ -283,6 +301,7 @@ private fun MyCoins(
) {
items(coins) { coin ->
CoinItem(
modifier = Modifier.testTag(tag = TestTagCoinItem),
coinItem = coin,
onItemClick = { onMyCoinsItemClick.invoke(coin) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ interface Output : BaseOutput {
val myCoins: StateFlow<List<CoinItemUiModel>>

val trendingCoins: StateFlow<List<CoinItemUiModel>>

val myCoinsError: SharedFlow<Throwable?>

val trendingCoinsError: SharedFlow<Throwable?>
}

@HiltViewModel
Expand Down Expand Up @@ -66,6 +70,14 @@ class HomeViewModel @Inject constructor(
override val trendingCoins: StateFlow<List<CoinItemUiModel>>
get() = _trendingCoins

private val _myCoinsError = MutableStateFlow<Throwable?>(null)
override val myCoinsError: StateFlow<Throwable?>
get() = _myCoinsError

private val _trendingCoinsError = MutableStateFlow<Throwable?>(null)
override val trendingCoinsError: StateFlow<Throwable?>
get() = _trendingCoinsError

private var trendingCoinsPage = MY_COINS_INITIAL_PAGE

init {
Expand All @@ -81,7 +93,11 @@ class HomeViewModel @Inject constructor(
}

private fun getMyCoins(isRefreshing: Boolean) = execute {
if (isRefreshing) showLoading() else _showMyCoinsLoading.value = true
if (isRefreshing) {
showLoading()
} else {
_showMyCoinsLoading.value = true
}
getMyCoinsUseCase.execute(
GetMyCoinsUseCase.Input(
currency = FIAT_CURRENCY,
Expand All @@ -92,7 +108,7 @@ class HomeViewModel @Inject constructor(
)
)
.catch { e ->
_error.emit(e)
_myCoinsError.emit(e)
}
.collect { coins ->
_myCoins.emit(coins.map { it.toUiModel() })
Expand All @@ -115,7 +131,7 @@ class HomeViewModel @Inject constructor(
)
)
.catch { e ->
_error.emit(e)
_trendingCoinsError.emit(e)
}
.collect { coins ->
val newCoinList = coins.map { it.toUiModel() }
Expand All @@ -126,8 +142,7 @@ class HomeViewModel @Inject constructor(
}
trendingCoinsPage++
}
if (isRefreshing) hideLoading() else
_showTrendingCoinsLoading.value = LoadingState.Idle
if (isRefreshing) hideLoading() else _showTrendingCoinsLoading.value = LoadingState.Idle
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.constraintlayout.compose.ConstraintLayout
Expand All @@ -20,6 +21,11 @@ import co.nimblehq.compose.crypto.ui.theme.Dimension.Dp16
import co.nimblehq.compose.crypto.ui.theme.Dimension.Dp40
import co.nimblehq.compose.crypto.ui.theme.Dimension.Dp8

const val TestTagTotalCoinsLabel = "CardTotalCoinsLabel"
const val TestTagTodayCoinProfitLabel = "todayProfitLabel"
const val TestTagCardTotalCoins = "CardTotalCoins"
const val TestTagCardTodayProfit = "CardTodayProfit"

@Composable
fun PortfolioCard(
modifier: Modifier
Expand Down Expand Up @@ -47,7 +53,8 @@ fun PortfolioCard(
modifier = Modifier
.constrainAs(totalCoinsLabel) {
start.linkTo(parent.start)
},
}
.testTag(TestTagTotalCoinsLabel),
text = stringResource(R.string.portfolio_card_total_coin_label),
style = Style.lightSilverMedium16()
)
Expand All @@ -56,7 +63,8 @@ fun PortfolioCard(
modifier = Modifier
.constrainAs(totalCoins) {
top.linkTo(totalCoinsLabel.bottom, margin = Dp8)
},
}
.testTag(tag = TestTagCardTotalCoins),
// TODO: Remove dummy value when work on Integrate.
text = stringResource(R.string.coin_currency, "7,273,291"),
style = Style.whiteSemiBold24()
Expand All @@ -66,7 +74,8 @@ fun PortfolioCard(
modifier = Modifier
.constrainAs(todayProfitLabel) {
top.linkTo(totalCoins.bottom, margin = Dp40)
},
}
.testTag(tag = TestTagTodayCoinProfitLabel),
text = stringResource(R.string.portfolio_card_today_profit_label),
style = Style.lightSilverMedium16()
)
Expand All @@ -75,7 +84,8 @@ fun PortfolioCard(
modifier = Modifier
.constrainAs(todayProfit) {
top.linkTo(todayProfitLabel.bottom, margin = Dp8)
},
}
.testTag(tag = TestTagCardTodayProfit),
// TODO: Remove dummy value when work on Integrate.
text = stringResource(R.string.coin_currency, "193,280"),
style = Style.whiteSemiBold24()
Expand All @@ -95,7 +105,7 @@ fun PortfolioCard(

@Composable
@Preview
fun PortfolioCardPreview() {
private fun PortfolioCardPreview() {
ComposeTheme {
PortfolioCard(
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.constraintlayout.compose.ConstraintLayout
Expand All @@ -33,14 +34,20 @@ import co.nimblehq.compose.crypto.ui.theme.Style.textColor
import co.nimblehq.compose.crypto.ui.uimodel.CoinItemUiModel
import coil.compose.rememberAsyncImagePainter

const val TestTagTrendingItemSymbol = "TrendingItemSymbol"
const val TestTagTrendingItemCoinName = "TrendingItemCoinName"
const val TestTagTrendingItemPriceChange = "TrendingItemPriceChange"

@Suppress("LongMethod")
@Composable
fun TrendingItem(
modifier: Modifier = Modifier,
coinItem: CoinItemUiModel,
onItemClick: () -> Unit
) {

ConstraintLayout(
modifier = Modifier
modifier = modifier
.fillMaxWidth()
.clip(RoundedCornerShape(Dp12))
.clickable { onItemClick.invoke() }
Expand Down Expand Up @@ -76,7 +83,8 @@ fun TrendingItem(
top.linkTo(parent.top)
bottom.linkTo(coinName.top)
start.linkTo(anchor = logo.end, margin = Dp16)
},
}
.testTag(tag = TestTagTrendingItemSymbol),
text = coinItem.symbol.uppercase(),
color = MaterialTheme.colors.textColor,
style = Style.semiBold16()
Expand All @@ -89,7 +97,8 @@ fun TrendingItem(
top.linkTo(coinSymbol.bottom)
bottom.linkTo(parent.bottom)
width = Dimension.preferredWrapContent
},
}
.testTag(tag = TestTagTrendingItemCoinName),
text = coinItem.coinName,
color = MaterialTheme.colors.coinNameColor,
style = Style.medium14()
Expand All @@ -104,6 +113,7 @@ fun TrendingItem(
bottom.linkTo(coinName.bottom)
width = Dimension.preferredWrapContent
}
.testTag(tag = TestTagTrendingItemPriceChange)
)
}
}
Expand Down
Loading