diff --git a/.run/Generate Demo Baseline Profile.run.xml b/.run/Generate Demo Baseline Profile.run.xml deleted file mode 100644 index a4b0de892..000000000 --- a/.run/Generate Demo Baseline Profile.run.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - true - true - false - - - diff --git a/.run/spotlessApply.run.xml b/.run/spotlessApply.run.xml deleted file mode 100644 index 5c4dac833..000000000 --- a/.run/spotlessApply.run.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - true - true - false - - - \ No newline at end of file diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt index 05811f4be..4b5e7a5f8 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt @@ -17,13 +17,11 @@ package com.google.samples.apps.nowinandroid.core.data import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository -import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.mapToUserNewsResources import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository -import com.google.samples.apps.nowinandroid.core.testing.repository.emptyUserData import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant @@ -42,23 +40,16 @@ class CompositeUserNewsResourceRepositoryTest { @Test fun whenNoFilters_allNewsResourcesAreReturned() = runTest { - // Obtain the user news resources flow. + // Obtain the user news resources flow without any filters applied. val userNewsResources = userNewsResourceRepository.observeAll() - // Send some news resources and user data into the data repositories. + // Send test data into the repositories. newsRepository.sendNewsResources(sampleNewsResources) + userDataRepository.setFollowedTopicIds(emptySet()) - // Construct the test user data with bookmarks and followed topics. - val userData = emptyUserData.copy( - bookmarkedNewsResources = setOf(sampleNewsResources[0].id, sampleNewsResources[2].id), - followedTopics = setOf(sampleTopic1.id), - ) - - userDataRepository.setUserData(userData) - - // Check that the correct news resources are returned with their bookmarked state. + // Check that the correct list of news resources is returned. assertEquals( - sampleNewsResources.mapToUserNewsResources(userData), + sampleNewsResources.mapToUserNewsResources(userDataRepository.userData.first()), userNewsResources.first(), ) } @@ -66,46 +57,35 @@ class CompositeUserNewsResourceRepositoryTest { @Test fun whenFilteredByTopicId_matchingNewsResourcesAreReturned() = runTest { // Obtain a stream of user news resources for the given topic id. - val userNewsResources = - userNewsResourceRepository.observeAll( - NewsResourceQuery( - filterTopicIds = setOf( - sampleTopic1.id, - ), - ), - ) + val userNewsResources = userNewsResourceRepository.observeAllForFollowedTopics() // Send test data into the repositories. newsRepository.sendNewsResources(sampleNewsResources) - userDataRepository.setUserData(emptyUserData) + userDataRepository.setTopicIdFollowed(sampleTopic1.id, true) // Check that only news resources with the given topic id are returned. assertEquals( sampleNewsResources .filter { sampleTopic1 in it.topics } - .mapToUserNewsResources(emptyUserData), + .mapToUserNewsResources(userDataRepository.userData.first()), userNewsResources.first(), ) } @Test fun whenFilteredByFollowedTopics_matchingNewsResourcesAreReturned() = runTest { - // Obtain a stream of user news resources for the given topic id. - val userNewsResources = - userNewsResourceRepository.observeAllForFollowedTopics() + // Obtain a stream of user news resources filtered by followed topics + val userNewsResources = userNewsResourceRepository.observeAllForFollowedTopics() // Send test data into the repositories. - val userData = emptyUserData.copy( - followedTopics = setOf(sampleTopic1.id), - ) newsRepository.sendNewsResources(sampleNewsResources) - userDataRepository.setUserData(userData) + userDataRepository.setFollowedTopicIds(setOf(sampleTopic1.id)) - // Check that only news resources with the given topic id are returned. + // Check that only news resources with the followed topics are returned. assertEquals( sampleNewsResources .filter { sampleTopic1 in it.topics } - .mapToUserNewsResources(userData), + .mapToUserNewsResources(userDataRepository.userData.first()), userNewsResources.first(), ) } @@ -119,16 +99,14 @@ class CompositeUserNewsResourceRepositoryTest { newsRepository.sendNewsResources(sampleNewsResources) // Construct the test user data with bookmarks and followed topics. - val userData = emptyUserData.copy( - bookmarkedNewsResources = setOf(sampleNewsResources[0].id, sampleNewsResources[2].id), - followedTopics = setOf(sampleTopic1.id), - ) - - userDataRepository.setUserData(userData) + userDataRepository.setNewsResourceBookmarked(sampleNewsResources[0].id, true) + userDataRepository.setNewsResourceBookmarked(sampleNewsResources[2].id, true) + userDataRepository.setFollowedTopicIds(setOf(sampleTopic1.id)) // Check that the correct news resources are returned with their bookmarked state. assertEquals( - listOf(sampleNewsResources[0], sampleNewsResources[2]).mapToUserNewsResources(userData), + listOf(sampleNewsResources[0], sampleNewsResources[2]) + .mapToUserNewsResources(userDataRepository.userData.first()), userNewsResources.first(), ) } diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt index be76112dc..0b2013db3 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt @@ -75,16 +75,13 @@ class TestUserDataRepository : UserDataRepository { override suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) { currentUserData.let { current -> - _userData.tryEmit( - current.copy( - viewedNewsResources = - if (viewed) { - current.viewedNewsResources + newsResourceId - } else { - current.viewedNewsResources - newsResourceId - }, - ), - ) + val viewedNews = if (viewed) { + current.viewedNewsResources + newsResourceId + } else { + current.viewedNewsResources - newsResourceId + } + + _userData.tryEmit(current.copy(viewedNewsResources = viewedNews)) } } @@ -111,11 +108,4 @@ class TestUserDataRepository : UserDataRepository { _userData.tryEmit(current.copy(shouldHideOnboarding = shouldHideOnboarding)) } } - - /** - * A test-only API to allow setting of user data directly. - */ - fun setUserData(userData: UserData) { - _userData.tryEmit(userData) - } } diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt index 8e94a491a..a0776a41d 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt +++ b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/navigation/ForYouNavigation.kt @@ -16,6 +16,7 @@ package com.google.samples.apps.nowinandroid.feature.foryou.navigation +import androidx.annotation.VisibleForTesting import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions @@ -25,6 +26,7 @@ import androidx.navigation.navArgument import androidx.navigation.navDeepLink import com.google.samples.apps.nowinandroid.feature.foryou.ForYouRoute +@VisibleForTesting const val LINKED_NEWS_RESOURCE_ID = "linkedNewsResourceId" const val FOR_YOU_ROUTE = "for_you_route/{$LINKED_NEWS_RESOURCE_ID}" private const val DEEP_LINK_URI_PATTERN = diff --git a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt index 2fbdf0a79..e41bc0c26 100644 --- a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt +++ b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt @@ -29,7 +29,6 @@ import com.google.samples.apps.nowinandroid.core.model.data.mapToUserNewsResourc import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository -import com.google.samples.apps.nowinandroid.core.testing.repository.emptyUserData import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.core.testing.util.TestAnalyticsHelper import com.google.samples.apps.nowinandroid.core.testing.util.TestSyncManager @@ -277,8 +276,7 @@ class ForYouViewModelTest { topicsRepository.sendTopics(sampleTopics) val followedTopicIds = setOf("0", "1") - val userData = emptyUserData.copy(followedTopics = followedTopicIds) - userDataRepository.setUserData(userData) + userDataRepository.setFollowedTopicIds(followedTopicIds) viewModel.dismissOnboarding() assertEquals( @@ -295,7 +293,7 @@ class ForYouViewModelTest { ) assertEquals( NewsFeedUiState.Success( - feed = sampleNewsResources.mapToUserNewsResources(userData), + feed = sampleNewsResources.mapToUserNewsResources(userDataRepository.userData.first()), ), viewModel.feedState.value, ) @@ -341,13 +339,11 @@ class ForYouViewModelTest { viewModel.onboardingUiState.value, ) - val userData = emptyUserData.copy(followedTopics = setOf(followedTopicId)) - assertEquals( NewsFeedUiState.Success( feed = listOf( - UserNewsResource(sampleNewsResources[1], userData), - UserNewsResource(sampleNewsResources[2], userData), + UserNewsResource(newsResource = sampleNewsResources[1], userData = userDataRepository.userData.first()), + UserNewsResource(newsResource = sampleNewsResources[2], userData = userDataRepository.userData.first()), ), ), viewModel.feedState.value, @@ -427,14 +423,9 @@ class ForYouViewModelTest { launch(UnconfinedTestDispatcher()) { viewModel.onboardingUiState.collect() } val collectJob2 = launch(UnconfinedTestDispatcher()) { viewModel.feedState.collect() } - val followedTopicIds = setOf("1") - val userData = emptyUserData.copy( - followedTopics = followedTopicIds, - shouldHideOnboarding = true, - ) - topicsRepository.sendTopics(sampleTopics) - userDataRepository.setUserData(userData) + userDataRepository.setFollowedTopicIds(setOf("1")) + userDataRepository.setShouldHideOnboarding(true) newsRepository.sendNewsResources(sampleNewsResources) val bookmarkedNewsResourceId = "2" @@ -443,10 +434,6 @@ class ForYouViewModelTest { isChecked = true, ) - val userDataExpected = userData.copy( - bookmarkedNewsResources = setOf(bookmarkedNewsResourceId), - ) - assertEquals( OnboardingUiState.NotShown, viewModel.onboardingUiState.value, @@ -454,8 +441,8 @@ class ForYouViewModelTest { assertEquals( NewsFeedUiState.Success( feed = listOf( - UserNewsResource(newsResource = sampleNewsResources[1], userDataExpected), - UserNewsResource(newsResource = sampleNewsResources[2], userDataExpected), + UserNewsResource(newsResource = sampleNewsResources[1], userData = userDataRepository.userData.first()), + UserNewsResource(newsResource = sampleNewsResources[2], userData = userDataRepository.userData.first()), ), ), viewModel.feedState.value, @@ -471,13 +458,13 @@ class ForYouViewModelTest { launch(UnconfinedTestDispatcher()) { viewModel.deepLinkedNewsResource.collect() } newsRepository.sendNewsResources(sampleNewsResources) - userDataRepository.setUserData(emptyUserData) + userDataRepository.setFollowedTopicIds(emptySet()) savedStateHandle[LINKED_NEWS_RESOURCE_ID] = sampleNewsResources.first().id assertEquals( expected = UserNewsResource( newsResource = sampleNewsResources.first(), - userData = emptyUserData, + userData = userDataRepository.userData.first(), ), actual = viewModel.deepLinkedNewsResource.value, ) @@ -524,6 +511,17 @@ class ForYouViewModelTest { actual = userDataRepository.userData.first().bookmarkedNewsResources, ) } + + @Test + fun whenSetNewsResourceViewedIsCalled_viewedStateIsUpdated() = runTest { + val newsResourceId = "123" + viewModel.setNewsResourceViewed(newsResourceId, true) + + assertEquals( + expected = setOf(newsResourceId), + actual = userDataRepository.userData.first().viewedNewsResources, + ) + } } private val sampleTopics = listOf( diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt index 8a0f2d130..c23b4dbc6 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt @@ -16,6 +16,7 @@ package com.google.samples.apps.nowinandroid.feature.interests.navigation +import androidx.annotation.VisibleForTesting import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions @@ -24,6 +25,7 @@ import androidx.navigation.compose.composable import androidx.navigation.navArgument import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute +@VisibleForTesting const val TOPIC_ID_ARG = "topicId" const val INTERESTS_ROUTE_BASE = "interests_route" const val INTERESTS_ROUTE = "$INTERESTS_ROUTE_BASE?$TOPIC_ID_ARG={$TOPIC_ID_ARG}" diff --git a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt index c832401de..417fb07ee 100644 --- a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt +++ b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt @@ -25,7 +25,6 @@ import com.google.samples.apps.nowinandroid.core.testing.data.topicsTestData import com.google.samples.apps.nowinandroid.core.testing.repository.TestRecentSearchRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestSearchContentsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository -import com.google.samples.apps.nowinandroid.core.testing.repository.emptyUserData import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.feature.search.RecentSearchQueriesUiState.Success import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.EmptyQuery @@ -41,6 +40,7 @@ import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertIs +import kotlin.test.assertTrue /** * To learn more about how this test handles Flows created with stateIn, see @@ -73,7 +73,6 @@ class SearchViewModelTest { userDataRepository = userDataRepository, analyticsHelper = NoOpAnalyticsHelper(), ) - userDataRepository.setUserData(emptyUserData) } @Test @@ -103,6 +102,7 @@ class SearchViewModelTest { viewModel.onSearchQueryChanged("XXX") searchContentsRepository.addNewsResources(newsResourcesTestData) searchContentsRepository.addTopics(topicsTestData) + userDataRepository.setFollowedTopicIds(emptySet()) val result = viewModel.searchResultUiState.value assertIs(result) @@ -122,6 +122,20 @@ class SearchViewModelTest { collectJob.cancel() } + @Test + fun recentSearches_verifyQueryHistoryClear() = runTest { + val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.recentSearchQueriesUiState.collect() } + + viewModel.onSearchTriggered("kotlin") + viewModel.clearRecentSearches() + + val result = viewModel.recentSearchQueriesUiState.value + assertIs(result) + assertTrue(result.recentQueries.isEmpty()) + + collectJob.cancel() + } + @Test fun searchNotReady_withNoFtsTableEntity() = runTest { val collectJob = diff --git a/feature/settings/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt b/feature/settings/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt index 9062abee6..bcc9b8dfe 100644 --- a/feature/settings/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt +++ b/feature/settings/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt @@ -55,8 +55,9 @@ class SettingsViewModelTest { val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.settingsUiState.collect() } - userDataRepository.setThemeBrand(ANDROID) - userDataRepository.setDarkThemeConfig(DARK) + viewModel.updateThemeBrand(ANDROID) + viewModel.updateDarkThemeConfig(DARK) + viewModel.updateDynamicColorPreference(false) assertEquals( Success(