mirror of
https://github.com/hmalik144/Weather-apps.git
synced 2026-03-18 15:36:04 +00:00
- Testsuite expansion
This commit is contained in:
@@ -15,6 +15,7 @@ class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
@Test
|
||||
fun loadApp_invalidKeyWeatherResponse_returnsEmptyViewPage() {
|
||||
homeScreen {
|
||||
waitFor(2000)
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
}
|
||||
@@ -23,6 +24,7 @@ class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
@Test
|
||||
fun invalidKeyWeatherResponse_swipeToRefresh_returnsValidPage() {
|
||||
homeScreen {
|
||||
waitFor(2000)
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
|
||||
|
||||
@@ -34,15 +34,6 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
|
||||
navigateToFurtherDetails(it)
|
||||
})
|
||||
|
||||
forecast_listview.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = recyclerAdapter
|
||||
}
|
||||
|
||||
swipe_refresh.apply {
|
||||
setOnRefreshListener {
|
||||
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
|
||||
@@ -51,16 +42,14 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(error: Any?) {
|
||||
swipe_refresh.isRefreshing = false
|
||||
}
|
||||
recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
|
||||
navigateToFurtherDetails(it)
|
||||
})
|
||||
|
||||
override fun onSuccess(data: Any?) {
|
||||
swipe_refresh.isRefreshing = false
|
||||
if (data is WeatherDisplay) {
|
||||
recyclerAdapter.addCurrent(data)
|
||||
forecast_listview.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = recyclerAdapter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +62,19 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSuccess(data: Any?) {
|
||||
super.onSuccess(data)
|
||||
swipe_refresh.isRefreshing = false
|
||||
if (data is WeatherDisplay) {
|
||||
recyclerAdapter.addCurrent(data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(error: Any?) {
|
||||
super.onFailure(error)
|
||||
swipe_refresh.isRefreshing = false
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun permissionsGranted() {
|
||||
viewModel.fetchData()
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
||||
import com.appttude.h_mal.atlas_weather.utils.generateView
|
||||
import com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast.ViewHolderForecast
|
||||
import com.appttude.h_mal.atlas_weather.ui.home.adapter.forecastDaily.ViewHolderForecastDaily
|
||||
|
||||
class WeatherRecyclerAdapter(
|
||||
val itemClick: (Forecast) -> Unit
|
||||
private val itemClick: (Forecast) -> Unit
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
var weather: WeatherDisplay? = null
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun addCurrent(current: WeatherDisplay) {
|
||||
weather = current
|
||||
notifyDataSetChanged()
|
||||
@@ -32,8 +31,8 @@ class WeatherRecyclerAdapter(
|
||||
ViewHolderCurrent(viewCurrent)
|
||||
}
|
||||
|
||||
is ViewType.Forecast -> {
|
||||
val viewForecast = parent.generateView(R.layout.list_item_forecast)
|
||||
is ViewType.ForecastHourly -> {
|
||||
val viewForecast = parent.generateView(R.layout.hourly_item_forecast)
|
||||
ViewHolderForecast(viewForecast)
|
||||
}
|
||||
|
||||
@@ -41,13 +40,19 @@ class WeatherRecyclerAdapter(
|
||||
val viewFurther = parent.generateView(R.layout.list_item_further)
|
||||
ViewHolderFurtherDetails(viewFurther)
|
||||
}
|
||||
|
||||
is ViewType.ForecastDaily -> {
|
||||
val viewForecast = parent.generateView(R.layout.list_item_forecast)
|
||||
ViewHolderForecastDaily(viewForecast)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ViewType {
|
||||
object Empty : ViewType()
|
||||
object Current : ViewType()
|
||||
object Forecast : ViewType()
|
||||
object ForecastHourly : ViewType()
|
||||
object ForecastDaily : ViewType()
|
||||
object Further : ViewType()
|
||||
}
|
||||
|
||||
@@ -55,19 +60,20 @@ class WeatherRecyclerAdapter(
|
||||
return when (type) {
|
||||
0 -> ViewType.Empty
|
||||
1 -> ViewType.Current
|
||||
2 -> ViewType.Forecast
|
||||
2 -> ViewType.ForecastHourly
|
||||
3 -> ViewType.Further
|
||||
4 -> ViewType.ForecastDaily
|
||||
else -> ViewType.Empty
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (weather == null) return 0
|
||||
|
||||
return when (position) {
|
||||
0 -> 1
|
||||
in 1 until itemCount - 2 -> 2
|
||||
itemCount - 1 -> 3
|
||||
1 -> 3
|
||||
2 -> 2
|
||||
in 3 until (itemCount) -> 4
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
@@ -84,28 +90,31 @@ class WeatherRecyclerAdapter(
|
||||
viewHolderCurrent.bindData(weather)
|
||||
}
|
||||
|
||||
is ViewType.Forecast -> {
|
||||
val viewHolderForecast = holder as ViewHolderForecast
|
||||
|
||||
weather?.forecast?.get(position - 1)?.let { i ->
|
||||
viewHolderForecast.bindView(i)
|
||||
viewHolderForecast.itemView.setOnClickListener {
|
||||
itemClick(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is ViewType.Further -> {
|
||||
val viewHolderCurrent = holder as ViewHolderFurtherDetails
|
||||
viewHolderCurrent.bindData(weather)
|
||||
}
|
||||
|
||||
is ViewType.ForecastHourly -> {
|
||||
val viewHolderForecast = holder as ViewHolderForecast
|
||||
viewHolderForecast.bindView(weather?.hourly)
|
||||
}
|
||||
|
||||
is ViewType.ForecastDaily -> {
|
||||
val viewHolderForecast = holder as ViewHolderForecastDaily
|
||||
weather?.forecast?.getOrNull(position - 3)?.let { f ->
|
||||
viewHolderForecast.bindView(f)
|
||||
viewHolderForecast.itemView.setOnClickListener {
|
||||
itemClick.invoke(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
if (weather == null) return 0
|
||||
return 2 + (weather?.forecast?.size ?: 0)
|
||||
return if (weather == null) 1 else 3 + (weather?.forecast?.size ?: 0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.appttude.h_mal.monoWeather.ui.home.adapter.forecast
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.Hour
|
||||
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
||||
import com.appttude.h_mal.atlas_weather.utils.toTime
|
||||
|
||||
class GridCellHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
var dayTV: TextView = itemView.findViewById(R.id.widget_item_day)
|
||||
var weatherIV: ImageView = itemView.findViewById(R.id.widget_item_image)
|
||||
var mainTempTV: TextView = itemView.findViewById(R.id.widget_item_temp_high)
|
||||
|
||||
fun bindView(hour: Hour?) {
|
||||
dayTV.text = hour?.dt?.toTime()
|
||||
weatherIV.loadImage(hour?.icon)
|
||||
mainTempTV.text = hour?.temp?.toInt()?.toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.Hour
|
||||
import com.appttude.h_mal.atlas_weather.utils.generateView
|
||||
import com.appttude.h_mal.monoWeather.ui.home.adapter.forecast.GridCellHolder
|
||||
|
||||
class GridForecastAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
var weather: MutableList<Hour> = mutableListOf()
|
||||
|
||||
fun addCurrent(current: List<Hour>?) {
|
||||
weather.clear()
|
||||
current?.let { weather.addAll(it) }
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val viewCurrent = parent.generateView(R.layout.hourly_forecast_grid_item)
|
||||
return GridCellHolder(viewCurrent)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val view = holder as GridCellHolder
|
||||
val forecast = weather[position]
|
||||
view.bindView(forecast)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = weather.size
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.Hour
|
||||
import com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast.GridForecastAdapter
|
||||
|
||||
class ViewHolderForecast(
|
||||
itemView: View
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
var recyclerView: RecyclerView = itemView.findViewById(R.id.forecast_recyclerview)
|
||||
|
||||
fun bindView(forecasts: List<Hour>?) {
|
||||
val adapter = GridForecastAdapter()
|
||||
adapter.addCurrent(forecasts)
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter.forecastDaily
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
@@ -8,21 +8,21 @@ import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
||||
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
||||
|
||||
class ViewHolderForecast(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
class ViewHolderForecastDaily(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
var dateTV: TextView = itemView.findViewById(R.id.list_date)
|
||||
var dayTV: TextView = itemView.findViewById(R.id.list_day)
|
||||
var conditionTV: TextView = itemView.findViewById(R.id.list_condition)
|
||||
var weatherIV: ImageView = itemView.findViewById(R.id.list_icon)
|
||||
var mainTempTV: TextView = itemView.findViewById(R.id.list_main_temp)
|
||||
var minorTempTV: TextView = itemView.findViewById(R.id.list_minor_temp)
|
||||
var maxTempTV: TextView = itemView.findViewById(R.id.list_main_temp)
|
||||
var minTempTV: TextView = itemView.findViewById(R.id.list_minor_temp)
|
||||
var conditionTV: TextView = itemView.findViewById(R.id.list_condition)
|
||||
|
||||
fun bindView(forecast: Forecast?) {
|
||||
dateTV.text = forecast?.date
|
||||
dayTV.text = forecast?.day
|
||||
conditionTV.text = forecast?.condition
|
||||
weatherIV.loadImage(forecast?.weatherIcon)
|
||||
mainTempTV.text = forecast?.mainTemp
|
||||
minorTempTV.text = forecast?.minorTemp
|
||||
maxTempTV.text = forecast?.mainTemp
|
||||
minTempTV.text = forecast?.minorTemp
|
||||
conditionTV.text = forecast?.condition
|
||||
}
|
||||
}
|
||||
@@ -36,20 +36,4 @@
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"
|
||||
android:visibility="gone">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -43,17 +43,5 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||
tools:layout="@layout/fragment_home" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_circular"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="0.2dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/widget_item_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_item_day"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="#ffffff"
|
||||
tools:text="Dec 1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/widget_item_image"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/image_string"
|
||||
tools:src="@drawable/cloud_symbol"
|
||||
tools:tint="@color/colour_one" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_item_temp_high"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="20" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/db_temp_unit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/degrees" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
24
app/src/atlasWeather/res/layout/hourly_item_forecast.xml
Normal file
24
app/src/atlasWeather/res/layout/hourly_item_forecast.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/forecast_recyclerview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:spanCount="1"
|
||||
tools:itemCount="24"
|
||||
tools:listitem="@layout/widget_item" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -48,7 +48,6 @@ class MainViewModel(
|
||||
// Save data if not null
|
||||
repository.saveLastSavedAt(CURRENT_LOCATION)
|
||||
repository.saveCurrentWeatherToRoom(entityItem)
|
||||
onSuccess(Unit)
|
||||
} catch (e: Exception) {
|
||||
onError(e.message!!)
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ class WorldViewModel(
|
||||
} else {
|
||||
repository.getSingleWeather(locationName)
|
||||
}
|
||||
onSuccess(Unit)
|
||||
repository.saveCurrentWeatherToRoom(weatherEntity)
|
||||
repository.saveLastSavedAt(weatherEntity.id)
|
||||
} catch (e: IOException) {
|
||||
|
||||
@@ -15,6 +15,5 @@ class ViewHolderForecast(
|
||||
val adapter = GridForecastAdapter()
|
||||
adapter.addCurrent(forecasts)
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
}
|
||||
}
|
||||
@@ -35,5 +35,5 @@ fun <T> LiveData<T>.getOrAwaitValue(
|
||||
}
|
||||
|
||||
fun sleep(millis: Long = 1000) {
|
||||
runBlocking(Dispatchers.IO) { delay(millis) }
|
||||
runBlocking(Dispatchers.Default) { delay(millis) }
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import com.appttude.h_mal.atlas_weather.utils.sleep
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.InjectMockKs
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
@@ -28,6 +29,7 @@ class WorldViewModelTest : BaseTest() {
|
||||
@get:Rule
|
||||
val rule = InstantTaskExecutorRule()
|
||||
|
||||
@InjectMockKs
|
||||
lateinit var viewModel: WorldViewModel
|
||||
|
||||
@MockK(relaxed = true)
|
||||
@@ -36,12 +38,11 @@ class WorldViewModelTest : BaseTest() {
|
||||
@MockK
|
||||
lateinit var locationProvider: LocationProviderImpl
|
||||
|
||||
lateinit var weatherResponse: WeatherResponse
|
||||
private lateinit var weatherResponse: WeatherResponse
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockKAnnotations.init(this)
|
||||
viewModel = WorldViewModel(locationProvider, repository)
|
||||
|
||||
weatherResponse = getTestData("weather_sample.json", WeatherResponse::class.java)
|
||||
}
|
||||
@@ -49,18 +50,17 @@ class WorldViewModelTest : BaseTest() {
|
||||
@Test
|
||||
fun fetchDataForSingleLocation_validLocation_validReturn() {
|
||||
// Arrange
|
||||
val location = CURRENT_LOCATION
|
||||
val entityItem = EntityItem(CURRENT_LOCATION, FullWeather(weatherResponse).apply {
|
||||
temperatureUnit = "°C"
|
||||
locationString = CURRENT_LOCATION
|
||||
})
|
||||
|
||||
// Act
|
||||
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
||||
coEvery { locationProvider.getLatLongFromLocationName(CURRENT_LOCATION) } returns Pair(
|
||||
weatherResponse.lat,
|
||||
weatherResponse.lon
|
||||
)
|
||||
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
||||
coEvery {
|
||||
repository.getWeatherFromApi(
|
||||
weatherResponse.lat.toString(),
|
||||
@@ -77,10 +77,14 @@ class WorldViewModelTest : BaseTest() {
|
||||
every { repository.saveLastSavedAt(CURRENT_LOCATION) } returns Unit
|
||||
coEvery { repository.saveCurrentWeatherToRoom(entityItem) } returns Unit
|
||||
|
||||
viewModel.fetchDataForSingleLocation(location)
|
||||
viewModel.fetchDataForSingleLocation(CURRENT_LOCATION)
|
||||
|
||||
// Assert
|
||||
sleep(300)
|
||||
viewModel.uiState.observeForever {
|
||||
println(it.javaClass.name)
|
||||
}
|
||||
|
||||
sleep(3000)
|
||||
assertIs<ViewState.HasData<*>>(viewModel.uiState.getOrAwaitValue())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user