diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryFragment.kt index 9630ed846..023d2f7cb 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/online/OnlineLibraryFragment.kt @@ -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() } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt index 43932f4ac..b9a726a46 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt @@ -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> = MutableSharedFlow() - val libraryItems = MutableStateFlow>(emptyList()) + private val _libraryItems = MutableStateFlow>(emptyList()) + val libraryItems: StateFlow> = _libraryItems.asStateFlow() val fileSelectListStates: MutableLiveData = MutableLiveData() val deviceListScanningProgress = MutableLiveData() val libraryListIsRefreshing = MutableLiveData() + val onlineLibraryDownloading = MutableStateFlow(false) val shouldShowWifiOnlyDialog = MutableLiveData() val networkStates = MutableLiveData() - + val networkLibrary = MutableSharedFlow(replay = 0) val requestFileSystemCheck = MutableSharedFlow(replay = 0) val fileSelectActions = MutableSharedFlow() private val requestDownloadLibrary = MutableSharedFlow( @@ -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() private val coroutineJobs: MutableList = 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(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( diff --git a/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt b/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt index 104e657cf..ef9ab08b8 100644 --- a/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt +++ b/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt @@ -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.DetectingFileSystem) private val networkStates: PublishProcessor = PublishProcessor.create() private val booksOnDiskListItems = MutableStateFlow>(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, dbBooks: List, 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