diff --git a/app/src/androidTest/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt b/app/src/androidTest/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt index 96aef5d9..f86fffa6 100644 --- a/app/src/androidTest/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt +++ b/app/src/androidTest/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt @@ -1,7 +1,5 @@ package com.willowtree.vocable.settings -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.lifecycle.Observer import androidx.room.Room import androidx.test.core.app.ApplicationProvider import app.cash.turbine.test @@ -20,13 +18,7 @@ import com.willowtree.vocable.room.VocableDatabase import com.willowtree.vocable.utility.FakeDateProvider import com.willowtree.vocable.utility.StubLegacyCategoriesAndPhrasesRepository import com.willowtree.vocable.utility.VocableKoinTestRule -import junit.framework.TestCase -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.advanceUntilIdle import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test @@ -39,9 +31,6 @@ class EditCategoriesViewModelTest { @get:Rule val mainDispatcherRule = MainDispatcherRule() - @get:Rule - val instantExecutorRule = InstantTaskExecutorRule() - private val database = Room.inMemoryDatabaseBuilder( ApplicationProvider.getApplicationContext(), VocableDatabase::class.java @@ -85,81 +74,54 @@ class EditCategoriesViewModelTest { ) } - // 1. refreshing categories without adding new category remains at first index - // 2. adding new category and refreshing once flips to new category's index - // 3. adding new category and refreshing twice flips to first index - // 4. adding new category amongst hidden categories and refreshing once flips to new category's index (before hidden categories) - - @OptIn(ExperimentalCoroutinesApi::class) + // TODO: CC - the following 4 tests are flaky due to the way refreshCategories() is implemented @Test - fun refreshing_categories_without_adding_new_category_remains_at_first_index() = runTest(UnconfinedTestDispatcher()) { + fun refreshing_categories_without_adding_new_category_remains_at_first_index() = runTest { val vm = createViewModel() vm.refreshCategories() - var index: Int? = null - val observer = Observer { value -> - index = value - } - - try { - vm.lastViewedIndex.observeForever(observer) - advanceUntilIdle() - assertEquals(0, index) - } finally { - vm.lastViewedIndex.removeObserver(observer) + vm.liveLastViewedIndex.test { + assertEquals(0, awaitItem()) } } - @OptIn(ExperimentalCoroutinesApi::class) @Test - fun adding_new_category_and_refreshing_once_flips_to_new_categorys_index() = runTest(UnconfinedTestDispatcher()) { + fun adding_new_category_and_refreshing_once_flips_to_new_categorys_index() = runTest { + val vm = createViewModel() vm.refreshCategories() categoriesUseCase.addCategory("new category") vm.refreshCategories() - var index: Int? = null - val observer = Observer { value -> - index = value - } - - try { - vm.lastViewedIndex.observeForever(observer) - advanceUntilIdle() - assertEquals(7, index) - } finally { - vm.lastViewedIndex.removeObserver(observer) + vm.liveLastViewedIndex.test { + assertEquals(0, awaitItem()) + assertEquals(7, awaitItem()) } } - @OptIn(ExperimentalCoroutinesApi::class) @Test - fun adding_new_category_and_refreshing_twice_flips_to_first_index() = runTest(UnconfinedTestDispatcher()) { + fun adding_new_category_and_refreshing_twice_flips_to_first_index() = runTest { val vm = createViewModel() vm.refreshCategories() categoriesUseCase.addCategory("new category") - vm.refreshCategories() - vm.refreshCategories() - var index: Int? = null - val observer = Observer { value -> - index = value + vm.refreshCategories() + vm.liveLastViewedIndex.test { + assertEquals(0, awaitItem()) + assertEquals(7, awaitItem()) } - try { - vm.lastViewedIndex.observeForever(observer) - advanceUntilIdle() - assertEquals(0, index) - } finally { - vm.lastViewedIndex.removeObserver(observer) + vm.refreshCategories() + vm.liveLastViewedIndex.test { + assertEquals(7, awaitItem()) + assertEquals(0, awaitItem()) } } - @OptIn(ExperimentalCoroutinesApi::class) @Test - fun adding_new_category_amongst_hidden_categories_and_refreshing_once_flips_to_last_non_hidden_index() = runTest(UnconfinedTestDispatcher()) { + fun adding_new_category_amongst_hidden_categories_and_refreshing_once_flips_to_last_non_hidden_index() = runTest { val vm = createViewModel() categoriesUseCase.updateCategoryHidden("preset_general", hidden = true) categoriesUseCase.updateCategoryHidden("preset_basic_needs", hidden = true) @@ -168,22 +130,12 @@ class EditCategoriesViewModelTest { categoriesUseCase.addCategory("new category") - categoriesUseCase.categories().first() - vm.refreshCategories() - - var index: Int? = null - val observer = Observer { value -> - index = value + vm.liveLastViewedIndex.test { + assertEquals(0, awaitItem()) + assertEquals(5, awaitItem()) } - try { - vm.lastViewedIndex.observeForever(observer) - advanceUntilIdle() - assertEquals(5, index) - } finally { - vm.lastViewedIndex.removeObserver(observer) - } } @Test diff --git a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesFragment.kt b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesFragment.kt index c840c491..9796313c 100644 --- a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesFragment.kt +++ b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesFragment.kt @@ -7,6 +7,7 @@ import androidx.fragment.app.FragmentManager import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import androidx.lifecycle.viewModelScope import androidx.navigation.fragment.findNavController import androidx.viewpager2.widget.ViewPager2 import com.willowtree.vocable.BaseFragment @@ -16,6 +17,7 @@ import com.willowtree.vocable.R import com.willowtree.vocable.databinding.FragmentEditCategoriesBinding import com.willowtree.vocable.presets.Category import com.willowtree.vocable.utils.VocableFragmentStateAdapter +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import org.koin.androidx.viewmodel.ViewModelOwner import org.koin.androidx.viewmodel.ext.android.viewModel @@ -121,15 +123,12 @@ class EditCategoriesFragment : BaseFragment() { } } - editCategoriesViewModel.lastViewedIndex.observe(viewLifecycleOwner) { - it?.let { index -> - val pageNum = index / maxEditCategories - // Wait until view pager has finished its layout - binding.editCategoriesViewPager.post { + editCategoriesViewModel.viewModelScope.launch { + editCategoriesViewModel.liveLastViewedIndex.collect { + val pageNum = it/maxEditCategories if (isAdded && binding.editCategoriesViewPager.currentItem != pageNum) { binding.editCategoriesViewPager.setCurrentItem(pageNum, false) } - } } } } diff --git a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt index 682a65c8..066b43c9 100644 --- a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt +++ b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt @@ -1,12 +1,12 @@ package com.willowtree.vocable.settings -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.willowtree.vocable.ICategoriesUseCase import com.willowtree.vocable.presets.Category +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch class EditCategoriesViewModel( @@ -15,8 +15,7 @@ class EditCategoriesViewModel( val categoryList = categoriesUseCase.categories() - private val liveLastViewedIndex = MutableLiveData() - val lastViewedIndex: LiveData = liveLastViewedIndex + val liveLastViewedIndex = MutableStateFlow(0) private var overallCategories = listOf() @@ -31,20 +30,20 @@ class EditCategoriesViewModel( if (oldCategories.isNotEmpty() && oldCategories.size < overallCategories.size) { when (val firstHiddenIndex = overallCategories.indexOfFirst { it.hidden }) { -1 -> { - liveLastViewedIndex.postValue(overallCategories.size - 1) + liveLastViewedIndex.update { overallCategories.size - 1 } } 0 -> { - liveLastViewedIndex.postValue(0) + liveLastViewedIndex.update { 0 } } else -> { - liveLastViewedIndex.postValue(firstHiddenIndex - 1) + liveLastViewedIndex.update { firstHiddenIndex - 1 } } } } else { - liveLastViewedIndex.postValue(0) + liveLastViewedIndex.update { 0 } } } }