From e92d6a11532b4d778200b5c520e6518c1e883571 Mon Sep 17 00:00:00 2001 From: Himanshu Choudhary Date: Tue, 5 May 2026 13:47:31 +0530 Subject: [PATCH 1/4] Added snippets for compose test onRootWithViewInteraction API. --- compose/snippets/build.gradle.kts | 1 + .../compose/snippets/test/TestSnippets.kt | 59 +++++++++++++++++++ gradle/libs.versions.toml | 6 +- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/compose/snippets/build.gradle.kts b/compose/snippets/build.gradle.kts index f151d4cd9..28ee15680 100644 --- a/compose/snippets/build.gradle.kts +++ b/compose/snippets/build.gradle.kts @@ -164,6 +164,7 @@ dependencies { androidTestImplementation(libs.androidx.test.core) androidTestImplementation(libs.androidx.test.runner) androidTestImplementation(libs.androidx.test.espresso.core) + androidTestImplementation(libs.androidx.test.espresso.contrib) androidTestImplementation(libs.androidx.compose.ui.test) debugImplementation(libs.androidx.compose.ui.tooling) diff --git a/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt b/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt index 0676ded2e..4d9ef8d53 100644 --- a/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt +++ b/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt @@ -16,6 +16,7 @@ package com.example.compose.snippets.test +import android.view.View import androidx.compose.material3.Text import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -24,12 +25,23 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.v2.createComposeRule import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.onRootWithViewInteraction +import androidx.compose.ui.test.performClick import androidx.compose.ui.test.v2.runComposeUiTest +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions.swipeLeft +import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.matcher.ViewMatchers.hasDescendant +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText import kotlinx.coroutines.delay import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest +import org.hamcrest.CoreMatchers.allOf import org.junit.Rule import org.junit.Test @@ -84,3 +96,50 @@ class Test2 { } // [END android_compose_test_v2_apis] } + +@OptIn(ExperimentalTestApi::class) +class OnRootWithViewInteractionApiSnippets { + private val recyclerViewId = View.generateViewId() + private val rootViewId = View.generateViewId() + private val viewPagerViewId = View.generateViewId() + private val fragmentRootViewId = View.generateViewId() +// [START android_compose_test_onRootWithViewInteraction_api_1] + @Test + fun testComposeButtonInsideRecyclerViewItem() = runComposeUiTest { + // Scroll to the desired position using Espresso + Espresso.onView(withId(recyclerViewId)) + .perform(RecyclerViewActions.scrollToPosition(3)) + + // Define an Espresso ViewInteraction that uniquely identifies the row + val rowView = Espresso.onView( + allOf( + withId(rootViewId), + hasDescendant(withText("Item #3")) + ) + ) + + // Scope the Compose search strictly to that specific row View + onRootWithViewInteraction(rowView) + .onNode(hasText("Like")) + .performClick() + } +// [END android_compose_test_onRootWithViewInteraction_api_1] + +// [START android_compose_test_onRootWithViewInteraction_api_2] + @Test + fun testComposeButtonInsideViewPagerItem() = runComposeUiTest { + // Swipe to the desired page using Espresso + Espresso.onView(withId(viewPagerViewId)).perform(swipeLeft()) + + // Identify the specific container view using Espresso + val fragmentB = Espresso.onView(withId(fragmentRootViewId)) + + // The generic text "Save" is now unique within this view scope + onRootWithViewInteraction(fragmentB) + .onNode(hasText("Save")) + .assertIsDisplayed() + } +// [END android_compose_test_onRootWithViewInteraction_api_2] +} + +private class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 24f9342c5..2496a68fe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -47,6 +47,7 @@ coil = "2.7.0" # @keep compileSdk = "37" compose-latest = "1.10.5" +compose-test-latest = "1.12.0-alpha01" composeUiTooling = "1.5.6" coreSplashscreen = "1.2.0" coroutines = "1.10.2" @@ -155,8 +156,8 @@ androidx-compose-runtime-livedata = { module = "androidx.compose.runtime:runtime androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose-latest" } androidx-compose-ui-googlefonts = { module = "androidx.compose.ui:ui-text-google-fonts" } androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" } -androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test", version.ref = "compose-latest" } -androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose-latest" } +androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test", version.ref = "compose-test-latest" } +androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose-test-latest" } androidx-compose-ui-test-junit4-accessibility = { module = "androidx.compose.ui:ui-test-junit4-accessibility", version.ref = "androidx-compose-ui-test-junit4-accessibility" } androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } @@ -214,6 +215,7 @@ androidx-registry-provider-play-services = { module = "androidx.credentials.regi androidx-startup-runtime = { module = "androidx.startup:startup-runtime", version.ref = "androidx-startup-runtime" } androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" } androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" } +androidx-test-espresso-contrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "androidx-test-espresso" } androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-junit" } androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test" } androidx-tiles = { module = "androidx.wear.tiles:tiles", version.ref = "tiles" } From d920c9997befad9ad21b16369df6cd234b5f069b Mon Sep 17 00:00:00 2001 From: hiichoudhary <239537084+hiichoudhary@users.noreply.github.com> Date: Tue, 5 May 2026 09:07:51 +0000 Subject: [PATCH 2/4] Apply Spotless --- .../java/com/example/compose/snippets/test/TestSnippets.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt b/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt index 4d9ef8d53..1b72f1d3e 100644 --- a/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt +++ b/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt @@ -142,4 +142,4 @@ class OnRootWithViewInteractionApiSnippets { // [END android_compose_test_onRootWithViewInteraction_api_2] } -private class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) \ No newline at end of file +private class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) From b792423c6e3efef52b4eb2f3044127bb8fd87d73 Mon Sep 17 00:00:00 2001 From: Himanshu Choudhary Date: Wed, 6 May 2026 13:28:26 +0530 Subject: [PATCH 3/4] Updated compose lib version. --- .../java/com/example/compose/snippets/test/TestSnippets.kt | 2 +- gradle/libs.versions.toml | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt b/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt index 4d9ef8d53..1b72f1d3e 100644 --- a/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt +++ b/compose/snippets/src/androidTest/java/com/example/compose/snippets/test/TestSnippets.kt @@ -142,4 +142,4 @@ class OnRootWithViewInteractionApiSnippets { // [END android_compose_test_onRootWithViewInteraction_api_2] } -private class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) \ No newline at end of file +private class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2496a68fe..a892a50d0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,8 +46,7 @@ appcompat = "1.7.1" coil = "2.7.0" # @keep compileSdk = "37" -compose-latest = "1.10.5" -compose-test-latest = "1.12.0-alpha01" +compose-latest = "1.12.0-alpha01" composeUiTooling = "1.5.6" coreSplashscreen = "1.2.0" coroutines = "1.10.2" @@ -156,8 +155,8 @@ androidx-compose-runtime-livedata = { module = "androidx.compose.runtime:runtime androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose-latest" } androidx-compose-ui-googlefonts = { module = "androidx.compose.ui:ui-text-google-fonts" } androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" } -androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test", version.ref = "compose-test-latest" } -androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose-test-latest" } +androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test", version.ref = "compose-latest" } +androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose-latest" } androidx-compose-ui-test-junit4-accessibility = { module = "androidx.compose.ui:ui-test-junit4-accessibility", version.ref = "androidx-compose-ui-test-junit4-accessibility" } androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } From 05187c2a6f98eba1397cd45db20833083c1e1fae Mon Sep 17 00:00:00 2001 From: Himanshu Choudhary Date: Thu, 7 May 2026 11:07:39 +0530 Subject: [PATCH 4/4] Update dependencies --- .../com/example/compose/snippets/components/DatePickers.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/components/DatePickers.kt b/compose/snippets/src/main/java/com/example/compose/snippets/components/DatePickers.kt index 4d59ea4ef..f5dca89af 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/components/DatePickers.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/components/DatePickers.kt @@ -61,6 +61,7 @@ import com.example.compose.snippets.ui.theme.SnippetsTheme import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import androidx.compose.ui.platform.LocalLocale @Preview @Composable @@ -105,7 +106,7 @@ fun DatePickerExamples() { // [END_EXCLUDE] if (selectedDate != null) { val date = Date(selectedDate!!) - val formattedDate = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(date) + val formattedDate = SimpleDateFormat("MMM dd, yyyy", LocalLocale.current.platformLocale).format(date) Text("Selected date: $formattedDate") } else { Text("No date selected") @@ -121,8 +122,8 @@ fun DatePickerExamples() { if (selectedDateRange.first != null && selectedDateRange.second != null) { val startDate = Date(selectedDateRange.first!!) val endDate = Date(selectedDateRange.second!!) - val formattedStartDate = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(startDate) - val formattedEndDate = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(endDate) + val formattedStartDate = SimpleDateFormat("MMM dd, yyyy", LocalLocale.current.platformLocale).format(startDate) + val formattedEndDate = SimpleDateFormat("MMM dd, yyyy", LocalLocale.current.platformLocale).format(endDate) Text("Selected date range: $formattedStartDate - $formattedEndDate") } else { Text("No date range selected")