From b4ec5184759cf151d2d6a52c1b1b1b7b6be90842 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 3 Jul 2024 10:55:21 -0700 Subject: [PATCH 1/6] Fix errors in the androidInstrumentedTest --- .../src/main/kotlin/CmpFeatureConventionPlugin.kt | 6 ++++++ .../nowinandroid/interests/InterestsScreenTest.kt | 13 ++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt index 53fc3ff3e..09fa12c3e 100644 --- a/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt @@ -50,7 +50,13 @@ class CmpFeatureConventionPlugin : Plugin { add("androidMainImplementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) add("androidMainImplementation", libs.findLibrary("androidx.tracing.ktx").get()) + add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.compose.ui.test").get()) add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.lifecycle.runtimeTesting").get()) + add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.test.core").get()) + add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.test.ext").get()) + add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.test.junit").get()) + add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.test.runner").get()) + add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.test.espresso.core").get()) } } } diff --git a/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt b/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt index a441f5a9d..8da022487 100644 --- a/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt +++ b/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt @@ -27,11 +27,15 @@ import androidx.compose.ui.test.onNodeWithText import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTestData import com.google.samples.apps.nowinandroid.feature.interests.InterestsScreen import com.google.samples.apps.nowinandroid.feature.interests.InterestsUiState +import kotlinx.coroutines.runBlocking +import nowinandroid.feature.interests.generated.resources.feature_interests_empty_header +import nowinandroid.feature.interests.generated.resources.feature_interests_loading +import org.jetbrains.compose.resources.getString import org.junit.Before import org.junit.Rule import org.junit.Test -import com.google.samples.apps.nowinandroid.core.ui.R as CoreUiR -import com.google.samples.apps.nowinandroid.feature.interests.R as InterestsR +import nowinandroid.core.ui.generated.resources.Res as CoreUiR +import nowinandroid.feature.interests.generated.resources.Res as InterestsR /** * UI test for checking the correct behaviour of the Interests screen; @@ -50,13 +54,16 @@ class InterestsScreenTest { @Before fun setup() { - composeTestRule.activity.apply { + // Temp solution to call getString + runBlocking { interestsLoading = getString(InterestsR.string.feature_interests_loading) interestsEmptyHeader = getString(InterestsR.string.feature_interests_empty_header) + // TODO res object is internal, it couldn't import from the other module interestsTopicCardFollowButton = getString(CoreUiR.string.core_ui_interests_card_follow_button_content_desc) interestsTopicCardUnfollowButton = getString(CoreUiR.string.core_ui_interests_card_unfollow_button_content_desc) + } } From f0c58bc83bc3a81493601ccd1124b7bde2ecf8ea Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 3 Jul 2024 11:23:17 -0700 Subject: [PATCH 2/6] Move compose declaration to build script --- .../convention/src/main/kotlin/CmpFeatureConventionPlugin.kt | 3 +++ feature/bookmarks/build.gradle.kts | 3 +-- feature/foryou/build.gradle.kts | 2 -- feature/interests/build.gradle.kts | 2 -- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt index 09fa12c3e..adf8167a5 100644 --- a/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt @@ -21,6 +21,7 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies +import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension // Convention plugin for the Compose Multiplatform feature module class CmpFeatureConventionPlugin : Plugin { @@ -29,6 +30,8 @@ class CmpFeatureConventionPlugin : Plugin { pluginManager.apply { apply("nowinandroid.kmp.library") apply("nowinandroid.kmp.inject") + apply("org.jetbrains.kotlin.plugin.compose") + apply("org.jetbrains.compose") } extensions.configure { defaultConfig { diff --git a/feature/bookmarks/build.gradle.kts b/feature/bookmarks/build.gradle.kts index bb53f731e..e2ca6d1c8 100644 --- a/feature/bookmarks/build.gradle.kts +++ b/feature/bookmarks/build.gradle.kts @@ -16,9 +16,8 @@ plugins { alias(libs.plugins.nowinandroid.cmp.feature) - alias(libs.plugins.jetbrains.compose) - alias(libs.plugins.compose) alias(libs.plugins.nowinandroid.android.library.jacoco) + alias(libs.plugins.roborazzi) } android { diff --git a/feature/foryou/build.gradle.kts b/feature/foryou/build.gradle.kts index 1ac58a500..60fb653ef 100644 --- a/feature/foryou/build.gradle.kts +++ b/feature/foryou/build.gradle.kts @@ -16,8 +16,6 @@ plugins { alias(libs.plugins.nowinandroid.cmp.feature) - alias(libs.plugins.jetbrains.compose) - alias(libs.plugins.compose) alias(libs.plugins.nowinandroid.android.library.jacoco) alias(libs.plugins.roborazzi) } diff --git a/feature/interests/build.gradle.kts b/feature/interests/build.gradle.kts index a97a304df..798b5d15f 100644 --- a/feature/interests/build.gradle.kts +++ b/feature/interests/build.gradle.kts @@ -16,8 +16,6 @@ plugins { alias(libs.plugins.nowinandroid.cmp.feature) - alias(libs.plugins.jetbrains.compose) - alias(libs.plugins.compose) alias(libs.plugins.nowinandroid.android.library.jacoco) alias(libs.plugins.roborazzi) } From 392050cee0d139bb02e89017efb8c13509eab8d0 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 3 Jul 2024 12:12:51 -0700 Subject: [PATCH 3/6] Fix compilation errors in androidInstrumentedTest --- core/ui/build.gradle.kts | 5 ++++ .../feature/bookmarks/BookmarksScreenTest.kt | 22 +++++++++----- .../feature/foryou/ForYouScreenTest.kt | 29 ++++++++++++------- .../interests/InterestsScreenTest.kt | 3 +- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 3fa97c17f..86bfa6297 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -48,3 +48,8 @@ kotlin { } } } + +compose.resources { + publicResClass = true + generateResClass = always +} diff --git a/feature/bookmarks/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt b/feature/bookmarks/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt index 40f54e4a7..54e75d5a0 100644 --- a/feature/bookmarks/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt +++ b/feature/bookmarks/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt @@ -37,6 +37,12 @@ import androidx.lifecycle.testing.TestLifecycleOwner import com.google.samples.apps.nowinandroid.core.testing.data.userNewsResourcesTestData import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import kotlinx.coroutines.test.runTest +import nowinandroid.core.ui.generated.resources.core_ui_unbookmark +import nowinandroid.feature.bookmarks.generated.resources.Res +import nowinandroid.feature.bookmarks.generated.resources.feature_bookmarks_empty_description +import nowinandroid.feature.bookmarks.generated.resources.feature_bookmarks_empty_error +import nowinandroid.feature.bookmarks.generated.resources.feature_bookmarks_loading +import org.jetbrains.compose.resources.getString import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals @@ -51,7 +57,7 @@ class BookmarksScreenTest { val composeTestRule = createAndroidComposeRule() @Test - fun loading_showsLoadingSpinner() { + fun loading_showsLoadingSpinner() = runTest { composeTestRule.setContent { BookmarksScreen( feedState = NewsFeedUiState.Loading, @@ -64,7 +70,7 @@ class BookmarksScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.feature_bookmarks_loading), + getString(Res.string.feature_bookmarks_loading), ) .assertExists() } @@ -109,7 +115,7 @@ class BookmarksScreenTest { } @Test - fun feed_whenRemovingBookmark_removesBookmark() { + fun feed_whenRemovingBookmark_removesBookmark() = runTest { var removeFromBookmarksCalled = false composeTestRule.setContent { @@ -129,8 +135,8 @@ class BookmarksScreenTest { composeTestRule .onAllNodesWithContentDescription( - composeTestRule.activity.getString( - com.google.samples.apps.nowinandroid.core.ui.R.string.core_ui_unbookmark, + getString( + nowinandroid.core.ui.generated.resources.Res.string.core_ui_unbookmark, ), ).filter( hasAnyAncestor( @@ -148,7 +154,7 @@ class BookmarksScreenTest { } @Test - fun feed_whenHasNoBookmarks_showsEmptyState() { + fun feed_whenHasNoBookmarks_showsEmptyState() = runTest { composeTestRule.setContent { BookmarksScreen( feedState = NewsFeedUiState.Success(emptyList()), @@ -161,13 +167,13 @@ class BookmarksScreenTest { composeTestRule .onNodeWithText( - composeTestRule.activity.getString(R.string.feature_bookmarks_empty_error), + getString(Res.string.feature_bookmarks_empty_error), ) .assertExists() composeTestRule .onNodeWithText( - composeTestRule.activity.getString(R.string.feature_bookmarks_empty_description), + getString(Res.string.feature_bookmarks_empty_description), ) .assertExists() } diff --git a/feature/foryou/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt b/feature/foryou/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt index 5477493ef..84ebaaae3 100644 --- a/feature/foryou/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt +++ b/feature/foryou/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt @@ -32,6 +32,12 @@ import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPer import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTestData import com.google.samples.apps.nowinandroid.core.testing.data.userNewsResourcesTestData import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import nowinandroid.feature.foryou.generated.resources.Res +import nowinandroid.feature.foryou.generated.resources.feature_foryou_done +import nowinandroid.feature.foryou.generated.resources.feature_foryou_loading +import org.jetbrains.compose.resources.getString import org.junit.Rule import org.junit.Test @@ -44,13 +50,14 @@ class ForYouScreenTest { val composeTestRule = createAndroidComposeRule() private val doneButtonMatcher by lazy { - hasText( - composeTestRule.activity.resources.getString(R.string.feature_foryou_done), - ) + runBlocking { + hasText(getString(Res.string.feature_foryou_done)) + } + } @Test - fun circularProgressIndicator_whenScreenIsLoading_exists() { + fun circularProgressIndicator_whenScreenIsLoading_exists() = runTest { composeTestRule.setContent { BoxWithConstraints { ForYouScreen( @@ -70,13 +77,13 @@ class ForYouScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.feature_foryou_loading), + getString(Res.string.feature_foryou_loading), ) .assertExists() } @Test - fun circularProgressIndicator_whenScreenIsSyncing_exists() { + fun circularProgressIndicator_whenScreenIsSyncing_exists() = runTest { composeTestRule.setContent { BoxWithConstraints { ForYouScreen( @@ -96,7 +103,7 @@ class ForYouScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.feature_foryou_loading), + getString(Res.string.feature_foryou_loading), ) .assertExists() } @@ -194,7 +201,7 @@ class ForYouScreenTest { } @Test - fun feed_whenInterestsSelectedAndLoading_showsLoadingIndicator() { + fun feed_whenInterestsSelectedAndLoading_showsLoadingIndicator() = runTest { composeTestRule.setContent { BoxWithConstraints { ForYouScreen( @@ -215,13 +222,13 @@ class ForYouScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.feature_foryou_loading), + getString(Res.string.feature_foryou_loading), ) .assertExists() } @Test - fun feed_whenNoInterestsSelectionAndLoading_showsLoadingIndicator() { + fun feed_whenNoInterestsSelectionAndLoading_showsLoadingIndicator() = runTest{ composeTestRule.setContent { BoxWithConstraints { ForYouScreen( @@ -241,7 +248,7 @@ class ForYouScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.feature_foryou_loading), + getString(Res.string.feature_foryou_loading), ) .assertExists() } diff --git a/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt b/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt index 8da022487..c220f65c6 100644 --- a/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt +++ b/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt @@ -28,6 +28,8 @@ import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTes import com.google.samples.apps.nowinandroid.feature.interests.InterestsScreen import com.google.samples.apps.nowinandroid.feature.interests.InterestsUiState import kotlinx.coroutines.runBlocking +import nowinandroid.core.ui.generated.resources.core_ui_interests_card_follow_button_content_desc +import nowinandroid.core.ui.generated.resources.core_ui_interests_card_unfollow_button_content_desc import nowinandroid.feature.interests.generated.resources.feature_interests_empty_header import nowinandroid.feature.interests.generated.resources.feature_interests_loading import org.jetbrains.compose.resources.getString @@ -58,7 +60,6 @@ class InterestsScreenTest { runBlocking { interestsLoading = getString(InterestsR.string.feature_interests_loading) interestsEmptyHeader = getString(InterestsR.string.feature_interests_empty_header) - // TODO res object is internal, it couldn't import from the other module interestsTopicCardFollowButton = getString(CoreUiR.string.core_ui_interests_card_follow_button_content_desc) interestsTopicCardUnfollowButton = From a686abc9d5e573687ffd12dd0c1d79309771a008 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 3 Jul 2024 13:59:26 -0700 Subject: [PATCH 4/6] Migrate search to the multiplatform module --- feature/search/build.gradle.kts | 39 ++++++++++++++----- .../feature/search/SearchScreenTest.kt | 0 .../feature/search/SearchViewModelTest.kt | 0 .../composeResources}/values/strings.xml | 0 .../search/RecentSearchQueriesUiState.kt | 0 .../feature/search/SearchResultUiState.kt | 0 .../feature/search/SearchScreen.kt | 24 ++++++++---- .../SearchUiStatePreviewParameterProvider.kt | 0 .../feature/search/SearchViewModel.kt | 0 .../search/navigation/SearchNavigation.kt | 0 feature/search/src/main/AndroidManifest.xml | 17 -------- 11 files changed, 46 insertions(+), 34 deletions(-) rename feature/search/src/{androidTest => androidInstrumentedTest}/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt (100%) rename feature/search/src/{test => androidUnitTest}/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt (100%) rename feature/search/src/{main/res => commonMain/composeResources}/values/strings.xml (100%) rename feature/search/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/feature/search/RecentSearchQueriesUiState.kt (100%) rename feature/search/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchResultUiState.kt (100%) rename feature/search/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt (94%) rename feature/search/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt (100%) rename feature/search/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt (100%) rename feature/search/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt (100%) delete mode 100644 feature/search/src/main/AndroidManifest.xml diff --git a/feature/search/build.gradle.kts b/feature/search/build.gradle.kts index 98052e9ab..f0d95fb21 100644 --- a/feature/search/build.gradle.kts +++ b/feature/search/build.gradle.kts @@ -15,22 +15,41 @@ */ plugins { - alias(libs.plugins.nowinandroid.android.feature) - alias(libs.plugins.nowinandroid.android.library.compose) + alias(libs.plugins.nowinandroid.cmp.feature) alias(libs.plugins.nowinandroid.android.library.jacoco) + alias(libs.plugins.roborazzi) } android { namespace = "com.google.samples.apps.nowinandroid.feature.search" } -dependencies { - implementation(projects.core.data) - implementation(projects.core.domain) - implementation(projects.core.ui) - - testImplementation(projects.core.testing) - - androidTestImplementation(projects.core.testing) +kotlin { + sourceSets { + commonMain.dependencies { + implementation(projects.core.data) + implementation(projects.core.domain) + implementation(projects.core.ui) + implementation(compose.material3) + implementation(compose.foundation) + implementation(compose.ui) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + } + commonMain.dependencies { + implementation(projects.core.testing) + } + androidUnitTest.dependencies { + implementation(libs.robolectric) + implementation(libs.roborazzi) + implementation(projects.core.screenshotTesting) + } + androidInstrumentedTest.dependencies { + implementation(projects.core.testing) + } + } } +compose.resources { + publicResClass = true +} diff --git a/feature/search/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt b/feature/search/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt similarity index 100% rename from feature/search/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt rename to feature/search/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt diff --git a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt b/feature/search/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt similarity index 100% rename from feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt rename to feature/search/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt diff --git a/feature/search/src/main/res/values/strings.xml b/feature/search/src/commonMain/composeResources/values/strings.xml similarity index 100% rename from feature/search/src/main/res/values/strings.xml rename to feature/search/src/commonMain/composeResources/values/strings.xml diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/RecentSearchQueriesUiState.kt b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/RecentSearchQueriesUiState.kt similarity index 100% rename from feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/RecentSearchQueriesUiState.kt rename to feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/RecentSearchQueriesUiState.kt diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchResultUiState.kt b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchResultUiState.kt similarity index 100% rename from feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchResultUiState.kt rename to feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchResultUiState.kt diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt similarity index 94% rename from feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt rename to feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt index 86b1eb717..2ccb7974f 100644 --- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt +++ b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt @@ -64,7 +64,6 @@ import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.onKeyEvent import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextStyle @@ -74,11 +73,7 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.withStyle -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.DraggableScrollbar import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.rememberDraggableScroller import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState @@ -91,8 +86,23 @@ import com.google.samples.apps.nowinandroid.core.ui.InterestsItem import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success import com.google.samples.apps.nowinandroid.core.ui.R.string import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent +import com.google.samples.apps.nowinandroid.core.ui.collectAsStateWithLifecycle import com.google.samples.apps.nowinandroid.core.ui.newsFeed -import com.google.samples.apps.nowinandroid.feature.search.R as searchR +import nowinandroid.feature.search.generated.resources.feature_search_clear_recent_searches_content_desc +import nowinandroid.feature.search.generated.resources.feature_search_clear_search_text_content_desc +import nowinandroid.feature.search.generated.resources.feature_search_interests +import nowinandroid.feature.search.generated.resources.feature_search_not_ready +import nowinandroid.feature.search.generated.resources.feature_search_recent_searches +import nowinandroid.feature.search.generated.resources.feature_search_result_not_found +import nowinandroid.feature.search.generated.resources.feature_search_title +import nowinandroid.feature.search.generated.resources.feature_search_to_browse_topics +import nowinandroid.feature.search.generated.resources.feature_search_topics +import nowinandroid.feature.search.generated.resources.feature_search_try_another_search +import nowinandroid.feature.search.generated.resources.feature_search_updates +import org.jetbrains.compose.resources.stringResource +import org.jetbrains.compose.ui.tooling.preview.Preview +import org.jetbrains.compose.ui.tooling.preview.PreviewParameter +import nowinandroid.feature.search.generated.resources.Res as searchR @Composable internal fun SearchRoute( @@ -100,7 +110,7 @@ internal fun SearchRoute( onInterestsClick: () -> Unit, onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, - searchViewModel: SearchViewModel = hiltViewModel(), + searchViewModel: SearchViewModel, ) { val recentSearchQueriesUiState by searchViewModel.recentSearchQueriesUiState.collectAsStateWithLifecycle() val searchResultUiState by searchViewModel.searchResultUiState.collectAsStateWithLifecycle() diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt similarity index 100% rename from feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt rename to feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt similarity index 100% rename from feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt rename to feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt similarity index 100% rename from feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt rename to feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt diff --git a/feature/search/src/main/AndroidManifest.xml b/feature/search/src/main/AndroidManifest.xml deleted file mode 100644 index 312b22fd3..000000000 --- a/feature/search/src/main/AndroidManifest.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - From ea32f9f1c4d69d4131858c507596aa5bccba0cc4 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 3 Jul 2024 14:51:19 -0700 Subject: [PATCH 5/6] Fix compilation errors --- .../feature/foryou/ForYouScreenTest.kt | 3 +- .../interests/InterestsScreenTest.kt | 1 - .../feature/search/SearchScreenTest.kt | 41 ++++++++++++------- .../feature/search/SearchScreen.kt | 27 ++++++------ .../SearchUiStatePreviewParameterProvider.kt | 2 +- .../feature/search/SearchViewModel.kt | 6 +-- .../search/navigation/SearchNavigation.kt | 11 +++-- 7 files changed, 50 insertions(+), 41 deletions(-) diff --git a/feature/foryou/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt b/feature/foryou/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt index 84ebaaae3..350e1cb7d 100644 --- a/feature/foryou/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt +++ b/feature/foryou/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt @@ -53,7 +53,6 @@ class ForYouScreenTest { runBlocking { hasText(getString(Res.string.feature_foryou_done)) } - } @Test @@ -228,7 +227,7 @@ class ForYouScreenTest { } @Test - fun feed_whenNoInterestsSelectionAndLoading_showsLoadingIndicator() = runTest{ + fun feed_whenNoInterestsSelectionAndLoading_showsLoadingIndicator() = runTest { composeTestRule.setContent { BoxWithConstraints { ForYouScreen( diff --git a/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt b/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt index c220f65c6..8f665fd27 100644 --- a/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt +++ b/feature/interests/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt @@ -64,7 +64,6 @@ class InterestsScreenTest { getString(CoreUiR.string.core_ui_interests_card_follow_button_content_desc) interestsTopicCardUnfollowButton = getString(CoreUiR.string.core_ui_interests_card_unfollow_button_content_desc) - } } diff --git a/feature/search/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt b/feature/search/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt index a9e2fa98f..d1c9e11ac 100644 --- a/feature/search/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt +++ b/feature/search/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt @@ -35,10 +35,23 @@ import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTestData import com.google.samples.apps.nowinandroid.core.testing.data.newsResourcesTestData -import com.google.samples.apps.nowinandroid.core.ui.R.string +import kotlinx.coroutines.runBlocking +import nowinandroid.core.ui.generated.resources.core_ui_interests_card_follow_button_content_desc +import nowinandroid.core.ui.generated.resources.core_ui_interests_card_unfollow_button_content_desc +import nowinandroid.feature.search.generated.resources.Res +import nowinandroid.feature.search.generated.resources.feature_search_clear_recent_searches_content_desc +import nowinandroid.feature.search.generated.resources.feature_search_clear_search_text_content_desc +import nowinandroid.feature.search.generated.resources.feature_search_interests +import nowinandroid.feature.search.generated.resources.feature_search_not_ready +import nowinandroid.feature.search.generated.resources.feature_search_to_browse_topics +import nowinandroid.feature.search.generated.resources.feature_search_topics +import nowinandroid.feature.search.generated.resources.feature_search_try_another_search +import nowinandroid.feature.search.generated.resources.feature_search_updates +import org.jetbrains.compose.resources.getString import org.junit.Before import org.junit.Rule import org.junit.Test +import nowinandroid.core.ui.generated.resources.Res as uiR /** * UI test for checking the correct behaviour of the Search screen. @@ -68,20 +81,18 @@ class SearchScreenTest { ) @Before - fun setup() { - composeTestRule.activity.apply { - clearSearchContentDesc = getString(R.string.feature_search_clear_search_text_content_desc) - clearRecentSearchesContentDesc = getString(R.string.feature_search_clear_recent_searches_content_desc) - followButtonContentDesc = - getString(string.core_ui_interests_card_follow_button_content_desc) - unfollowButtonContentDesc = - getString(string.core_ui_interests_card_unfollow_button_content_desc) - topicsString = getString(R.string.feature_search_topics) - updatesString = getString(R.string.feature_search_updates) - tryAnotherSearchString = getString(R.string.feature_search_try_another_search) + - " " + getString(R.string.feature_search_interests) + " " + getString(R.string.feature_search_to_browse_topics) - searchNotReadyString = getString(R.string.feature_search_not_ready) - } + fun setup() = runBlocking { + clearSearchContentDesc = getString(Res.string.feature_search_clear_search_text_content_desc) + clearRecentSearchesContentDesc = getString(Res.string.feature_search_clear_recent_searches_content_desc) + followButtonContentDesc = + getString(uiR.string.core_ui_interests_card_follow_button_content_desc) + unfollowButtonContentDesc = + getString(uiR.string.core_ui_interests_card_unfollow_button_content_desc) + topicsString = getString(Res.string.feature_search_topics) + updatesString = getString(Res.string.feature_search_updates) + tryAnotherSearchString = getString(Res.string.feature_search_try_another_search) + + " " + getString(Res.string.feature_search_interests) + " " + getString(Res.string.feature_search_to_browse_topics) + searchNotReadyString = getString(Res.string.feature_search_not_ready) } @Test diff --git a/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt index 2ccb7974f..1cd7211c0 100644 --- a/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt +++ b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt @@ -84,10 +84,10 @@ import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.InterestsItem import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success -import com.google.samples.apps.nowinandroid.core.ui.R.string import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent import com.google.samples.apps.nowinandroid.core.ui.collectAsStateWithLifecycle import com.google.samples.apps.nowinandroid.core.ui.newsFeed +import nowinandroid.core.ui.generated.resources.core_ui_back import nowinandroid.feature.search.generated.resources.feature_search_clear_recent_searches_content_desc import nowinandroid.feature.search.generated.resources.feature_search_clear_search_text_content_desc import nowinandroid.feature.search.generated.resources.feature_search_interests @@ -102,6 +102,7 @@ import nowinandroid.feature.search.generated.resources.feature_search_updates import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview import org.jetbrains.compose.ui.tooling.preview.PreviewParameter +import nowinandroid.core.ui.generated.resources.Res as uiR import nowinandroid.feature.search.generated.resources.Res as searchR @Composable @@ -220,7 +221,7 @@ fun EmptySearchResultBody( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(horizontal = 48.dp), ) { - val message = stringResource(id = searchR.string.feature_search_result_not_found, searchQuery) + val message = stringResource(searchR.string.feature_search_result_not_found, searchQuery) val start = message.indexOf(searchQuery) Text( text = AnnotatedString( @@ -237,9 +238,9 @@ fun EmptySearchResultBody( textAlign = TextAlign.Center, modifier = Modifier.padding(vertical = 24.dp), ) - val interests = stringResource(id = searchR.string.feature_search_interests) + val interests = stringResource(searchR.string.feature_search_interests) val tryAnotherSearchString = buildAnnotatedString { - append(stringResource(id = searchR.string.feature_search_try_another_search)) + append(stringResource(searchR.string.feature_search_try_another_search)) append(" ") withStyle( style = SpanStyle( @@ -251,7 +252,7 @@ fun EmptySearchResultBody( append(interests) } append(" ") - append(stringResource(id = searchR.string.feature_search_to_browse_topics)) + append(stringResource(searchR.string.feature_search_to_browse_topics)) } ClickableText( text = tryAnotherSearchString, @@ -279,7 +280,7 @@ private fun SearchNotReadyBody() { modifier = Modifier.padding(horizontal = 48.dp), ) { Text( - text = stringResource(id = searchR.string.feature_search_not_ready), + text = stringResource(searchR.string.feature_search_not_ready), style = MaterialTheme.typography.bodyLarge, textAlign = TextAlign.Center, modifier = Modifier.padding(vertical = 24.dp), @@ -320,7 +321,7 @@ private fun SearchResultBody( Text( text = buildAnnotatedString { withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { - append(stringResource(id = searchR.string.feature_search_topics)) + append(stringResource(searchR.string.feature_search_topics)) } }, modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), @@ -356,7 +357,7 @@ private fun SearchResultBody( Text( text = buildAnnotatedString { withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { - append(stringResource(id = searchR.string.feature_search_updates)) + append(stringResource(searchR.string.feature_search_updates)) } }, modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), @@ -408,7 +409,7 @@ private fun RecentSearchesBody( Text( text = buildAnnotatedString { withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { - append(stringResource(id = searchR.string.feature_search_recent_searches)) + append(stringResource(searchR.string.feature_search_recent_searches)) } }, modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), @@ -423,7 +424,7 @@ private fun RecentSearchesBody( Icon( imageVector = NiaIcons.Close, contentDescription = stringResource( - id = searchR.string.feature_search_clear_recent_searches_content_desc, + searchR.string.feature_search_clear_recent_searches_content_desc, ), tint = MaterialTheme.colorScheme.onSurface, ) @@ -461,7 +462,7 @@ private fun SearchToolbar( Icon( imageVector = NiaIcons.ArrowBack, contentDescription = stringResource( - id = string.core_ui_back, + uiR.string.core_ui_back, ), ) } @@ -497,7 +498,7 @@ private fun SearchTextField( Icon( imageVector = NiaIcons.Search, contentDescription = stringResource( - id = searchR.string.feature_search_title, + searchR.string.feature_search_title, ), tint = MaterialTheme.colorScheme.onSurface, ) @@ -512,7 +513,7 @@ private fun SearchTextField( Icon( imageVector = NiaIcons.Close, contentDescription = stringResource( - id = searchR.string.feature_search_clear_search_text_content_desc, + searchR.string.feature_search_clear_search_text_content_desc, ), tint = MaterialTheme.colorScheme.onSurface, ) diff --git a/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt index 257d8b68e..76b9460f5 100644 --- a/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt +++ b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchUiStatePreviewParameterProvider.kt @@ -18,10 +18,10 @@ package com.google.samples.apps.nowinandroid.feature.search -import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.newsResources import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.topics +import org.jetbrains.compose.ui.tooling.preview.PreviewParameterProvider /** * This [PreviewParameterProvider](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/PreviewParameterProvider) diff --git a/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt index 6c2af240c..5afe79684 100644 --- a/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt +++ b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt @@ -28,7 +28,7 @@ import com.google.samples.apps.nowinandroid.core.data.repository.UserDataReposit import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUseCase import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase import com.google.samples.apps.nowinandroid.core.model.data.UserSearchResult -import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch @@ -37,9 +37,9 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import javax.inject.Inject +import me.tatarka.inject.annotations.Inject -@HiltViewModel +@OptIn(ExperimentalCoroutinesApi::class) class SearchViewModel @Inject constructor( getSearchContentsUseCase: GetSearchContentsUseCase, recentSearchQueriesUseCase: GetRecentSearchQueriesUseCase, diff --git a/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt index 81f3576b4..fa6e2bcb8 100644 --- a/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt +++ b/feature/search/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt @@ -20,7 +20,6 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable -import com.google.samples.apps.nowinandroid.feature.search.SearchRoute const val SEARCH_ROUTE = "search_route" @@ -34,10 +33,10 @@ fun NavGraphBuilder.searchScreen( // TODO: Handle back stack for each top-level destination. At the moment each top-level // destination may have own search screen's back stack. composable(route = SEARCH_ROUTE) { - SearchRoute( - onBackClick = onBackClick, - onInterestsClick = onInterestsClick, - onTopicClick = onTopicClick, - ) +// SearchRoute( +// onBackClick = onBackClick, +// onInterestsClick = onInterestsClick, +// onTopicClick = onTopicClick, +// ) } } From 0db59ec1a0a641e27af4291740ea15b8f8156005 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 3 Jul 2024 14:52:13 -0700 Subject: [PATCH 6/6] Update progress in readme.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 241039ee9..af47da1e9 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ As Firebase Analytics does not yet support Kotlin Multiplatform, the implementat | :feature:bookmarks | In progress | ✔️ | ✔️ | ✔️ | ❌ | | :feature:foryou | In progress | ✔️ | ✔️ | ✔️ | ❌ | | :feature:interests | In progress | ✔️ | ✔️ | ✔️ | ❌ | -| :feature:search | Not started | ❌ | ❌ | ❌ | ❌ | +| :feature:search | In progress | ✔️ | ✔️ | ✔️ | ❌ | | :feature:settings | Not started | ❌ | ❌ | ❌ | ❌ | | :feature:topic | Not started | ❌ | ❌ | ❌ | ❌ | | lint | Not started | ❌ | ❌ | ❌ | ❌ |