From dd43e9c2211395c367302af6f6347ca20282787f Mon Sep 17 00:00:00 2001 From: cinadia Date: Thu, 23 May 2024 14:02:18 -0700 Subject: [PATCH 1/4] selects the next non-hidden category to display when the currently-displayed category is hidden --- .../vocable/presets/PresetsViewModel.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/willowtree/vocable/presets/PresetsViewModel.kt b/app/src/main/java/com/willowtree/vocable/presets/PresetsViewModel.kt index c3faf183..edbccd83 100644 --- a/app/src/main/java/com/willowtree/vocable/presets/PresetsViewModel.kt +++ b/app/src/main/java/com/willowtree/vocable/presets/PresetsViewModel.kt @@ -34,7 +34,20 @@ class PresetsViewModel( categoriesUseCase.categories(), liveSelectedCategoryId ) { categories, selectedId -> - categories.find { it.categoryId == selectedId } + val currentCategory = categories.find { it.categoryId == selectedId } + if (currentCategory?.hidden == true) { + val newSortOrder = (currentCategory.sortOrder + 1) + val newCategory = categories.find { it.sortOrder == newSortOrder} + if (newCategory != null) { + liveSelectedCategoryId.update { newCategory.categoryId } + categories.find { it.sortOrder == newSortOrder} + } else { + liveSelectedCategoryId.update { categories.first().categoryId } + categories.first() + } + } else { + currentCategory + } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000L), null) val selectedCategoryLiveData: LiveData = selectedCategory.asLiveData() From 7332c7d88e000a2145760b07e3efc136d1caa147 Mon Sep 17 00:00:00 2001 From: Cindy Cui Date: Fri, 24 May 2024 14:56:44 -0700 Subject: [PATCH 2/4] add automated tests --- .../willowtree/vocable/screens/MainScreen.kt | 33 +++++++ .../vocable/tests/MainScreenTest.kt | 87 ++++++++++++++++++- 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/app/src/androidTest/java/com/willowtree/vocable/screens/MainScreen.kt b/app/src/androidTest/java/com/willowtree/vocable/screens/MainScreen.kt index 9a376fe8..97c2113f 100644 --- a/app/src/androidTest/java/com/willowtree/vocable/screens/MainScreen.kt +++ b/app/src/androidTest/java/com/willowtree/vocable/screens/MainScreen.kt @@ -4,6 +4,7 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.* import com.willowtree.vocable.R +import com.willowtree.vocable.customviews.NoSayTextButton import com.willowtree.vocable.customviews.VocableButton import com.willowtree.vocable.utility.tap import org.hamcrest.CoreMatchers.allOf @@ -48,6 +49,10 @@ class MainScreen { } } + fun verifyGivenCategoryDisplay(category: String) { + onView(withText(category)).check(matches(isDisplayed())) + } + // Taps on the selected phrase fun tapPhrase(phraseText: String) { onView(withText(phraseText)).tap() @@ -63,6 +68,34 @@ class MainScreen { val keyboardNavitgationButton = onView(withId(R.id.keyboard_button)) val settingsNavigationButton = onView(withId(R.id.settings_button)) + // Settings buttons + val editCategoriesButton = onView(withId(R.id.edit_categories_button)) + val showCategorySwitch = onView(withId(R.id.show_category_switch)) + val editOptionsBackButton = onView(withId(R.id.edit_options_back_button)) + val backButton = onView(withId(R.id.back_button)) + val closeSettingsButton = onView(withId(R.id.settings_close_button)) + val editGeneralCategoryButton = onView( + allOf( + instanceOf(NoSayTextButton::class.java), + withId(R.id.individual_edit_category_button), + withText("General") + ) + ) + val editRecentCategoryButton = onView( + allOf( + instanceOf(NoSayTextButton::class.java), + withId(R.id.individual_edit_category_button), + withText("Recents") + ) + ) + val editBasicNeedsCategoryButton = onView( + allOf( + instanceOf(NoSayTextButton::class.java), + withId(R.id.individual_edit_category_button), + withText("Basic Needs") + ) + ) + // Categories and preset phrases val categoryBackButton = onView(withId(R.id.category_back_button)) val categoryForwardButton = onView(withId(R.id.category_forward_button)) diff --git a/app/src/androidTest/java/com/willowtree/vocable/tests/MainScreenTest.kt b/app/src/androidTest/java/com/willowtree/vocable/tests/MainScreenTest.kt index e79c3bc4..a29c9ad6 100644 --- a/app/src/androidTest/java/com/willowtree/vocable/tests/MainScreenTest.kt +++ b/app/src/androidTest/java/com/willowtree/vocable/tests/MainScreenTest.kt @@ -1,11 +1,9 @@ package com.willowtree.vocable.tests import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.rule.GrantPermissionRule import com.willowtree.vocable.screens.MainScreen import com.willowtree.vocable.utility.assertTextMatches -import org.junit.Ignore -import org.junit.Rule +import com.willowtree.vocable.utility.tap import org.junit.Test import org.junit.runner.RunWith @@ -52,4 +50,87 @@ class MainScreenTest : BaseTest() { verifyGivenPhrasesDisplay(defaultPhraseBasicNeeds) } } + + @Test + fun verifyNextCategoryShownAfterNavigatingToAndHidingFirstPresetCategory() { + mainScreen.apply { + // already defaults to being on the first category + + // navigate to Show Category toggle + settingsNavigationButton.tap() + editCategoriesButton.tap() + editGeneralCategoryButton.tap() + showCategorySwitch.tap() + // exit + editOptionsBackButton.tap() + backButton.tap() + closeSettingsButton.tap() + + // verify basic needs is shown: Category button and Phrases + verifyGivenCategoryDisplay("Basic Needs") + verifyGivenPhrasesDisplay(defaultPhraseBasicNeeds) + } + } + + @Test + fun verifyFirstCategoryIsShownAfterNavigatingToAndHidingLastPresetCategory() { + mainScreen.apply { + // navigate to last category + categoryBackButton.tap() + + // navigate to Show Category toggle + settingsNavigationButton.tap() + editCategoriesButton.tap() + categoryForwardButton.tap() + editRecentCategoryButton.tap() + showCategorySwitch.tap() + // exit + editOptionsBackButton.tap() + backButton.tap() + closeSettingsButton.tap() + + // verify General is shown + verifyGivenCategoryDisplay("General") + verifyGivenPhrasesDisplay(defaultPhraseGeneral) + } + } + + @Test + fun verifyNothingChangesWhenNextCategoryIsHidden() { + mainScreen.apply { + // navigate to Show Category toggle + settingsNavigationButton.tap() + editCategoriesButton.tap() + editBasicNeedsCategoryButton.tap() + showCategorySwitch.tap() + // exit + editOptionsBackButton.tap() + backButton.tap() + closeSettingsButton.tap() + + // verify General is shown + verifyGivenCategoryDisplay("General") + verifyGivenPhrasesDisplay(defaultPhraseGeneral) + } + } + + @Test + fun verifyNothingChangesWhenPreviousCategoryIsHidden() { + mainScreen.apply { + // navigate to Show Category toggle + settingsNavigationButton.tap() + editCategoriesButton.tap() + categoryForwardButton.tap() + editRecentCategoryButton.tap() + showCategorySwitch.tap() + // exit + editOptionsBackButton.tap() + backButton.tap() + closeSettingsButton.tap() + + // verify General is shown + verifyGivenCategoryDisplay("General") + verifyGivenPhrasesDisplay(defaultPhraseGeneral) + } + } } \ No newline at end of file From a039c1b8504d06b30cf8d43e3caa885fe36e0911 Mon Sep 17 00:00:00 2001 From: Cindy Cui Date: Mon, 3 Jun 2024 16:47:41 -0700 Subject: [PATCH 3/4] adds ViewModel unit tests --- .../vocable/presets/PresetsViewModelTest.kt | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/app/src/test/java/com/willowtree/vocable/presets/PresetsViewModelTest.kt b/app/src/test/java/com/willowtree/vocable/presets/PresetsViewModelTest.kt index 46cf498d..c44a7832 100644 --- a/app/src/test/java/com/willowtree/vocable/presets/PresetsViewModelTest.kt +++ b/app/src/test/java/com/willowtree/vocable/presets/PresetsViewModelTest.kt @@ -114,6 +114,148 @@ class PresetsViewModelTest { ) } + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `selected category is hidden and next immediate category is shown`() = runTest(UnconfinedTestDispatcher()) { + fakeCategoriesUseCase._categories.update { + listOf( + Category.StoredCategory( + categoryId = "1", + localizedName = LocalesWithText(mapOf("en_US" to "category")), + hidden = true, + sortOrder = 0 + ), + Category.StoredCategory( + categoryId = "2", + localizedName = LocalesWithText(mapOf("en_US" to "second category")), + hidden = false, + sortOrder = 1 + ) + ) + } + + val vm = createViewModel() + + vm.onCategorySelected("1") + + var category: Category? = null + val job = launch { + vm.selectedCategory.collect { + category = it + } + } + job.cancel() + + assertEquals( + Category.StoredCategory( + categoryId = "2", + localizedName = LocalesWithText(mapOf("en_US" to "second category")), + hidden = false, + sortOrder = 1 + ), + category + ) + + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `selected category (last in list) is hidden and first category is shown`() = runTest(UnconfinedTestDispatcher()) { + fakeCategoriesUseCase._categories.update { + listOf( + Category.StoredCategory( + categoryId = "1", + localizedName = LocalesWithText(mapOf("en_US" to "category")), + hidden = false, + sortOrder = 0 + ), + Category.StoredCategory( + categoryId = "2", + localizedName = LocalesWithText(mapOf("en_US" to "second category")), + hidden = false, + sortOrder = 1 + ), + Category.StoredCategory( + categoryId = "3", + localizedName = LocalesWithText(mapOf("en_US" to "third category")), + hidden = true, + sortOrder = 2 + ) + ) + } + + val vm = createViewModel() + + vm.onCategorySelected("3") + + var category: Category? = null + val job = launch { + vm.selectedCategory.collect { + category = it + } + } + job.cancel() + + assertEquals( + Category.StoredCategory( + categoryId = "1", + localizedName = LocalesWithText(mapOf("en_US" to "category")), + hidden = false, + sortOrder = 0 + ), + category + ) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `selected category is hidden and next non-hidden category is shown`() = runTest(UnconfinedTestDispatcher()) { + fakeCategoriesUseCase._categories.update { + listOf( + Category.StoredCategory( + categoryId = "1", + localizedName = LocalesWithText(mapOf("en_US" to "category")), + hidden = true, + sortOrder = 0 + ), + Category.StoredCategory( + categoryId = "2", + localizedName = LocalesWithText(mapOf("en_US" to "second category")), + hidden = false, + sortOrder = 1 + ), + Category.StoredCategory( + categoryId = "3", + localizedName = LocalesWithText(mapOf("en_US" to "third category")), + hidden = true, + sortOrder = 2 + ) + ) + } + + val vm = createViewModel() + + vm.onCategorySelected("3") + + var category: Category? = null + val job = launch { + vm.selectedCategory.collect { + category = it + } + } + job.cancel() + + assertEquals( + Category.StoredCategory( + categoryId = "2", + localizedName = LocalesWithText(mapOf("en_US" to "second category")), + hidden = false, + sortOrder = 1 + ), + category + ) + } + @Test fun `current phrases updated when category ID changed`() { fakePhrasesUseCase._allCategories.update { From c7350b52295fb4e2458fa0964604cf8e17306f57 Mon Sep 17 00:00:00 2001 From: Cindy Cui Date: Mon, 3 Jun 2024 16:49:15 -0700 Subject: [PATCH 4/4] removes UI tests --- .../willowtree/vocable/screens/MainScreen.kt | 34 -------- .../vocable/tests/MainScreenTest.kt | 84 ------------------- 2 files changed, 118 deletions(-) diff --git a/app/src/androidTest/java/com/willowtree/vocable/screens/MainScreen.kt b/app/src/androidTest/java/com/willowtree/vocable/screens/MainScreen.kt index 97c2113f..fce2e51f 100644 --- a/app/src/androidTest/java/com/willowtree/vocable/screens/MainScreen.kt +++ b/app/src/androidTest/java/com/willowtree/vocable/screens/MainScreen.kt @@ -4,7 +4,6 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.* import com.willowtree.vocable.R -import com.willowtree.vocable.customviews.NoSayTextButton import com.willowtree.vocable.customviews.VocableButton import com.willowtree.vocable.utility.tap import org.hamcrest.CoreMatchers.allOf @@ -48,11 +47,6 @@ class MainScreen { onView(withText(phrase)).check(matches(isDisplayed())) } } - - fun verifyGivenCategoryDisplay(category: String) { - onView(withText(category)).check(matches(isDisplayed())) - } - // Taps on the selected phrase fun tapPhrase(phraseText: String) { onView(withText(phraseText)).tap() @@ -68,34 +62,6 @@ class MainScreen { val keyboardNavitgationButton = onView(withId(R.id.keyboard_button)) val settingsNavigationButton = onView(withId(R.id.settings_button)) - // Settings buttons - val editCategoriesButton = onView(withId(R.id.edit_categories_button)) - val showCategorySwitch = onView(withId(R.id.show_category_switch)) - val editOptionsBackButton = onView(withId(R.id.edit_options_back_button)) - val backButton = onView(withId(R.id.back_button)) - val closeSettingsButton = onView(withId(R.id.settings_close_button)) - val editGeneralCategoryButton = onView( - allOf( - instanceOf(NoSayTextButton::class.java), - withId(R.id.individual_edit_category_button), - withText("General") - ) - ) - val editRecentCategoryButton = onView( - allOf( - instanceOf(NoSayTextButton::class.java), - withId(R.id.individual_edit_category_button), - withText("Recents") - ) - ) - val editBasicNeedsCategoryButton = onView( - allOf( - instanceOf(NoSayTextButton::class.java), - withId(R.id.individual_edit_category_button), - withText("Basic Needs") - ) - ) - // Categories and preset phrases val categoryBackButton = onView(withId(R.id.category_back_button)) val categoryForwardButton = onView(withId(R.id.category_forward_button)) diff --git a/app/src/androidTest/java/com/willowtree/vocable/tests/MainScreenTest.kt b/app/src/androidTest/java/com/willowtree/vocable/tests/MainScreenTest.kt index a29c9ad6..29eac0e5 100644 --- a/app/src/androidTest/java/com/willowtree/vocable/tests/MainScreenTest.kt +++ b/app/src/androidTest/java/com/willowtree/vocable/tests/MainScreenTest.kt @@ -3,7 +3,6 @@ package com.willowtree.vocable.tests import androidx.test.ext.junit.runners.AndroidJUnit4 import com.willowtree.vocable.screens.MainScreen import com.willowtree.vocable.utility.assertTextMatches -import com.willowtree.vocable.utility.tap import org.junit.Test import org.junit.runner.RunWith @@ -50,87 +49,4 @@ class MainScreenTest : BaseTest() { verifyGivenPhrasesDisplay(defaultPhraseBasicNeeds) } } - - @Test - fun verifyNextCategoryShownAfterNavigatingToAndHidingFirstPresetCategory() { - mainScreen.apply { - // already defaults to being on the first category - - // navigate to Show Category toggle - settingsNavigationButton.tap() - editCategoriesButton.tap() - editGeneralCategoryButton.tap() - showCategorySwitch.tap() - // exit - editOptionsBackButton.tap() - backButton.tap() - closeSettingsButton.tap() - - // verify basic needs is shown: Category button and Phrases - verifyGivenCategoryDisplay("Basic Needs") - verifyGivenPhrasesDisplay(defaultPhraseBasicNeeds) - } - } - - @Test - fun verifyFirstCategoryIsShownAfterNavigatingToAndHidingLastPresetCategory() { - mainScreen.apply { - // navigate to last category - categoryBackButton.tap() - - // navigate to Show Category toggle - settingsNavigationButton.tap() - editCategoriesButton.tap() - categoryForwardButton.tap() - editRecentCategoryButton.tap() - showCategorySwitch.tap() - // exit - editOptionsBackButton.tap() - backButton.tap() - closeSettingsButton.tap() - - // verify General is shown - verifyGivenCategoryDisplay("General") - verifyGivenPhrasesDisplay(defaultPhraseGeneral) - } - } - - @Test - fun verifyNothingChangesWhenNextCategoryIsHidden() { - mainScreen.apply { - // navigate to Show Category toggle - settingsNavigationButton.tap() - editCategoriesButton.tap() - editBasicNeedsCategoryButton.tap() - showCategorySwitch.tap() - // exit - editOptionsBackButton.tap() - backButton.tap() - closeSettingsButton.tap() - - // verify General is shown - verifyGivenCategoryDisplay("General") - verifyGivenPhrasesDisplay(defaultPhraseGeneral) - } - } - - @Test - fun verifyNothingChangesWhenPreviousCategoryIsHidden() { - mainScreen.apply { - // navigate to Show Category toggle - settingsNavigationButton.tap() - editCategoriesButton.tap() - categoryForwardButton.tap() - editRecentCategoryButton.tap() - showCategorySwitch.tap() - // exit - editOptionsBackButton.tap() - backButton.tap() - closeSettingsButton.tap() - - // verify General is shown - verifyGivenCategoryDisplay("General") - verifyGivenPhrasesDisplay(defaultPhraseGeneral) - } - } } \ No newline at end of file