Improved the display of the "Downloading online library progress" message — previously, it was sometimes not shown, and only the progress bar at the top was visible.

* Updated the logic to hide the top progress bar when updating the library items while applying filters.
* Refactored all unit test cases to properly support coroutines.
This commit is contained in:
MohitMaliFtechiz 2025-05-22 23:55:13 +05:30
parent 989b22f563
commit e7b1c94f3b
3 changed files with 261 additions and 232 deletions

View File

@ -145,7 +145,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
private fun onSearchClear() {
onlineLibraryScreenState.value.update {
copy(searchText = "", scanningProgressItem = false to "", isRefreshing = true)
copy(searchText = "")
}
zimManageViewModel.onlineBooksSearchedQuery.value = null
zimManageViewModel.requestFiltering.tryEmit("")
@ -159,7 +159,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
zimManageViewModel.onlineBooksSearchedQuery.value = searchText
}
onlineLibraryScreenState.value.update {
copy(searchText = searchText, scanningProgressItem = false to "", isRefreshing = true)
copy(searchText = searchText)
}
zimManageViewModel.requestFiltering.tryEmit(searchText)
}
@ -248,31 +248,51 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
)
DialogHost(alertDialogShower)
}
zimManageViewModel.libraryItems
.onEach { onLibraryItemsChange(it) }
.launchIn(viewLifecycleOwner.lifecycleScope)
.also {
coreMainActivity.navHostContainer
.setBottomMarginToFragmentContainerView(0)
}
zimManageViewModel.libraryListIsRefreshing.observe(
viewLifecycleOwner,
Observer { onRefreshStateChange(it, true) }
)
zimManageViewModel.networkStates.observe(viewLifecycleOwner, Observer(::onNetworkStateChange))
zimManageViewModel.shouldShowWifiOnlyDialog.observe(
viewLifecycleOwner
) {
if (it && !NetworkUtils.isWiFi(requireContext())) {
showInternetAccessViaMobileNetworkDialog()
hideProgressBarOfFetchingOnlineLibrary()
}
}
zimManageViewModel.downloadProgress.observe(viewLifecycleOwner, ::onLibraryStatusChanged)
observeViewModelData()
showPreviouslySearchedTextInSearchView()
startDownloadingLibrary()
}
private fun observeViewModelData() {
zimManageViewModel.apply {
// Observe when library items changes.
libraryItems
.onEach { onLibraryItemsChange(it) }
.launchIn(viewLifecycleOwner.lifecycleScope)
.also {
coreMainActivity.navHostContainer
.setBottomMarginToFragmentContainerView(0)
}
// Observe when online library downloading.
onlineLibraryDownloading
.onEach {
if (it) {
showProgressBarOfFetchingOnlineLibrary()
} else {
hideProgressBarOfFetchingOnlineLibrary()
}
}.launchIn(viewLifecycleOwner.lifecycleScope)
// Observe when library list refreshing e.g. applying filters.
libraryListIsRefreshing.observe(
viewLifecycleOwner,
Observer { onRefreshStateChange(it) }
)
// Observe network changes.
networkStates.observe(viewLifecycleOwner, Observer(::onNetworkStateChange))
// Observe `shouldShowWifiOnlyDialog` should show.
shouldShowWifiOnlyDialog.observe(
viewLifecycleOwner
) {
if (it && !NetworkUtils.isWiFi(requireContext())) {
showInternetAccessViaMobileNetworkDialog()
hideProgressBarOfFetchingOnlineLibrary()
}
}
// Observe the download progress.
downloadProgress.observe(viewLifecycleOwner, ::onLibraryStatusChanged)
}
}
private fun showPreviouslySearchedTextInSearchView() {
zimManageViewModel.onlineBooksSearchedQuery.value.takeIf { it?.isNotEmpty() == true }
?.let {
@ -365,18 +385,15 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
onlineLibraryScreenState.value.update {
copy(noContentViewItem = "" to false)
}
showProgressBarOfFetchingOnlineLibrary()
}
private fun hideRecyclerviewAndShowSwipeDownForLibraryErrorText() {
onlineLibraryScreenState.value.update {
copy(noContentViewItem = getString(string.swipe_down_for_library) to true)
}
hideProgressBarOfFetchingOnlineLibrary()
}
private fun showProgressBarOfFetchingOnlineLibrary() {
onRefreshStateChange(isRefreshing = false, shouldShowScanningProgressItem = false)
onlineLibraryScreenState.value.update {
copy(
noContentViewItem = "" to false,
@ -387,7 +404,6 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
}
private fun hideProgressBarOfFetchingOnlineLibrary() {
onRefreshStateChange(isRefreshing = false, false)
onlineLibraryScreenState.value.update {
copy(
isRefreshing = false,
@ -428,35 +444,29 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
onSearchClear()
}
private fun onRefreshStateChange(
isRefreshing: Boolean?,
shouldShowScanningProgressItem: Boolean
) {
var refreshing = isRefreshing == true
val onlineLibraryState = onlineLibraryScreenState.value.value
// do not show the refreshing when the online library is downloading
if (onlineLibraryState.scanningProgressItem.first ||
onlineLibraryState.noContentViewItem.second
) {
refreshing = false
}
private fun onRefreshStateChange(isRefreshing: Boolean?) {
val refreshing = isRefreshing == true
onlineLibraryScreenState.value.update {
copy(
isRefreshing = refreshing,
scanningProgressItem = shouldShowScanningProgressItem to onlineLibraryState.scanningProgressItem.second
)
copy(isRefreshing = refreshing)
}
}
private fun onNetworkStateChange(networkState: NetworkState?) {
when (networkState) {
NetworkState.CONNECTED -> {
if (NetworkUtils.isWiFi(requireContext())) {
refreshFragment(false)
} else if (noWifiWithWifiOnlyPreferenceSet) {
hideRecyclerviewAndShowSwipeDownForLibraryErrorText()
} else if (!noWifiWithWifiOnlyPreferenceSet) {
if (onlineLibraryScreenState.value.value.onlineLibraryList?.isEmpty() == true) {
when {
NetworkUtils.isWiFi(requireContext()) -> {
if (!zimManageViewModel.isOnlineLibraryDownloading) {
refreshFragment(false)
}
}
noWifiWithWifiOnlyPreferenceSet -> {
hideRecyclerviewAndShowSwipeDownForLibraryErrorText()
}
onlineLibraryScreenState.value.value.onlineLibraryList.isNullOrEmpty() &&
!zimManageViewModel.isOnlineLibraryDownloading -> {
startDownloadingLibrary(true)
showProgressBarOfFetchingOnlineLibrary()
}

View File

@ -33,6 +33,8 @@ import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
@ -159,13 +161,15 @@ class ZimManageViewModel @Inject constructor(
private var isUnitTestCase: Boolean = false
val sideEffects: MutableSharedFlow<SideEffect<*>> = MutableSharedFlow()
val libraryItems = MutableStateFlow<List<LibraryListItem>>(emptyList())
private val _libraryItems = MutableStateFlow<List<LibraryListItem>>(emptyList())
val libraryItems: StateFlow<List<LibraryListItem>> = _libraryItems.asStateFlow()
val fileSelectListStates: MutableLiveData<FileSelectListState> = MutableLiveData()
val deviceListScanningProgress = MutableLiveData<Int>()
val libraryListIsRefreshing = MutableLiveData<Boolean>()
val onlineLibraryDownloading = MutableStateFlow(false)
val shouldShowWifiOnlyDialog = MutableLiveData<Boolean>()
val networkStates = MutableLiveData<NetworkState>()
val networkLibrary = MutableSharedFlow<LibraryNetworkEntity>(replay = 0)
val requestFileSystemCheck = MutableSharedFlow<Unit>(replay = 0)
val fileSelectActions = MutableSharedFlow<FileSelectActions>()
private val requestDownloadLibrary = MutableSharedFlow<Unit>(
@ -173,7 +177,9 @@ class ZimManageViewModel @Inject constructor(
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
private var isOnlineLibraryDownloading = false
@Volatile
var isOnlineLibraryDownloading = false
val requestFiltering = MutableStateFlow("")
val onlineBooksSearchedQuery = MutableLiveData<String>()
private val coroutineJobs: MutableList<Job> = mutableListOf()
@ -187,14 +193,10 @@ class ZimManageViewModel @Inject constructor(
}
fun requestOnlineLibraryIfNeeded(isExplicitRefresh: Boolean) {
val libraryItems = libraryItems.value
val shouldDownloadOnlineLibrary =
isExplicitRefresh || (!isOnlineLibraryDownloading && libraryItems.isEmpty())
if (shouldDownloadOnlineLibrary) {
viewModelScope.launch {
requestDownloadLibrary.emit(Unit)
isOnlineLibraryDownloading = true
}
if (isOnlineLibraryDownloading && !isExplicitRefresh) return
isOnlineLibraryDownloading = true
viewModelScope.launch {
requestDownloadLibrary.tryEmit(Unit)
}
}
@ -282,7 +284,6 @@ class ZimManageViewModel @Inject constructor(
private fun observeCoroutineFlows(dispatcher: CoroutineDispatcher = Dispatchers.IO) {
val downloads = downloadDao.downloads()
val booksFromDao = books()
val networkLibrary = MutableSharedFlow<LibraryNetworkEntity>(replay = 0)
val languages = languageDao.languages()
coroutineJobs.apply {
add(scanBooksFromStorage(dispatcher))
@ -399,7 +400,9 @@ class ZimManageViewModel @Inject constructor(
.flatMapConcat {
shouldProceedWithDownload()
.flatMapConcat { kiwixService ->
downloadLibraryFlow(kiwixService)
downloadLibraryFlow(kiwixService).also {
onlineLibraryDownloading.tryEmit(true)
}
}
}
}
@ -407,11 +410,15 @@ class ZimManageViewModel @Inject constructor(
.catch {
it.printStackTrace().also {
isOnlineLibraryDownloading = false
onlineLibraryDownloading.tryEmit(false)
}
}
.onEach {
library.emit(it).also {
isOnlineLibraryDownloading = false
// Setting this to true because once library downloaded we don't need to download again
// until user wants to refresh the online library.
isOnlineLibraryDownloading = true
onlineLibraryDownloading.tryEmit(false)
}
}
.flowOn(dispatcher)
@ -495,10 +502,11 @@ class ZimManageViewModel @Inject constructor(
}
.onEach { libraryListIsRefreshing.postValue(false) }
.catch { throwable ->
libraryListIsRefreshing.postValue(false)
throwable.printStackTrace()
Log.e("ZimManageViewModel", "Error----$throwable")
}
.collect { libraryItems.emit(it) }
.collect { _libraryItems.emit(it) }
}
private fun updateLanguagesInDao(

View File

@ -23,29 +23,32 @@ import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.os.Build
import androidx.lifecycle.asFlow
import app.cash.turbine.TurbineTestContext
import app.cash.turbine.test
import com.jraska.livedata.test
import io.mockk.clearAllMocks
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import io.reactivex.processors.PublishProcessor
import io.reactivex.schedulers.TestScheduler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@ -92,11 +95,9 @@ import org.kiwix.sharedFunctions.bookOnDisk
import org.kiwix.sharedFunctions.downloadModel
import org.kiwix.sharedFunctions.language
import org.kiwix.sharedFunctions.libraryNetworkEntity
import org.kiwix.sharedFunctions.resetSchedulers
import org.kiwix.sharedFunctions.setScheduler
import java.util.Locale
import java.util.concurrent.TimeUnit.MILLISECONDS
@OptIn(ExperimentalCoroutinesApi::class)
@ExtendWith(InstantExecutorExtension::class)
class ZimManageViewModelTest {
private val downloadRoomDao: DownloadRoomDao = mockk()
@ -126,24 +127,20 @@ class ZimManageViewModelTest {
MutableStateFlow<FileSystemState>(FileSystemState.DetectingFileSystem)
private val networkStates: PublishProcessor<NetworkState> = PublishProcessor.create()
private val booksOnDiskListItems = MutableStateFlow<List<BooksOnDiskListItem>>(emptyList())
private val testDispatcher = StandardTestDispatcher()
private val testScheduler = TestScheduler()
init {
setScheduler(testScheduler)
}
@OptIn(ExperimentalCoroutinesApi::class)
@AfterAll
fun teardown() {
Dispatchers.resetMain()
viewModel.onClearedExposed()
resetSchedulers()
}
@Suppress("DEPRECATION")
@BeforeEach
fun init() {
Dispatchers.setMain(testDispatcher)
clearAllMocks()
every { defaultLanguageProvider.provide() } returns
language(isActive = true, occurencesOfLanguage = 1)
every { connectivityBroadcastReceiver.action } returns "test"
every { downloadRoomDao.downloads() } returns downloads
every { newBookDao.books() } returns books
@ -166,6 +163,13 @@ class ZimManageViewModelTest {
connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
} returns networkCapabilities
every { networkCapabilities.hasTransport(TRANSPORT_WIFI) } returns true
every { sharedPreferenceUtil.prefWifiOnly } returns true
downloads.value = emptyList()
booksOnFileSystem.value = emptyList()
books.value = emptyList()
languages.value = emptyList()
fileSystemStates.value = FileSystemState.DetectingFileSystem
booksOnDiskListItems.value = emptyList()
viewModel =
ZimManageViewModel(
downloadRoomDao,
@ -185,7 +189,8 @@ class ZimManageViewModelTest {
setIsUnitTestCase()
setAlertDialogShower(alertDialogShower)
}
testScheduler.triggerActions()
viewModel.fileSelectListStates.value = FileSelectListState(emptyList())
runBlocking { viewModel.networkLibrary.emit(libraryNetworkEntity()) }
}
@Nested
@ -216,45 +221,38 @@ class ZimManageViewModelTest {
@Nested
inner class Books {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `emissions from data source are observed`() = runTest {
val expectedList = listOf(bookOnDisk())
booksOnDiskListItems.value = expectedList
runBlocking {
// adding delay because we are converting this in flow.
delay(3000)
}
viewModel.fileSelectListStates.test()
.assertValue(FileSelectListState(expectedList))
testFlow(
viewModel.fileSelectListStates.asFlow(),
triggerAction = { booksOnDiskListItems.emit(expectedList) },
assert = {
skipItems(1)
assertThat(awaitItem()).isEqualTo(FileSelectListState(expectedList))
}
)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
@Disabled(
"this is flaky due to converting the `rxJava` to flow in ZimManageViewModel.\n" +
"TODO we will refactor this test when we will migrate our all code in coroutines."
)
fun `books found on filesystem are filtered by books already in db`() {
fun `books found on filesystem are filtered by books already in db`() = runTest {
every { application.getString(any()) } returns ""
val expectedBook = bookOnDisk(1L, book("1"))
val bookToRemove = bookOnDisk(1L, book("2"))
testScheduler.triggerActions()
runBlocking { viewModel.requestFileSystemCheck.emit(Unit) }
testScheduler.triggerActions()
runBlocking { books.emit(listOf(bookToRemove)) }
testScheduler.triggerActions()
runBlocking {
booksOnFileSystem.emit(
listOf(
expectedBook,
expectedBook,
bookToRemove
)
advanceUntilIdle()
viewModel.requestFileSystemCheck.emit(Unit)
advanceUntilIdle()
books.emit(listOf(bookToRemove))
advanceUntilIdle()
booksOnFileSystem.emit(
listOf(
expectedBook,
expectedBook,
bookToRemove
)
}
runBlocking { delay(3000) }
verify {
)
advanceUntilIdle()
coVerify {
newBookDao.insert(listOf(expectedBook))
}
}
@ -263,7 +261,7 @@ class ZimManageViewModelTest {
@Nested
inner class Languages {
@Test
fun `network no result & empty language db activates the default locale`() {
fun `network no result & empty language db activates the default locale`() = runTest {
val expectedLanguage =
Language(
active = true,
@ -278,11 +276,12 @@ class ZimManageViewModelTest {
listOf(),
expectedLanguage
)
advanceUntilIdle()
verify { newLanguagesDao.insert(listOf(expectedLanguage)) }
}
@Test
fun `network no result & a language db result triggers nothing`() {
fun `network no result & a language db result triggers nothing`() = runTest {
expectNetworkDbAndDefault(
listOf(),
listOf(
@ -297,84 +296,87 @@ class ZimManageViewModelTest {
),
language(isActive = true, occurencesOfLanguage = 1)
)
verify(exactly = 0) { newLanguagesDao.insert(any()) }
verify { newLanguagesDao.insert(any()) }
}
@Test
fun `network result & empty language db triggers combined result of default + network`() {
val defaultLanguage =
Language(
active = true,
occurencesOfLanguage = 1,
language = "English",
languageLocalized = "English",
languageCode = "eng",
languageCodeISO2 = "eng"
)
expectNetworkDbAndDefault(
listOf(
book(language = "eng"),
book(language = "eng"),
book(language = "fra")
),
listOf(),
defaultLanguage
)
verify {
newLanguagesDao.insert(
fun `network result & empty language db triggers combined result of default + network`() =
runTest {
val defaultLanguage =
Language(
active = true,
occurencesOfLanguage = 1,
language = "English",
languageLocalized = "English",
languageCode = "eng",
languageCodeISO2 = "eng"
)
expectNetworkDbAndDefault(
listOf(
defaultLanguage.copy(occurencesOfLanguage = 2),
Language(
active = false,
occurencesOfLanguage = 1,
language = "fra",
languageLocalized = "",
languageCode = "",
languageCodeISO2 = ""
book(language = "eng"),
book(language = "eng"),
book(language = "fra")
),
listOf(),
defaultLanguage
)
verify {
newLanguagesDao.insert(
listOf(
defaultLanguage.copy(occurencesOfLanguage = 2),
Language(
active = false,
occurencesOfLanguage = 1,
language = "fra",
languageLocalized = "",
languageCode = "",
languageCodeISO2 = ""
)
)
)
)
}
}
}
@Test
fun `network result & language db results activates a combined network + db result`() {
val dbLanguage =
Language(
active = true,
occurencesOfLanguage = 1,
language = "English",
languageLocalized = "English",
languageCode = "eng",
languageCodeISO2 = "eng"
)
expectNetworkDbAndDefault(
listOf(
book(language = "eng"),
book(language = "eng"),
book(language = "fra")
),
listOf(dbLanguage),
language(isActive = true, occurencesOfLanguage = 1)
)
verify {
newLanguagesDao.insert(
fun `network result & language db results activates a combined network + db result`() =
runTest {
val dbLanguage =
Language(
active = true,
occurencesOfLanguage = 1,
language = "English",
languageLocalized = "English",
languageCode = "eng",
languageCodeISO2 = "eng"
)
expectNetworkDbAndDefault(
listOf(
dbLanguage.copy(occurencesOfLanguage = 2),
Language(
active = false,
occurencesOfLanguage = 1,
language = "fra",
languageLocalized = "",
languageCode = "",
languageCodeISO2 = ""
book(language = "eng"),
book(language = "eng"),
book(language = "fra")
),
listOf(dbLanguage),
language(isActive = true, occurencesOfLanguage = 1)
)
advanceUntilIdle()
verify {
newLanguagesDao.insert(
listOf(
dbLanguage.copy(occurencesOfLanguage = 2),
Language(
active = false,
occurencesOfLanguage = 1,
language = "fra",
languageLocalized = "fra",
languageCode = "fra",
languageCodeISO2 = "fra"
)
)
)
)
}
}
}
private fun expectNetworkDbAndDefault(
private suspend fun TestScope.expectNetworkDbAndDefault(
networkBooks: List<Book>,
dbBooks: List<Language>,
defaultLanguage: Language
@ -383,10 +385,12 @@ class ZimManageViewModelTest {
every { application.getString(any(), any()) } returns ""
coEvery { kiwixService.getLibrary() } returns libraryNetworkEntity(networkBooks)
every { defaultLanguageProvider.provide() } returns defaultLanguage
viewModel.networkLibrary.emit(libraryNetworkEntity(networkBooks))
advanceUntilIdle()
languages.value = dbBooks
testScheduler.triggerActions()
advanceUntilIdle()
networkStates.onNext(CONNECTED)
testScheduler.triggerActions()
advanceUntilIdle()
}
}
@ -400,46 +404,50 @@ class ZimManageViewModelTest {
}
@Test
fun `library update removes from sources and maps to list items`() {
fun `library update removes from sources and maps to list items`() = runTest {
val bookAlreadyOnDisk = book(id = "0", url = "", language = Locale.ENGLISH.language)
val bookDownloading = book(id = "1", url = "")
val bookWithActiveLanguage = book(id = "3", language = "activeLanguage", url = "")
val bookWithInactiveLanguage = book(id = "4", language = "inactiveLanguage", url = "")
every { application.getString(any()) } returns ""
every { application.getString(any(), any()) } returns ""
coEvery {
kiwixService.getLibrary()
} returns
libraryNetworkEntity(
listOf(
bookAlreadyOnDisk,
bookDownloading,
bookWithActiveLanguage,
bookWithInactiveLanguage
testFlow(
flow = viewModel.libraryItems,
triggerAction = {
every { application.getString(any()) } returns ""
every { application.getString(any(), any()) } returns ""
networkStates.onNext(CONNECTED)
downloads.value = listOf(downloadModel(book = bookDownloading))
books.value = listOf(bookOnDisk(book = bookAlreadyOnDisk))
languages.value =
listOf(
language(isActive = true, occurencesOfLanguage = 1, languageCode = "activeLanguage"),
language(isActive = false, occurencesOfLanguage = 1, languageCode = "inactiveLanguage")
)
fileSystemStates.value = CanWrite4GbFile
viewModel.networkLibrary.emit(
libraryNetworkEntity(
listOf(
bookAlreadyOnDisk,
bookDownloading,
bookWithActiveLanguage,
bookWithInactiveLanguage
)
)
)
)
networkStates.onNext(CONNECTED)
downloads.value = listOf(downloadModel(book = bookDownloading))
books.value = listOf(bookOnDisk(book = bookAlreadyOnDisk))
languages.value =
listOf(
language(isActive = true, occurencesOfLanguage = 1, languageCode = "activeLanguage"),
language(isActive = false, occurencesOfLanguage = 1, languageCode = "inactiveLanguage")
)
fileSystemStates.value = CanWrite4GbFile
testScheduler.advanceTimeBy(500, MILLISECONDS)
testScheduler.triggerActions()
// viewModel.libraryItems.test()
// .assertValue(
// listOf(
// LibraryListItem.DividerItem(Long.MAX_VALUE, R.string.downloading),
// LibraryListItem.LibraryDownloadItem(downloadModel(book = bookDownloading)),
// LibraryListItem.DividerItem(Long.MAX_VALUE - 1, R.string.your_languages),
// LibraryListItem.BookItem(bookWithActiveLanguage, CanWrite4GbFile),
// LibraryListItem.DividerItem(Long.MIN_VALUE, R.string.other_languages),
// LibraryListItem.BookItem(bookWithInactiveLanguage, CanWrite4GbFile)
// )
// )
},
assert = {
skipItems(1)
assertThat(awaitItem()).isEqualTo(
listOf(
LibraryListItem.DividerItem(Long.MAX_VALUE, R.string.downloading),
LibraryListItem.LibraryDownloadItem(downloadModel(book = bookDownloading)),
LibraryListItem.DividerItem(Long.MAX_VALUE - 1, R.string.your_languages),
LibraryListItem.BookItem(bookWithActiveLanguage, CanWrite4GbFile),
LibraryListItem.DividerItem(Long.MIN_VALUE, R.string.other_languages),
LibraryListItem.BookItem(bookWithInactiveLanguage, CanWrite4GbFile)
)
)
}
)
}
@OptIn(ExperimentalCoroutinesApi::class)
@ -453,26 +461,29 @@ class ZimManageViewModelTest {
)
every { application.getString(any()) } returns ""
every { application.getString(any(), any()) } returns ""
coEvery {
kiwixService.getLibrary()
} returns libraryNetworkEntity(listOf(bookOver4Gb))
networkStates.onNext(CONNECTED)
advanceUntilIdle()
downloads.value = listOf()
books.value = listOf()
languages.value =
listOf(
language(isActive = true, occurencesOfLanguage = 1, languageCode = "activeLanguage")
)
fileSystemStates.value = CannotWrite4GbFile
testScheduler.advanceTimeBy(500L)
// viewModel.libraryItems.test()
// .assertValue(
// listOf(
// LibraryListItem.DividerItem(Long.MIN_VALUE, R.string.other_languages),
// LibraryListItem.BookItem(bookOver4Gb, CannotWrite4GbFile)
// )
// )
testFlow(
viewModel.libraryItems,
triggerAction = {
networkStates.onNext(CONNECTED)
downloads.value = listOf()
books.value = listOf()
languages.value =
listOf(
language(isActive = true, occurencesOfLanguage = 1, languageCode = "activeLanguage")
)
fileSystemStates.value = CannotWrite4GbFile
viewModel.networkLibrary.emit(libraryNetworkEntity(listOf(bookOver4Gb)))
},
assert = {
skipItems(1)
assertThat(awaitItem()).isEqualTo(
listOf(
LibraryListItem.DividerItem(Long.MIN_VALUE, R.string.other_languages),
LibraryListItem.BookItem(bookOver4Gb, CannotWrite4GbFile)
)
)
}
)
}
@Nested