From 3975c3fb5cd98a98bced017e47bd3562f7f49897 Mon Sep 17 00:00:00 2001 From: williamrai Date: Tue, 25 Nov 2025 16:15:49 -0500 Subject: [PATCH 01/16] - espresso fixes --- .../java/org/wikipedia/base/BaseTest.kt | 16 +++++++++++----- .../java/org/wikipedia/robots/DialogRobot.kt | 4 ++++ .../offline/SavedArticleOnlineOfflineTest.kt | 3 +++ .../java/org/wikipedia/testsuites/TestSuite.kt | 1 - 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt b/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt index 59bf433eadb..d7aeda793b9 100644 --- a/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt +++ b/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt @@ -40,6 +40,8 @@ data class DataInjector( val readingListShareTooltipShown: Boolean = true, val otdEntryDialogShown: Boolean = true, val enableYearInReview: Boolean = false, + val yearInReviewReadingListSurveyShown: Boolean = false, + val exploreFeedSurveyShown: Boolean = false ) abstract class BaseTest( @@ -67,11 +69,15 @@ abstract class BaseTest( init { val intent = Intent(context, activityClass) activityScenarioRule = ActivityScenarioRule(intent) - Prefs.isInitialOnboardingEnabled = dataInjector.isInitialOnboardingEnabled - Prefs.showOneTimeCustomizeToolbarTooltip = dataInjector.showOneTimeCustomizeToolbarTooltip - Prefs.readingListShareTooltipShown = dataInjector.readingListShareTooltipShown - Prefs.otdEntryDialogShown = dataInjector.otdEntryDialogShown - Prefs.isYearInReviewEnabled = dataInjector.enableYearInReview + Prefs.apply { + isInitialOnboardingEnabled = dataInjector.isInitialOnboardingEnabled + showOneTimeCustomizeToolbarTooltip = dataInjector.showOneTimeCustomizeToolbarTooltip + readingListShareTooltipShown = dataInjector.readingListShareTooltipShown + otdEntryDialogShown = dataInjector.otdEntryDialogShown + isYearInReviewEnabled = dataInjector.enableYearInReview + yearInReviewReadingListSurveyShown = dataInjector.yearInReviewReadingListSurveyShown + exploreFeedSurveyShown = dataInjector.exploreFeedSurveyShown + } dataInjector.overrideEditsContribution?.let { Prefs.overrideSuggestedEditContribution = it } diff --git a/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt index 17e0e1bca74..bcdb987b8d1 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt @@ -6,6 +6,10 @@ import org.wikipedia.R class DialogRobot : BaseRobot() { + fun dismissSurveyDialog() = apply { + click.ifDialogShown("No thanks", errorString = "No Survey Dialog dialog shown.") + } + fun dismissContributionDialog() = apply { click.ifDialogShown("No thanks", errorString = "No Contribution dialog shown.") } diff --git a/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt b/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt index 778bf36bda5..7a6ecf1a039 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt @@ -68,6 +68,9 @@ class SavedArticleOnlineOfflineTest : BaseTest( .pressBack() bottomNavRobot .navigateToExploreFeed() + dialogRobot + .dismissSurveyDialog() + bottomNavRobot .navigateToSavedPage() savedScreenRobot .clickItemOnTheList(0) diff --git a/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt b/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt index 1860c1ade61..c6ceee74ae7 100644 --- a/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt +++ b/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt @@ -17,7 +17,6 @@ import org.wikipedia.tests.SuggestedEditScreenTest ExploreFeedTestSuite::class, SearchTest::class, SuggestedEditScreenTest::class, - SettingsTestSuite::class, DeepLinkingTest::class, ArticlesTestSuite::class ) From f574a4173202f8b7a5d4f7d12d1513f3a5c3b91e Mon Sep 17 00:00:00 2001 From: williamrai Date: Wed, 26 Nov 2025 12:04:20 -0500 Subject: [PATCH 02/16] - adds an optional action to scroll to in ListActions.kt - adds isItemPresent before scrolling with log --- .../org/wikipedia/base/actions/ListActions.kt | 21 ++++++++++++++++++ .../robots/feature/ExploreFeedRobot.kt | 22 ++++++++++++------- .../wikipedia/tests/OfflinePageLoadTest.kt | 4 ++-- .../tests/articles/ArticleTabTest.kt | 4 ++-- .../tests/articles/SavedArticleTest.kt | 2 +- .../tests/explorefeed/BecauseYouReadTest.kt | 4 ++-- .../tests/explorefeed/FeedScreenTest.kt | 16 +++++++------- .../wikipedia/tests/settings/ShowImageTest.kt | 10 +++++---- 8 files changed, 56 insertions(+), 27 deletions(-) diff --git a/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt b/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt index fc31f7e7e78..24b0217abd4 100644 --- a/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt +++ b/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt @@ -10,6 +10,7 @@ import androidx.core.widget.NestedScrollView import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onData import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.NoMatchingViewException import androidx.test.espresso.UiController import androidx.test.espresso.ViewAction import androidx.test.espresso.ViewAssertion @@ -25,6 +26,7 @@ import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText +import junit.framework.AssertionFailedError import org.hamcrest.Description import org.hamcrest.Matcher import org.hamcrest.Matchers @@ -223,6 +225,25 @@ class ListActions { ) } + fun isItemPresent( + recyclerViewId: Int = R.id.feed_view, + title: String, + textViewId: Int = R.id.view_card_header_title + ): Boolean { + return try { + onView(withId(recyclerViewId)) + .check(matches(hasDescendant(allOf( + withId(textViewId), + withText(title) + )))) + true + } catch (_: NoMatchingViewException) { + false + } catch (_: AssertionFailedError) { + false + } + } + private fun verifyItemAtPosition( recyclerViewId: Int, position: Int, diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt index 3212facaf43..7a8ca10bfa5 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt @@ -190,18 +190,24 @@ class ExploreFeedRobot : BaseRobot() { delay(TestConfig.DELAY_SWIPE_TO_REFRESH) } - fun scrollToItem( + fun scrollToAndPerform( recyclerViewId: Int = R.id.feed_view, title: String, textViewId: Int = R.id.view_card_header_title, - verticalOffset: Int = 200 + verticalOffset: Int = 200, + action: (ExploreFeedRobot.() -> Unit)? = null ) = apply { - list.scrollToRecyclerView( - recyclerViewId, - title, - textViewId, - verticalOffset - ) + if (list.isItemPresent(title = title)) { + list.scrollToRecyclerView( + recyclerViewId, + title, + textViewId, + verticalOffset + ) + action?.invoke(this@ExploreFeedRobot) + } else { + Log.i("ExploreFeed", "$title not present today - skipping test") + } } fun assertFeaturedArticleTitleColor(theme: Theme) = apply { diff --git a/app/src/androidTest/java/org/wikipedia/tests/OfflinePageLoadTest.kt b/app/src/androidTest/java/org/wikipedia/tests/OfflinePageLoadTest.kt index 4a651c58079..ba87c0768e4 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/OfflinePageLoadTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/OfflinePageLoadTest.kt @@ -19,13 +19,13 @@ class OfflinePageLoadTest : BaseTest( systemRobot .clickOnSystemDialogWithText("Allow") exploreFeedRobot - .scrollToItem(title = FEATURED_ARTICLE) + .scrollToAndPerform(title = FEATURED_ARTICLE) .clickOnFeaturedArticle() .pressBack() systemRobot .turnOffInternet() exploreFeedRobot - .scrollToItem(title = FEATURED_ARTICLE) + .scrollToAndPerform(title = FEATURED_ARTICLE) .clickOnFeaturedArticle() .pressBack() systemRobot diff --git a/app/src/androidTest/java/org/wikipedia/tests/articles/ArticleTabTest.kt b/app/src/androidTest/java/org/wikipedia/tests/articles/ArticleTabTest.kt index 105cb0632ad..f5116c47f33 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/articles/ArticleTabTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/articles/ArticleTabTest.kt @@ -35,12 +35,12 @@ class ArticleTabTest : BaseTest( .clickOnSystemDialogWithText("Allow") .disableDarkMode(context) exploreFeedRobot - .scrollToItem(title = TestConstants.FEATURED_ARTICLE) + .scrollToAndPerform(title = TestConstants.FEATURED_ARTICLE) .clickOnFeaturedArticle() pageRobot .navigateUp() exploreFeedRobot - .scrollToItem(title = TestConstants.FEATURED_ARTICLE) + .scrollToAndPerform(title = TestConstants.FEATURED_ARTICLE) .clickOnFeaturedArticle() dialogRobot .dismissBigEnglishDialog() diff --git a/app/src/androidTest/java/org/wikipedia/tests/articles/SavedArticleTest.kt b/app/src/androidTest/java/org/wikipedia/tests/articles/SavedArticleTest.kt index 490cbee718c..b06177d34f8 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/articles/SavedArticleTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/articles/SavedArticleTest.kt @@ -47,7 +47,7 @@ class SavedArticleTest : BaseTest( } }) exploreFeedRobot - .scrollToItem(title = FEATURED_ARTICLE) + .scrollToAndPerform(title = FEATURED_ARTICLE) .longClickFeaturedArticleCardContainer() .clickSave() setDeviceOrientation(isLandscape = true) diff --git a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/BecauseYouReadTest.kt b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/BecauseYouReadTest.kt index ca5246f4a60..f70a1c07c16 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/BecauseYouReadTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/BecauseYouReadTest.kt @@ -32,11 +32,11 @@ class BecauseYouReadTest : BaseTest( // Because you read, requires users to read some article for 30 seconds exploreFeedRobot - .scrollToItem(title = FEATURED_ARTICLE) + .scrollToAndPerform(title = FEATURED_ARTICLE) .stayOnFeaturedArticleFor(milliseconds = 30000) .pressBack() .swipeToRefresh() - .scrollToItem(title = BECAUSE_YOU_READ) + .scrollToAndPerform(title = BECAUSE_YOU_READ) .clickBecauseYouReadArticle() .pressBack() } diff --git a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt index 18e9ead84e9..a99770831d1 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt @@ -42,11 +42,11 @@ class FeedScreenTest : BaseTest( // Feed Test flow exploreFeedRobot - .scrollToItem(title = FEATURED_ARTICLE) + .scrollToAndPerform(title = FEATURED_ARTICLE) .assertFeaturedArticleTitleColor(theme = Theme.LIGHT) .clickOnFeaturedArticle() .pressBack() - .scrollToItem(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE, verticalOffset = -100) + .scrollToAndPerform(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE, verticalOffset = -100) .clickTodayOnWikipedia() dialogRobot .dismissBigEnglishDialog() @@ -56,23 +56,23 @@ class FeedScreenTest : BaseTest( systemRobot .enableDarkMode(context) exploreFeedRobot - .scrollToItem(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE, verticalOffset = 400) - .scrollToItem(title = TOP_READ_ARTICLES, verticalOffset = 400) + .scrollToAndPerform(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE, verticalOffset = 400) + .scrollToAndPerform(title = TOP_READ_ARTICLES, verticalOffset = 400) .assertTopReadTitleColor(theme = Theme.DARK) .clickTopReadArticle() - .scrollToItem(title = PICTURE_OF_DAY) + .scrollToAndPerform(title = PICTURE_OF_DAY) .clickPictureOfTheDay() .pressBack() systemRobot .enableDarkMode(context) exploreFeedRobot - .scrollToItem(title = NEWS_CARD) + .scrollToAndPerform(title = NEWS_CARD) .clickNewsArticle() .pressBack() - .scrollToItem(title = ON_THIS_DAY_CARD) + .scrollToAndPerform(title = ON_THIS_DAY_CARD) .clickOnThisDayCard() .pressBack() - .scrollToItem(title = RANDOM_CARD) + .scrollToAndPerform(title = RANDOM_CARD) .clickRandomArticle() .pressBack() } diff --git a/app/src/androidTest/java/org/wikipedia/tests/settings/ShowImageTest.kt b/app/src/androidTest/java/org/wikipedia/tests/settings/ShowImageTest.kt index 0dcf8e41fc3..9509bdf322b 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/settings/ShowImageTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/settings/ShowImageTest.kt @@ -38,9 +38,11 @@ class ShowImageTest : BaseTest( .toggleShowImages() exploreFeedRobot .pressBack() - .scrollToItem(title = "Featured article") - .verifyFeaturedArticleImageIsNotVisible() - .scrollToItem(title = "Top read") - .verifyTopReadArticleIsGreyedOut(theme = Theme.LIGHT) + .scrollToAndPerform(title = "Featured article") { + verifyFeaturedArticleImageIsNotVisible() + } + .scrollToAndPerform(title = "Top read") { + verifyTopReadArticleIsGreyedOut(theme = Theme.LIGHT) + } } } From c31286f80f880965d2a41a3ab7593bd794573422 Mon Sep 17 00:00:00 2001 From: williamrai Date: Wed, 26 Nov 2025 13:25:16 -0500 Subject: [PATCH 03/16] - fixes AboutSettingsTest --- .../org/wikipedia/robots/feature/SettingsRobot.kt | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt index 6fa4957f45f..6ebe86dd9b2 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt @@ -12,14 +12,12 @@ import androidx.test.espresso.ViewAction import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.scrollTo import androidx.test.espresso.assertion.ViewAssertions.doesNotExist -import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withParent import androidx.test.espresso.matcher.ViewMatchers.withText import org.hamcrest.Matchers.allOf import org.junit.Assert.assertTrue @@ -86,18 +84,12 @@ class SettingsRobot : BaseRobot() { fun clickDeveloperMode() = apply { // Assert that developer mode is activated - onView(allOf(withId(R.id.developer_settings), withContentDescription("Developer settings"), - childAtPosition(childAtPosition(withId(androidx.appcompat.R.id.action_bar), 2), 0), isDisplayed())) - .perform(click()) + click.onDisplayedViewWithIdAnContentDescription(R.id.developer_settings, "Developer settings") delay(TestConfig.DELAY_MEDIUM) } fun assertWeAreInDeveloperSettings() = apply { - onView(allOf(withText("Developer settings"), - withParent(allOf(withId(androidx.appcompat.R.id.action_bar), - withParent(withId(androidx.appcompat.R.id.action_bar_container)) - )), isDisplayed())) - .check(matches(withText("Developer settings"))) + verify.viewWithTextDisplayed("Developer settings") delay(TestConfig.DELAY_MEDIUM) } From 8a0bf683e6bbc3303672f2ca7ffbfeebedd64a42 Mon Sep 17 00:00:00 2001 From: williamrai Date: Wed, 26 Nov 2025 15:11:42 -0500 Subject: [PATCH 04/16] - updates github actions workflow, adds TestSuite to smoke_test workf;pw --- .github/workflows/android_offline_test.yml | 1 + .github/workflows/android_smoke_test.yml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/android_offline_test.yml b/.github/workflows/android_offline_test.yml index e1b3de31423..686b6bc488f 100644 --- a/.github/workflows/android_offline_test.yml +++ b/.github/workflows/android_offline_test.yml @@ -23,6 +23,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: 29 + arch: arm64-v8a script: ./gradlew connectedDevDebugAndroidTest --stacktrace --no-daemon --info -P android.testInstrumentationRunnerArguments.class=org.wikipedia.tests.offline.SavedArticleOnlineOfflineTest - name: Upload results diff --git a/.github/workflows/android_smoke_test.yml b/.github/workflows/android_smoke_test.yml index 3c4f0c35081..00b1a871518 100644 --- a/.github/workflows/android_smoke_test.yml +++ b/.github/workflows/android_smoke_test.yml @@ -23,7 +23,8 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: 29 - script: ./gradlew connectedDevDebugAndroidTest --stacktrace --no-daemon + arch: arm64-v8a + script: ./gradlew connectedDevDebugAndroidTest --stacktrace --no-daemon --info -P android.testInstrumentationRunnerArguments.class=org.wikipedia.testsuites.TestSuite - name: Upload results if: ${{ always() }} From 897bf18d4e0e17cf8683211ce2e6940e9406b4e0 Mon Sep 17 00:00:00 2001 From: williamrai Date: Wed, 26 Nov 2025 15:35:04 -0500 Subject: [PATCH 05/16] - updates github workflow to use ubuntu runners --- .github/workflows/android_offline_test.yml | 9 +++++++-- .github/workflows/android_smoke_test.yml | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/android_offline_test.yml b/.github/workflows/android_offline_test.yml index 686b6bc488f..320f4a7d490 100644 --- a/.github/workflows/android_offline_test.yml +++ b/.github/workflows/android_offline_test.yml @@ -7,7 +7,7 @@ env: jobs: instrumentation-tests: - runs-on: macos-latest + runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -19,11 +19,16 @@ jobs: cache: 'gradle' - uses: gradle/gradle-build-action@v3 + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: Instrumentation Tests uses: reactivecircus/android-emulator-runner@v2 with: api-level: 29 - arch: arm64-v8a script: ./gradlew connectedDevDebugAndroidTest --stacktrace --no-daemon --info -P android.testInstrumentationRunnerArguments.class=org.wikipedia.tests.offline.SavedArticleOnlineOfflineTest - name: Upload results diff --git a/.github/workflows/android_smoke_test.yml b/.github/workflows/android_smoke_test.yml index 00b1a871518..e123d518c3f 100644 --- a/.github/workflows/android_smoke_test.yml +++ b/.github/workflows/android_smoke_test.yml @@ -7,7 +7,7 @@ env: jobs: instrumentation-tests: - runs-on: macos-latest + runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -19,11 +19,16 @@ jobs: cache: 'gradle' - uses: gradle/gradle-build-action@v3 + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: Instrumentation Tests uses: reactivecircus/android-emulator-runner@v2 with: api-level: 29 - arch: arm64-v8a script: ./gradlew connectedDevDebugAndroidTest --stacktrace --no-daemon --info -P android.testInstrumentationRunnerArguments.class=org.wikipedia.testsuites.TestSuite - name: Upload results From e703b6fea41a1ac701c6e7232532286b0d7d6d1b Mon Sep 17 00:00:00 2001 From: williamrai Date: Mon, 1 Dec 2025 11:45:15 -0500 Subject: [PATCH 06/16] - fixes permission issue for api below 33 - code fixes, adds new test functions - github workflow updates to test on different api level and device --- .github/workflows/android_offline_test.yml | 23 ++++++++++++++++--- .../java/org/wikipedia/base/BaseTest.kt | 9 +++++--- .../wikipedia/base/actions/ClickActions.kt | 4 ++-- .../java/org/wikipedia/robots/DialogRobot.kt | 10 +++++++- .../wikipedia/robots/feature/SearchRobot.kt | 18 +++++++++++++++ .../offline/SavedArticleOnlineOfflineTest.kt | 5 ++-- 6 files changed, 58 insertions(+), 11 deletions(-) diff --git a/.github/workflows/android_offline_test.yml b/.github/workflows/android_offline_test.yml index 320f4a7d490..e00897909ab 100644 --- a/.github/workflows/android_offline_test.yml +++ b/.github/workflows/android_offline_test.yml @@ -9,6 +9,19 @@ jobs: instrumentation-tests: runs-on: ubuntu-latest timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - api-level: 29 + target: google_apis + arch: x86_64 + profile: pixel_2 + + - api-level: 36 + target: google_apis + arch: x86_64 + profile: pixel_9 steps: - uses: actions/checkout@v6 @@ -25,15 +38,19 @@ jobs: sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - - name: Instrumentation Tests + - name: run instrumentation tests uses: reactivecircus/android-emulator-runner@v2 with: - api-level: 29 + api-level: ${{ matrix.api-level }} + target: ${{ matrix.target }} + arch: ${{ matrix.arch }} + profile: ${{ matrix.profile }} + disable-animations: true script: ./gradlew connectedDevDebugAndroidTest --stacktrace --no-daemon --info -P android.testInstrumentationRunnerArguments.class=org.wikipedia.tests.offline.SavedArticleOnlineOfflineTest - name: Upload results if: ${{ always() }} uses: actions/upload-artifact@v4 with: - name: instrumentation-test-results ${{ matrix.api-level }} + name: instrumentation-test-results${{ matrix.api-level }} path: ./**/build/reports/androidTests/connected/** diff --git a/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt b/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt index d7aeda793b9..450fa9c8ab9 100644 --- a/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt +++ b/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt @@ -3,6 +3,7 @@ package org.wikipedia.base import android.Manifest import android.content.Context import android.content.Intent +import android.os.Build import androidx.appcompat.app.AppCompatActivity import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.espresso.IdlingPolicies @@ -58,9 +59,11 @@ abstract class BaseTest( var composeTestRule = createComposeRule() @get:Rule - val permissionRule: GrantPermissionRule = GrantPermissionRule.grant( - Manifest.permission.POST_NOTIFICATIONS - ) + val permissionRule: GrantPermissionRule = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + GrantPermissionRule.grant(Manifest.permission.POST_NOTIFICATIONS) + } else { + GrantPermissionRule.grant() + } protected lateinit var activity: T protected lateinit var device: UiDevice diff --git a/app/src/androidTest/java/org/wikipedia/base/actions/ClickActions.kt b/app/src/androidTest/java/org/wikipedia/base/actions/ClickActions.kt index b4fb820962b..3f713c967f9 100644 --- a/app/src/androidTest/java/org/wikipedia/base/actions/ClickActions.kt +++ b/app/src/androidTest/java/org/wikipedia/base/actions/ClickActions.kt @@ -134,9 +134,9 @@ class ClickActions { .inRoot(isDialog()) .perform(click()) } catch (e: NoMatchingViewException) { - Log.e("BaseRobot", "$errorString") + Log.e("DialogRobot", errorString) } catch (e: Exception) { - Log.e("BaseRobot", "Unexpected Error: ${e.message}") + Log.e("DialogRobot", "Unexpected Error: ${e.message}") } } } diff --git a/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt index bcdb987b8d1..32cfcf6e2ca 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt @@ -2,6 +2,8 @@ package org.wikipedia.robots import BaseRobot import android.content.Context +import android.util.Log +import androidx.test.espresso.NoMatchingViewException import org.wikipedia.R class DialogRobot : BaseRobot() { @@ -15,7 +17,13 @@ class DialogRobot : BaseRobot() { } fun dismissBigEnglishDialog() = apply { - click.ifDialogShown("Maybe later", errorString = "No Big English dialog shown.") + try { + click.onDisplayedViewWithIdAnContentDescription(R.id.closeButton, "Close") + } catch (e: NoMatchingViewException) { + Log.e("DialogRobot", "No Big English Dialog shown.") + } catch (e: Exception) { + Log.e("DialogRobot", "Unexpected Error: ${e.message}") + } } fun clickLogOutUser() = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt index bb7cba86938..ce82cfefa9a 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt @@ -13,6 +13,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText +import junit.framework.AssertionFailedError import org.hamcrest.Matchers.allOf import org.wikipedia.R import org.wikipedia.WikipediaApp @@ -147,6 +148,23 @@ class SearchRobot : BaseRobot() { pressBack() } + fun pressBackUntilExploreFeed() = apply { + val maxAttempts = 3 + var attempts = 0 + while (attempts < maxAttempts) { + try { + verify.viewWithIdDoesNotExist(R.id.feed_view) + pressBack() + delay(TestConfig.DELAY_SHORT) + attempts++ + } catch (_: AssertionFailedError) { + break + } catch (_: Exception) { + break + } + } + } + fun swipeToDelete(position: Int, title: String) = apply { onView(withId(R.id.history_list)) .perform( diff --git a/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt b/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt index 7a6ecf1a039..41b517beecd 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt @@ -51,8 +51,7 @@ class SavedArticleOnlineOfflineTest : BaseTest( .pressBack() } }) - .pressBack() - .pressBack() + .pressBackUntilExploreFeed() bottomNavRobot .navigateToSavedPage() savedScreenRobot @@ -61,6 +60,8 @@ class SavedArticleOnlineOfflineTest : BaseTest( .verifySavedArticle("Apple") .verifySavedArticle("Orange") .clickItemOnReadingList(1) + dialogRobot + .dismissBigEnglishDialog() systemRobot .turnOnAirplaneMode() savedScreenRobot From 3202bb0078be8fe5fe0b0bc88679f6d14228d16c Mon Sep 17 00:00:00 2001 From: williamrai Date: Mon, 1 Dec 2025 13:31:50 -0500 Subject: [PATCH 07/16] - update workflow --- .github/workflows/android_offline_test.yml | 5 +---- .github/workflows/android_smoke_test.yml | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/android_offline_test.yml b/.github/workflows/android_offline_test.yml index e00897909ab..193fd715646 100644 --- a/.github/workflows/android_offline_test.yml +++ b/.github/workflows/android_offline_test.yml @@ -13,15 +13,13 @@ jobs: fail-fast: false matrix: include: - - api-level: 29 + - api-level: 23 target: google_apis arch: x86_64 - profile: pixel_2 - api-level: 36 target: google_apis arch: x86_64 - profile: pixel_9 steps: - uses: actions/checkout@v6 @@ -44,7 +42,6 @@ jobs: api-level: ${{ matrix.api-level }} target: ${{ matrix.target }} arch: ${{ matrix.arch }} - profile: ${{ matrix.profile }} disable-animations: true script: ./gradlew connectedDevDebugAndroidTest --stacktrace --no-daemon --info -P android.testInstrumentationRunnerArguments.class=org.wikipedia.tests.offline.SavedArticleOnlineOfflineTest diff --git a/.github/workflows/android_smoke_test.yml b/.github/workflows/android_smoke_test.yml index e123d518c3f..fb6fe2f5620 100644 --- a/.github/workflows/android_smoke_test.yml +++ b/.github/workflows/android_smoke_test.yml @@ -9,6 +9,17 @@ jobs: instrumentation-tests: runs-on: ubuntu-latest timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - api-level: 23 + target: google_apis + arch: x86_64 + + - api-level: 36 + target: google_apis + arch: x86_64 steps: - uses: actions/checkout@v6 @@ -28,12 +39,15 @@ jobs: - name: Instrumentation Tests uses: reactivecircus/android-emulator-runner@v2 with: - api-level: 29 + api-level: ${{ matrix.api-level }} + target: ${{ matrix.target }} + arch: ${{ matrix.arch }} + disable-animations: true script: ./gradlew connectedDevDebugAndroidTest --stacktrace --no-daemon --info -P android.testInstrumentationRunnerArguments.class=org.wikipedia.testsuites.TestSuite - name: Upload results if: ${{ always() }} uses: actions/upload-artifact@v4 with: - name: instrumentation-test-results ${{ matrix.api-level }} + name: instrumentation-test-results${{ matrix.api-level }} path: ./**/build/reports/androidTests/connected/** From cf0f481c93b604e5c9accf97e37931bb0ea66376 Mon Sep 17 00:00:00 2001 From: williamrai Date: Mon, 1 Dec 2025 13:54:12 -0500 Subject: [PATCH 08/16] - add "error code" to test espresso github action workflow --- .../wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt b/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt index 41b517beecd..8784a1143c4 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt @@ -26,6 +26,8 @@ class SavedArticleOnlineOfflineTest : BaseTest( @Test fun runTest() { + searchRobot + .clickSearchInsideSearchFragment() if (!isOnline()) { systemRobot .turnOffAirplaneMode() From 1c2d2f5450830b1f2e83c4d83a3390196ca13bd2 Mon Sep 17 00:00:00 2001 From: williamrai Date: Mon, 1 Dec 2025 14:35:39 -0500 Subject: [PATCH 09/16] - revert code to original --- .../wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt b/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt index 8784a1143c4..41b517beecd 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/offline/SavedArticleOnlineOfflineTest.kt @@ -26,8 +26,6 @@ class SavedArticleOnlineOfflineTest : BaseTest( @Test fun runTest() { - searchRobot - .clickSearchInsideSearchFragment() if (!isOnline()) { systemRobot .turnOffAirplaneMode() From 9392ba87c60359e6e6f7981ce905eb1233106d73 Mon Sep 17 00:00:00 2001 From: williamrai Date: Tue, 2 Dec 2025 11:54:27 -0500 Subject: [PATCH 10/16] - fixes changLanguage test and NavigationItemTest - adds two new function onParentViewWithChildIdAndText and selectTabWithText - code fixes --- .../java/org/wikipedia/base/BaseRobot.kt | 6 ++--- .../wikipedia/base/actions/ClickActions.kt | 17 +++++++++++- .../org/wikipedia/base/actions/ListActions.kt | 26 +++++++++++++++++++ .../java/org/wikipedia/robots/DialogRobot.kt | 2 +- .../org/wikipedia/robots/feature/PageRobot.kt | 8 +++--- .../wikipedia/robots/feature/SearchRobot.kt | 2 +- .../wikipedia/robots/feature/SettingsRobot.kt | 2 +- .../robots/navigation/BottomNavRobot.kt | 10 +++++-- .../robots/screen/HomeScreenRobot.kt | 2 +- .../tests/explorefeed/NavigationItemTest.kt | 2 +- 10 files changed, 62 insertions(+), 15 deletions(-) diff --git a/app/src/androidTest/java/org/wikipedia/base/BaseRobot.kt b/app/src/androidTest/java/org/wikipedia/base/BaseRobot.kt index 094d8b5b9b7..13d2bad4b4e 100644 --- a/app/src/androidTest/java/org/wikipedia/base/BaseRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/base/BaseRobot.kt @@ -15,6 +15,9 @@ import org.wikipedia.base.actions.WebActions import java.util.concurrent.TimeUnit abstract class BaseRobot { + protected val composeTestRule: ComposeTestRule + get() = ComposeTestManager.getComposeTestRule() + protected val click = ClickActions() protected val input = InputActions() protected val list = ListActions() @@ -24,9 +27,6 @@ abstract class BaseRobot { protected val verify = VerificationActions() protected val web = WebActions() - protected val composeTestRule: ComposeTestRule - get() = ComposeTestManager.getComposeTestRule() - protected fun delay(seconds: Long) { onView(isRoot()).perform(waitOnId(TimeUnit.SECONDS.toMillis(seconds))) } diff --git a/app/src/androidTest/java/org/wikipedia/base/actions/ClickActions.kt b/app/src/androidTest/java/org/wikipedia/base/actions/ClickActions.kt index 3f713c967f9..b3dde81b437 100644 --- a/app/src/androidTest/java/org/wikipedia/base/actions/ClickActions.kt +++ b/app/src/androidTest/java/org/wikipedia/base/actions/ClickActions.kt @@ -12,6 +12,7 @@ import androidx.test.espresso.ViewAction import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.doubleClick import androidx.test.espresso.matcher.RootMatchers.isDialog +import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast import androidx.test.espresso.matcher.ViewMatchers.withContentDescription @@ -49,7 +50,7 @@ class ClickActions { onView(allOf(withContentDescription(description), isDisplayed())).perform(click()) } - fun onDisplayedViewWithIdAnContentDescription( + fun onDisplayedViewWithIdAndContentDescription( @IdRes viewId: Int, description: String ) { @@ -62,6 +63,20 @@ class ClickActions { onView(withText(text)).perform(click()) } + fun onParentViewWithChildIdAndText(@IdRes childId: Int, text: String) { + onView( + allOf( + hasDescendant( + allOf( + withId(childId), + withText(text) + ) + ), + isDisplayed() + ) + ).perform(click()) + } + fun doubleClickOnViewWithId(@IdRes viewId: Int) { onView( allOf( diff --git a/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt b/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt index 24b0217abd4..e3e11c6bd3d 100644 --- a/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt +++ b/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt @@ -26,6 +26,7 @@ import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText +import com.google.android.material.tabs.TabLayout import junit.framework.AssertionFailedError import org.hamcrest.Description import org.hamcrest.Matcher @@ -244,6 +245,31 @@ class ListActions { } } + fun selectTabWithText(@IdRes viewId: Int, text: String) { + onView(withId(viewId)) + .perform(object : ViewAction { + override fun getConstraints(): Matcher = isDisplayed() + + override fun getDescription(): String = "Select tab with text: $text" + + override fun perform( + uiController: UiController, + view: View + ) { + val tabLayout = view as TabLayout + for (i in 0 until tabLayout.tabCount) { + val tab = tabLayout.getTabAt(i) + val labelView = tab?.customView?.findViewById(R.id.language_label) + if (labelView?.text == text) { + tab.select() + break + } + } + uiController.loopMainThreadUntilIdle() + } + }) + } + private fun verifyItemAtPosition( recyclerViewId: Int, position: Int, diff --git a/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt index 32cfcf6e2ca..dc532711672 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt @@ -18,7 +18,7 @@ class DialogRobot : BaseRobot() { fun dismissBigEnglishDialog() = apply { try { - click.onDisplayedViewWithIdAnContentDescription(R.id.closeButton, "Close") + click.onDisplayedViewWithIdAndContentDescription(R.id.closeButton, "Close") } catch (e: NoMatchingViewException) { Log.e("DialogRobot", "No Big English Dialog shown.") } catch (e: Exception) { diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/PageRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/PageRobot.kt index 38e4155d249..4068a62e338 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/PageRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/PageRobot.kt @@ -265,11 +265,11 @@ class PageRobot(private val context: Context) : BaseRobot() { } fun openLanguageSelector() = apply { - click.onDisplayedViewWithIdAnContentDescription(R.id.page_language, context.getString(R.string.article_menu_bar_language_button)) + click.onDisplayedViewWithIdAndContentDescription(R.id.page_language, context.getString(R.string.article_menu_bar_language_button)) } fun openFindInArticle() = apply { - click.onDisplayedViewWithIdAnContentDescription(R.id.page_find_in_article, context.getString(R.string.menu_page_find_in_page)) + click.onDisplayedViewWithIdAndContentDescription(R.id.page_find_in_article, context.getString(R.string.menu_page_find_in_page)) } fun verifyFindInArticleCount(count: String) = apply { @@ -281,11 +281,11 @@ class PageRobot(private val context: Context) : BaseRobot() { } fun openThemeSelector() = apply { - click.onDisplayedViewWithIdAnContentDescription(R.id.page_theme, context.getString(R.string.article_menu_bar_theme_button)) + click.onDisplayedViewWithIdAndContentDescription(R.id.page_theme, context.getString(R.string.article_menu_bar_theme_button)) } fun openTableOfContents() = apply { - click.onDisplayedViewWithIdAnContentDescription(R.id.page_contents, context.getString(R.string.article_menu_bar_contents_button)) + click.onDisplayedViewWithIdAndContentDescription(R.id.page_contents, context.getString(R.string.article_menu_bar_contents_button)) delay(TestConfig.DELAY_SHORT) } diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt index ce82cfefa9a..143fd423313 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt @@ -113,7 +113,7 @@ class SearchRobot : BaseRobot() { fun clickLanguage(languageCode: String) = apply { val language = WikipediaApp.instance.languageState.getAppLanguageLocalizedName(languageCode) ?: "" - click.onDisplayedViewWithText(viewId = R.id.language_label, text = language) + list.selectTabWithText(R.id.horizontal_scroll_languages, language) delay(TestConfig.DELAY_MEDIUM) } diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt index 6ebe86dd9b2..447cee77611 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt @@ -84,7 +84,7 @@ class SettingsRobot : BaseRobot() { fun clickDeveloperMode() = apply { // Assert that developer mode is activated - click.onDisplayedViewWithIdAnContentDescription(R.id.developer_settings, "Developer settings") + click.onDisplayedViewWithIdAndContentDescription(R.id.developer_settings, "Developer settings") delay(TestConfig.DELAY_MEDIUM) } diff --git a/app/src/androidTest/java/org/wikipedia/robots/navigation/BottomNavRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/navigation/BottomNavRobot.kt index 8d0ce3e3bc6..73780c8c95e 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/navigation/BottomNavRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/navigation/BottomNavRobot.kt @@ -2,6 +2,8 @@ package org.wikipedia.robots.navigation import BaseRobot import android.util.Log +import androidx.compose.ui.test.isDisplayed +import androidx.compose.ui.test.onNodeWithText import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.matcher.ViewMatchers.isDisplayed @@ -44,13 +46,17 @@ class BottomNavRobot : BaseRobot() { delay(TestConfig.DELAY_MEDIUM) } - fun navigateToSuggestedEdits() = apply { + fun navigateToActivityTab() = apply { onView( allOf( - withId(R.id.nav_tab_edits), withContentDescription(R.string.nav_item_suggested_edits), + withId(R.id.nav_tab_edits), withContentDescription(R.string.nav_item_activity), childAtPosition(childAtPosition(withId(R.id.main_nav_tab_layout), 0), 3), isDisplayed() ) ).perform(click()) + val isOnboardingActivity = composeTestRule.onNodeWithText("Introducing Activity").isDisplayed() + if (isOnboardingActivity) { + pressBack() + } delay(TestConfig.DELAY_LARGE) } diff --git a/app/src/androidTest/java/org/wikipedia/robots/screen/HomeScreenRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/screen/HomeScreenRobot.kt index 15ed67d7912..6cb2d0544b2 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/screen/HomeScreenRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/screen/HomeScreenRobot.kt @@ -23,7 +23,7 @@ class HomeScreenRobot : BaseRobot() { } fun navigateToNotifications() = apply { - click.onDisplayedViewWithIdAnContentDescription(viewId = R.id.menu_notifications, "Notifications") + click.onDisplayedViewWithIdAndContentDescription(viewId = R.id.menu_notifications, "Notifications") delay(TestConfig.DELAY_LARGE) } diff --git a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/NavigationItemTest.kt b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/NavigationItemTest.kt index 8ff40a5248f..dc1222be098 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/NavigationItemTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/NavigationItemTest.kt @@ -25,7 +25,7 @@ class NavigationItemTest : BaseTest( bottomNavRobot .navigateToSavedPage() .navigateToSearchPage() - .navigateToSuggestedEdits() + .navigateToActivityTab() .navigateToMoreMenu() .pressBack() .navigateToExploreFeed() From 074053acbed970b980c4f6e77baeecf69e1464b6 Mon Sep 17 00:00:00 2001 From: williamrai Date: Tue, 2 Dec 2025 16:19:34 -0500 Subject: [PATCH 11/16] - fixes feed screen test --- .../java/org/wikipedia/base/BaseTest.kt | 10 ++ .../robots/feature/ExploreFeedRobot.kt | 120 +++++++----------- .../tests/explorefeed/FeedScreenTest.kt | 62 +++++---- 3 files changed, 90 insertions(+), 102 deletions(-) diff --git a/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt b/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt index 450fa9c8ab9..b415ca3d729 100644 --- a/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt +++ b/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt @@ -102,6 +102,16 @@ abstract class BaseTest( WikipediaApp.instance.languageState.let { it.removeAppLanguageCodes(it.appLanguageCodes.filter { it != "en" }) } + // Disable animations + InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand( + "settings put global window_animation_scale 0" + ) + InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand( + "settings put global transition_animation_scale 0" + ) + InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand( + "settings put global animator_duration_scale 0" + ) } protected fun setDeviceOrientation(isLandscape: Boolean) { diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt index 7a8ca10bfa5..2c019e0b671 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt @@ -7,18 +7,21 @@ import android.widget.TextView import androidx.annotation.IdRes import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.NoMatchingViewException import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition -import androidx.test.espresso.contrib.RecyclerViewActions.scrollTo import androidx.test.espresso.matcher.BoundedMatcher -import androidx.test.espresso.matcher.ViewMatchers.hasDescendant -import androidx.test.espresso.matcher.ViewMatchers.hasSibling import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiScrollable +import androidx.test.uiautomator.UiSelector import com.google.android.material.imageview.ShapeableImageView import org.hamcrest.Description import org.hamcrest.Matcher @@ -35,17 +38,11 @@ import org.wikipedia.theme.Theme class ExploreFeedRobot : BaseRobot() { fun clickOnThisDayCard() = apply { - onView( - allOf( - withId(R.id.on_this_day_page), childAtPosition( - allOf( - withId(R.id.event_layout), - childAtPosition(withId(R.id.on_this_day_card_view_click_container), 0) - ), 3 - ), isDisplayed() - ) - ) - .perform(click()) + onView(allOf( + withId(R.id.on_this_day_card_view_click_container), + isDescendantOfA(withId(R.id.feed_view)), + isDisplayed() + )).perform(scrollTo(), click()) delay(TestConfig.DELAY_MEDIUM) } @@ -58,10 +55,7 @@ class ExploreFeedRobot : BaseRobot() { fun clickRandomArticle() = apply { // Random article card seen and saved to reading lists - scroll.toViewAndMakeVisibleAndClick( - viewId = R.id.view_featured_article_card_content_container, - parentViewId = R.id.feed_view - ) + click.onViewWithId(R.id.articleImage) delay(TestConfig.DELAY_MEDIUM) } @@ -75,28 +69,15 @@ class ExploreFeedRobot : BaseRobot() { } fun clickTopReadArticle() = apply { - try { - onView( - allOf( - withId(R.id.view_list_card_list), - hasSibling( - allOf( - withId(R.id.view_list_card_header), - hasDescendant( - allOf( - withId(R.id.view_card_header_title), - withText("Top read") - ) - ) - ) - ) - )).perform(actionOnItemAtPosition(1, click())) - .perform() - pressBack() - delay(TestConfig.DELAY_MEDIUM) - } catch (e: NoMatchingViewException) { - Log.e("clickError", "") - } + val nestedListMatcher = allOf( + withId(R.id.view_list_card_list), + isDescendantOfA(withId(R.id.feed_view)), + isDisplayed() + ) + onView(nestedListMatcher).check(matches(isDisplayed())) + onView(nestedListMatcher).perform( + RecyclerViewActions.actionOnItemAtPosition(0, click()) + ) } fun clickBecauseYouReadArticle() = apply { @@ -135,20 +116,17 @@ class ExploreFeedRobot : BaseRobot() { } fun clickPictureOfTheDay() = apply { - click.onViewWithId(R.id.view_featured_image_card_content_container) + click.onViewWithId(R.id.view_featured_image_card_image) delay(TestConfig.DELAY_SHORT) } fun clickTodayOnWikipedia() = apply { - click.onViewWithIdAndContainsString(R.id.footerActionButton, text = "View main page") + click.onViewWithIdAndContainsString(R.id.footerActionButton, text = "Today on Wikipedia") delay(TestConfig.DELAY_LARGE) } fun clickOnFeaturedArticle() = apply { - scroll.toViewAndMakeVisibleAndClick( - viewId = R.id.view_featured_article_card_content_container, - parentViewId = R.id.feed_view - ) + click.onViewWithId(R.id.articleImage) delay(TestConfig.DELAY_MEDIUM) } @@ -170,20 +148,6 @@ class ExploreFeedRobot : BaseRobot() { } } - fun scrollToCardWithTitle(title: String, @IdRes viewId: Int = R.id.view_card_header_title) = - apply { - onView(withId(R.id.feed_view)) - .perform( - scrollTo( - hasDescendant( - scrollToCardViewWithTitle(title, viewId) - ) - ) - ) - .perform() - delay(TestConfig.DELAY_MEDIUM) - } - fun swipeToRefresh() = apply { onView(withId(R.id.swipe_refresh_layout)) .perform(ViewActions.swipeDown()) @@ -191,23 +155,31 @@ class ExploreFeedRobot : BaseRobot() { } fun scrollToAndPerform( - recyclerViewId: Int = R.id.feed_view, title: String, - textViewId: Int = R.id.view_card_header_title, - verticalOffset: Int = 200, + shouldSwipeMore: Boolean = false, action: (ExploreFeedRobot.() -> Unit)? = null ) = apply { - if (list.isItemPresent(title = title)) { - list.scrollToRecyclerView( - recyclerViewId, - title, - textViewId, - verticalOffset - ) - action?.invoke(this@ExploreFeedRobot) - } else { - Log.i("ExploreFeed", "$title not present today - skipping test") + val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + + val appScrollable = UiScrollable(UiSelector().scrollable(true)) + appScrollable.setMaxSearchSwipes(10) + try { + appScrollable.setAsVerticalList() + appScrollable.scrollIntoView(UiSelector().text(title)) + if (shouldSwipeMore) { + device.swipe( + device.displayWidth / 2, + device.displayHeight * 3 / 5, + device.displayWidth / 2, + device.displayHeight * 2 / 5, + 15 + ) + } + } catch (e: Exception) { + Log.e("ExploreFeed", "Scroll attempt failed: ${e.message}") } + Thread.sleep(500) + action?.invoke(this@ExploreFeedRobot) } fun assertFeaturedArticleTitleColor(theme: Theme) = apply { diff --git a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt index a99770831d1..65f979f0cf0 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt @@ -42,38 +42,44 @@ class FeedScreenTest : BaseTest( // Feed Test flow exploreFeedRobot - .scrollToAndPerform(title = FEATURED_ARTICLE) - .assertFeaturedArticleTitleColor(theme = Theme.LIGHT) - .clickOnFeaturedArticle() - .pressBack() - .scrollToAndPerform(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE, verticalOffset = -100) - .clickTodayOnWikipedia() - dialogRobot - .dismissBigEnglishDialog() - .dismissContributionDialog() - exploreFeedRobot - .pressBack() + .scrollToAndPerform(title = FEATURED_ARTICLE, shouldSwipeMore = true) { + assertFeaturedArticleTitleColor(theme = Theme.LIGHT) + clickOnFeaturedArticle() + pressBack() + } + .scrollToAndPerform(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE) { + clickTodayOnWikipedia() + dialogRobot + .dismissBigEnglishDialog() + .dismissContributionDialog() + pressBack() + } systemRobot .enableDarkMode(context) exploreFeedRobot - .scrollToAndPerform(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE, verticalOffset = 400) - .scrollToAndPerform(title = TOP_READ_ARTICLES, verticalOffset = 400) - .assertTopReadTitleColor(theme = Theme.DARK) - .clickTopReadArticle() - .scrollToAndPerform(title = PICTURE_OF_DAY) - .clickPictureOfTheDay() - .pressBack() + .scrollToAndPerform(title = TOP_READ_ARTICLES) { + assertTopReadTitleColor(theme = Theme.DARK) + clickTopReadArticle() + pressBack() + } + .scrollToAndPerform(title = PICTURE_OF_DAY) { + clickPictureOfTheDay() + pressBack() + } systemRobot - .enableDarkMode(context) + .disableDarkMode(context) exploreFeedRobot - .scrollToAndPerform(title = NEWS_CARD) - .clickNewsArticle() - .pressBack() - .scrollToAndPerform(title = ON_THIS_DAY_CARD) - .clickOnThisDayCard() - .pressBack() - .scrollToAndPerform(title = RANDOM_CARD) - .clickRandomArticle() - .pressBack() + .scrollToAndPerform(title = NEWS_CARD) { + clickNewsArticle() + pressBack() + } + .scrollToAndPerform(title = ON_THIS_DAY_CARD, shouldSwipeMore = true) { + clickOnThisDayCard() + pressBack() + } + .scrollToAndPerform(title = RANDOM_CARD, shouldSwipeMore = true) { + clickRandomArticle() + pressBack() + } } } From f3d48447628585c2fe337240c2a9bf31e8268581 Mon Sep 17 00:00:00 2001 From: williamrai Date: Wed, 3 Dec 2025 14:18:17 -0500 Subject: [PATCH 12/16] - fixes feed screen espresso test issues - increases smoke test time out to 45 min - removes unused code --- .github/workflows/android_smoke_test.yml | 2 +- .../org/wikipedia/base/actions/ListActions.kt | 228 +++++++++++------- .../robots/feature/ExploreFeedRobot.kt | 216 +++++++++-------- .../tests/explorefeed/FeedScreenTest.kt | 28 +-- .../org/wikipedia/testsuites/TestSuite.kt | 1 - 5 files changed, 270 insertions(+), 205 deletions(-) diff --git a/.github/workflows/android_smoke_test.yml b/.github/workflows/android_smoke_test.yml index fb6fe2f5620..c984ed88307 100644 --- a/.github/workflows/android_smoke_test.yml +++ b/.github/workflows/android_smoke_test.yml @@ -8,7 +8,7 @@ env: jobs: instrumentation-tests: runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 45 strategy: fail-fast: false matrix: diff --git a/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt b/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt index e3e11c6bd3d..e477d77e450 100644 --- a/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt +++ b/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt @@ -1,5 +1,6 @@ package org.wikipedia.base.actions +import android.util.Log import android.view.View import android.widget.HorizontalScrollView import android.widget.ListView @@ -10,11 +11,9 @@ import androidx.core.widget.NestedScrollView import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onData import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.NoMatchingViewException import androidx.test.espresso.UiController import androidx.test.espresso.ViewAction import androidx.test.espresso.ViewAssertion -import androidx.test.espresso.ViewInteraction import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.longClick @@ -23,19 +22,22 @@ import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.matcher.BoundedMatcher import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.hasDescendant +import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import com.google.android.material.tabs.TabLayout -import junit.framework.AssertionFailedError import org.hamcrest.Description import org.hamcrest.Matcher import org.hamcrest.Matchers import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.anything +import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.not import org.junit.Assert.assertEquals import org.wikipedia.R +import org.wikipedia.views.DefaultViewHolder +import java.util.concurrent.atomic.AtomicInteger class ListActions { fun clickOnListView(@IdRes viewId: Int, @IdRes childView: Int, position: Int) = apply { @@ -56,12 +58,7 @@ class ListActions { ) } - fun clickOnItemInList(textViewId: Int) { - onView(withId(textViewId)) - .perform(click()) - } - - fun longClickOnItemInList(@IdRes listId: Int, position: Int) { + fun longClickOnItemInList(@IdRes listId: Int, position: Int) { onView(withId(listId)) .perform( RecyclerViewActions.actionOnItemAtPosition( @@ -81,13 +78,6 @@ class ListActions { ) } - fun scrollToPositionInRecyclerView(@IdRes viewId: Int, position: Int) { - onView(withId(viewId)) - .perform( - RecyclerViewActions.scrollToPosition(position) - ) - } - fun clickOnSpecificItemInList(@IdRes listId: Int, @IdRes itemId: Int, position: Int) { onView(withId(listId)) .perform( @@ -173,23 +163,6 @@ class ListActions { .check(hasItemCount(expectedCount)) } - fun verifyItemDoesNotExistAtPosition( - recyclerViewId: Int, - itemId: Int - ) { - onView(withId(recyclerViewId)) - .check( - matches( - hasDescendant( - allOf( - withId(itemId), - not(isDisplayed()) - ) - ) - ) - ) - } - fun verifyItemDoesNotExistWithText( recyclerViewId: Int, text: String @@ -226,25 +199,6 @@ class ListActions { ) } - fun isItemPresent( - recyclerViewId: Int = R.id.feed_view, - title: String, - textViewId: Int = R.id.view_card_header_title - ): Boolean { - return try { - onView(withId(recyclerViewId)) - .check(matches(hasDescendant(allOf( - withId(textViewId), - withText(title) - )))) - true - } catch (_: NoMatchingViewException) { - false - } catch (_: AssertionFailedError) { - false - } - } - fun selectTabWithText(@IdRes viewId: Int, text: String) { onView(withId(viewId)) .perform(object : ViewAction { @@ -270,15 +224,6 @@ class ListActions { }) } - private fun verifyItemAtPosition( - recyclerViewId: Int, - position: Int, - itemMatcher: Matcher - ): ViewInteraction { - return onView(withId(recyclerViewId)) - .check(matches(atPosition(position, itemMatcher))) - } - private fun clickChildViewWithId(@IdRes id: Int) = object : ViewAction { override fun getConstraints() = null @@ -301,41 +246,150 @@ class ListActions { } } - private fun atPosition( - position: Int, - itemMatcher: Matcher - ): BoundedMatcher { - return object : BoundedMatcher(RecyclerView::class.java) { + fun moveClickIntoViewAndClick(childId: Int): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher = isAssignableFrom(View::class.java) + override fun getDescription(): String = "click child view with id $childId" + override fun perform(uiController: UiController, view: View) { + val child = view.findViewById(childId) + + // Find the parent RecyclerView + var parent = view.parent + while (parent != null && parent !is RecyclerView) { + parent = parent.parent + } + + if (parent is RecyclerView) { + // Calculate scroll distance to bring child into view + val childLocation = IntArray(2) + child.getLocationOnScreen(childLocation) + val recyclerLocation = IntArray(2) + parent.getLocationOnScreen(recyclerLocation) + + val scrollY = + childLocation[1] - recyclerLocation[1] - 100 // -100 for some padding/toolbar + + if (scrollY != 0) { + parent.smoothScrollBy(0, scrollY) + uiController.loopMainThreadForAtLeast(500) + } + } else { + // Fallback if no RecyclerView found (unlikely in this context) + child.requestRectangleOnScreen( + android.graphics.Rect( + 0, + 0, + child.width, + child.height + ), true + ) + uiController.loopMainThreadForAtLeast(300) + } + child.performClick() + } + } + } + + fun clickNestedItem(nestedRecyclerViewId: Int, position: Int): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher? = isAssignableFrom(View::class.java) + + override fun getDescription(): String? = + "click item at position $position in nested RecyclerView with id $nestedRecyclerViewId" + + override fun perform( + uiController: UiController, + view: View + ) { + val nestedRecyclerView = + view.findViewById(nestedRecyclerViewId) ?: throw RuntimeException( + "Could not find nested RecyclerView with id $nestedRecyclerViewId" + ) + + nestedRecyclerView.scrollToPosition(position) + uiController.loopMainThreadForAtLeast(500) + + val holder = nestedRecyclerView.findViewHolderForAdapterPosition(position) + ?: throw RuntimeException("Could not find ViewHolder at position $position in nested list") + + holder.itemView.performClick() + } + } + } + + fun scrollAndPerform( + viewIdRes: Int = R.id.feed_view, + title: String, + action: (Int) -> Unit = {} + ) { + val matcher = withCardTitle(title) + val position = getPosition(viewIdRes, matcher) + if (position != -1) { + onView(withId(viewIdRes)) + .perform(scrollToPosition(position)) + action(position) + } else { + Log.e("ExploreFeedRobot: ", "Skipping scroll for $title - item not found in adapter.") + } + } + + private fun scrollToPosition(position: Int): ViewAction { + return object : ViewAction { + override fun getConstraints() = isAssignableFrom(RecyclerView::class.java) + override fun getDescription() = "scroll to position $position" + override fun perform(uiController: UiController, view: View) { + (view as RecyclerView).scrollToPosition(position) + uiController.loopMainThreadForAtLeast(500) + } + } + } + + private fun withCardTitle(title: String): Matcher { + return object : BoundedMatcher>( + DefaultViewHolder::class.java + ) { override fun describeTo(description: Description) { - description.appendText("has item at position $position: ") - itemMatcher.describeTo(description) + description.appendText("ViewHolder with title: $title") } - override fun matchesSafely(recyclerView: RecyclerView): Boolean { - val viewHolder = recyclerView.findViewHolderForAdapterPosition(position) - ?: return false - return itemMatcher.matches(viewHolder.itemView) + override fun matchesSafely(item: DefaultViewHolder<*>?): Boolean { + val view = item?.view ?: return false + val matcher = hasDescendant( + allOf( + withId(R.id.view_card_header_title), + withText(containsString(title)) + ) + ) + return matcher.matches(view) } } } - private fun findItemPosition(recyclerViewId: Int, matcher: Matcher): Int { - var foundPosition = -1 + private fun findViewHolderPosition( + recyclerView: RecyclerView, + matcher: Matcher + ): Int { + val adapter = recyclerView.adapter ?: return -1 + for (i in 0 until adapter.itemCount) { + val holder = adapter.createViewHolder(recyclerView, adapter.getItemViewType(i)) + adapter.bindViewHolder(holder, i) + if (matcher.matches(holder)) { + return i + } + } + return -1 + } - onView(withId(recyclerViewId)) - .check { view, _ -> - val recyclerView = view as RecyclerView - val itemCount = recyclerView.adapter?.itemCount ?: 0 - - for (position in 0 until itemCount) { - val viewHolder = recyclerView.findViewHolderForAdapterPosition(position) - if (viewHolder != null && matcher.matches(viewHolder.itemView)) { - foundPosition = position - break - } - } + private fun getPosition(viewId: Int, matcher: Matcher): Int { + val position = AtomicInteger(-1) + onView(withId(viewId)).perform(object : ViewAction { + override fun getConstraints() = isAssignableFrom(RecyclerView::class.java) + override fun getDescription() = "get position" + override fun perform(uiController: UiController, view: View) { + position.set(findViewHolderPosition(view as RecyclerView, matcher)) } - return foundPosition + }) + return position.get() } } diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt index 2c019e0b671..e11f17e6cf8 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt @@ -2,18 +2,12 @@ package org.wikipedia.robots.feature import BaseRobot import android.util.Log -import android.view.View -import android.widget.TextView -import androidx.annotation.IdRes import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.action.ViewActions.scrollTo -import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition -import androidx.test.espresso.matcher.BoundedMatcher import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId @@ -23,8 +17,6 @@ import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiSelector import com.google.android.material.imageview.ShapeableImageView -import org.hamcrest.Description -import org.hamcrest.Matcher import org.hamcrest.Matchers.allOf import org.wikipedia.R import org.wikipedia.TestConstants @@ -37,12 +29,80 @@ import org.wikipedia.base.utils.ColorAssertions import org.wikipedia.theme.Theme class ExploreFeedRobot : BaseRobot() { - fun clickOnThisDayCard() = apply { - onView(allOf( - withId(R.id.on_this_day_card_view_click_container), - isDescendantOfA(withId(R.id.feed_view)), - isDisplayed() - )).perform(scrollTo(), click()) + fun clickOnFeaturedArticle(position: Int = 0) = apply { + onView(withId(R.id.feed_view)) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + position, + list.moveClickIntoViewAndClick(R.id.view_featured_article_card_content_container) + ) + ) + + delay(TestConfig.DELAY_MEDIUM) + } + + fun clickTodayOnWikipedia(position: Int) = apply { + onView(withId(R.id.feed_view)) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + position, + list.moveClickIntoViewAndClick(R.id.footerActionButton) + ) + ) + delay(TestConfig.DELAY_LARGE) + } + + fun clickTopReadArticle(position: Int) = apply { + onView(withId(R.id.feed_view)) + .perform( + actionOnItemAtPosition( + position, + list.clickNestedItem(R.id.view_list_card_list, 0) + ) + ) + } + + fun clickPictureOfTheDay(position: Int) = apply { + onView(withId(R.id.feed_view)) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + position, + list.moveClickIntoViewAndClick(R.id.view_featured_image_card_content_container) + ) + ) + delay(TestConfig.DELAY_SHORT) + } + + fun clickNewsArticle(position: Int) = apply { + onView(withId(R.id.feed_view)) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + position, + list.clickNestedItem(R.id.news_cardview_recycler_view, 0) + ) + ) + delay(TestConfig.DELAY_MEDIUM) + } + + fun clickOnThisDayCard(position: Int) = apply { + onView(withId(R.id.feed_view)) + .perform( + actionOnItemAtPosition( + position, + list.moveClickIntoViewAndClick(R.id.on_this_day_card_view_click_container) + ) + ) + delay(TestConfig.DELAY_MEDIUM) + } + + fun clickRandomArticle(position: Int) = apply { + onView(withId(R.id.feed_view)) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + position, + list.moveClickIntoViewAndClick(R.id.view_featured_article_card_content_container) + ) + ) delay(TestConfig.DELAY_MEDIUM) } @@ -53,12 +113,6 @@ class ExploreFeedRobot : BaseRobot() { ) } - fun clickRandomArticle() = apply { - // Random article card seen and saved to reading lists - click.onViewWithId(R.id.articleImage) - delay(TestConfig.DELAY_MEDIUM) - } - fun pressBack() = apply { goBack() delay(TestConfig.DELAY_LARGE) @@ -68,18 +122,6 @@ class ExploreFeedRobot : BaseRobot() { click.onDisplayedViewWithContentDescription("Navigate up") } - fun clickTopReadArticle() = apply { - val nestedListMatcher = allOf( - withId(R.id.view_list_card_list), - isDescendantOfA(withId(R.id.feed_view)), - isDisplayed() - ) - onView(nestedListMatcher).check(matches(isDisplayed())) - onView(nestedListMatcher).perform( - RecyclerViewActions.actionOnItemAtPosition(0, click()) - ) - } - fun clickBecauseYouReadArticle() = apply { onView( allOf( @@ -90,17 +132,6 @@ class ExploreFeedRobot : BaseRobot() { .perform(actionOnItemAtPosition(0, click())) } - fun clickNewsArticle() = apply { - onView( - allOf( - withId(R.id.news_cardview_recycler_view), - childAtPosition(withId(R.id.rtl_container), 1) - ) - ) - .perform(actionOnItemAtPosition(0, click())) - delay(TestConfig.DELAY_MEDIUM) - } - fun clickAddArticleDescription() = apply { click.onDisplayedViewWithContentDescription(description = "Add article descriptions") } @@ -115,21 +146,6 @@ class ExploreFeedRobot : BaseRobot() { delay(TestConfig.DELAY_MEDIUM) } - fun clickPictureOfTheDay() = apply { - click.onViewWithId(R.id.view_featured_image_card_image) - delay(TestConfig.DELAY_SHORT) - } - - fun clickTodayOnWikipedia() = apply { - click.onViewWithIdAndContainsString(R.id.footerActionButton, text = "Today on Wikipedia") - delay(TestConfig.DELAY_LARGE) - } - - fun clickOnFeaturedArticle() = apply { - click.onViewWithId(R.id.articleImage) - delay(TestConfig.DELAY_MEDIUM) - } - fun stayOnFeaturedArticleFor(milliseconds: Long) = apply { scroll.toViewAndMakeVisibleAndClick( viewId = R.id.view_featured_article_card_content_container, @@ -184,22 +200,29 @@ class ExploreFeedRobot : BaseRobot() { fun assertFeaturedArticleTitleColor(theme: Theme) = apply { val color = TestWikipediaColors.getGetColor(theme, colorType = TestThemeColorType.PRIMARY) - onView(allOf( - withId(R.id.view_card_header_title), - withText(TestConstants.FEATURED_ARTICLE) - )).check(ColorAssertions.hasColor(color, ColorAssertions.ColorType.TextColor)) + onView( + allOf( + withId(R.id.view_card_header_title), + withText(TestConstants.FEATURED_ARTICLE) + ) + ).check(ColorAssertions.hasColor(color, ColorAssertions.ColorType.TextColor)) } fun assertTopReadTitleColor(theme: Theme) = apply { val color = TestWikipediaColors.getGetColor(theme, colorType = TestThemeColorType.PRIMARY) - onView(allOf( - withId(R.id.view_card_header_title), - withText(TestConstants.TOP_READ_ARTICLES) - )).check(ColorAssertions.hasColor(color, ColorAssertions.ColorType.TextColor)) + onView( + allOf( + withId(R.id.view_card_header_title), + withText(TestConstants.TOP_READ_ARTICLES) + ) + ).check(ColorAssertions.hasColor(color, ColorAssertions.ColorType.TextColor)) } fun longClickFeaturedArticleCardContainer() = apply { - scroll.toViewAndMakeVisibleAndLongClick(viewId = R.id.view_featured_article_card_content_container, parentViewId = R.id.feed_view) + scroll.toViewAndMakeVisibleAndLongClick( + viewId = R.id.view_featured_article_card_content_container, + parentViewId = R.id.feed_view + ) delay(TestConfig.DELAY_SHORT) } @@ -212,47 +235,36 @@ class ExploreFeedRobot : BaseRobot() { } } - private fun scrollToCardViewWithTitle( + fun scrollAndPerform( + viewIdRes: Int = R.id.feed_view, title: String, - @IdRes textViewId: Int = R.id.view_card_header_title, - ): Matcher { - var currentOccurrence = 0 - return object : BoundedMatcher(View::class.java) { - override fun describeTo(description: Description?) { - description?.appendText("Scroll to Card View with title: $title") - } - - override fun matchesSafely(item: View?): Boolean { - val titleView = item?.findViewById(textViewId) - if (titleView?.text?.toString() == title) { - if (currentOccurrence == 0) { - currentOccurrence++ - return true - } - currentOccurrence++ - } - return false - } + action: ExploreFeedRobot.(Int) -> Unit = {} + ) = apply { + list.scrollAndPerform(viewIdRes, title) { position -> + action(position) } } fun verifyTopReadArticleIsGreyedOut(theme: Theme) = apply { delay(TestConfig.DELAY_MEDIUM) - onView(allOf( - withId(R.id.view_list_card_list), - isDescendantOfA(withId(R.id.feed_view)), - isDisplayed() - )).check { view, _ -> - val recyclerView = view as RecyclerView - val viewHolder = recyclerView.findViewHolderForAdapterPosition(1) - ?: throw AssertionError("No viewHolder found at position 0") - val imageView = viewHolder.itemView.findViewById(R.id.view_list_card_item_image) + onView( + allOf( + withId(R.id.view_list_card_list), + isDescendantOfA(withId(R.id.feed_view)), + isDisplayed() + ) + ).check { view, _ -> + val recyclerView = view as RecyclerView + val viewHolder = recyclerView.findViewHolderForAdapterPosition(1) + ?: throw AssertionError("No viewHolder found at position 0") + val imageView = + viewHolder.itemView.findViewById(R.id.view_list_card_item_image) ?: throw AssertionError("No ImageView found with id view_list_card_item_image") - val color = TestWikipediaColors.getGetColor(theme, TestThemeColorType.BORDER) - ColorAssertions.hasColor( - colorResId = color, - colorType = ColorAssertions.ColorType.ShapeableImageViewColor - ).check(imageView, null) - } + val color = TestWikipediaColors.getGetColor(theme, TestThemeColorType.BORDER) + ColorAssertions.hasColor( + colorResId = color, + colorType = ColorAssertions.ColorType.ShapeableImageViewColor + ).check(imageView, null) + } } } diff --git a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt index 65f979f0cf0..b153d1b1726 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/FeedScreenTest.kt @@ -42,13 +42,13 @@ class FeedScreenTest : BaseTest( // Feed Test flow exploreFeedRobot - .scrollToAndPerform(title = FEATURED_ARTICLE, shouldSwipeMore = true) { + .scrollAndPerform(title = FEATURED_ARTICLE) { position -> assertFeaturedArticleTitleColor(theme = Theme.LIGHT) - clickOnFeaturedArticle() + clickOnFeaturedArticle(position) pressBack() } - .scrollToAndPerform(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE) { - clickTodayOnWikipedia() + .scrollAndPerform(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE) { position -> + clickTodayOnWikipedia(position) dialogRobot .dismissBigEnglishDialog() .dismissContributionDialog() @@ -57,28 +57,28 @@ class FeedScreenTest : BaseTest( systemRobot .enableDarkMode(context) exploreFeedRobot - .scrollToAndPerform(title = TOP_READ_ARTICLES) { + .scrollAndPerform(title = TOP_READ_ARTICLES) { position -> assertTopReadTitleColor(theme = Theme.DARK) - clickTopReadArticle() + clickTopReadArticle(position) pressBack() } - .scrollToAndPerform(title = PICTURE_OF_DAY) { - clickPictureOfTheDay() + .scrollAndPerform(title = PICTURE_OF_DAY) { position -> + clickPictureOfTheDay(position) pressBack() } systemRobot .disableDarkMode(context) exploreFeedRobot - .scrollToAndPerform(title = NEWS_CARD) { - clickNewsArticle() + .scrollAndPerform(title = NEWS_CARD) { position -> + clickNewsArticle(position) pressBack() } - .scrollToAndPerform(title = ON_THIS_DAY_CARD, shouldSwipeMore = true) { - clickOnThisDayCard() + .scrollAndPerform(title = ON_THIS_DAY_CARD) { position -> + clickOnThisDayCard(position) pressBack() } - .scrollToAndPerform(title = RANDOM_CARD, shouldSwipeMore = true) { - clickRandomArticle() + .scrollAndPerform(title = RANDOM_CARD) { position -> + clickRandomArticle(position) pressBack() } } diff --git a/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt b/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt index c6ceee74ae7..7c96fd15310 100644 --- a/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt +++ b/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt @@ -16,7 +16,6 @@ import org.wikipedia.tests.SuggestedEditScreenTest SettingsTestSuite::class, ExploreFeedTestSuite::class, SearchTest::class, - SuggestedEditScreenTest::class, DeepLinkingTest::class, ArticlesTestSuite::class ) From fb0caacc07d4c38ebd0772dce9a530b5615e72b5 Mon Sep 17 00:00:00 2001 From: williamrai Date: Wed, 3 Dec 2025 16:32:19 -0500 Subject: [PATCH 13/16] - espresso fixes --- .../java/org/wikipedia/tests/articles/EditIconTest.kt | 2 +- .../androidTest/java/org/wikipedia/tests/articles/MediaTest.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/androidTest/java/org/wikipedia/tests/articles/EditIconTest.kt b/app/src/androidTest/java/org/wikipedia/tests/articles/EditIconTest.kt index b8b05ae0e67..1cdb87b904e 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/articles/EditIconTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/articles/EditIconTest.kt @@ -38,7 +38,7 @@ class EditIconTest : BaseTest( searchRobot .tapSearchView() assertEditIconProtection(SEARCH_TERM, isProtected = false) - assertEditIconProtection(SEARCH_TERM_AVATAR, action = { + assertEditIconProtection(SEARCH_TERM_AVATAR, isProtected = false, action = { dialogRobot .dismissBigEnglishDialog() .dismissContributionDialog() diff --git a/app/src/androidTest/java/org/wikipedia/tests/articles/MediaTest.kt b/app/src/androidTest/java/org/wikipedia/tests/articles/MediaTest.kt index 83fbe5e9857..27f68c27486 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/articles/MediaTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/articles/MediaTest.kt @@ -36,6 +36,7 @@ class MediaTest : BaseTest( .clickLeadImage() .swipePagerLeft() .swipePagerLeft() + .swipePagerLeft() mediaRobot .doubleTapToZoomOut() .doubleTapToZoomOut() From 0a2b75a1511d300105d306be556e2aba306668cf Mon Sep 17 00:00:00 2001 From: williamrai Date: Thu, 4 Dec 2025 14:18:03 -0500 Subject: [PATCH 14/16] - espresso fixes --- .../robots/feature/ExploreFeedRobot.kt | 23 +++++++++---------- .../wikipedia/robots/feature/SearchRobot.kt | 5 ++++ .../robots/screen/SavedScreenRobot.kt | 6 +++++ .../wikipedia/tests/OfflinePageLoadTest.kt | 14 ++++++----- .../java/org/wikipedia/tests/SearchTest.kt | 6 ++--- .../tests/articles/ArticleTabTest.kt | 10 ++++---- .../tests/articles/SavedArticleTest.kt | 19 +++++++-------- .../tests/explorefeed/BecauseYouReadTest.kt | 9 ++++---- .../wikipedia/tests/settings/ShowImageTest.kt | 4 ++-- 9 files changed, 54 insertions(+), 42 deletions(-) diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt index e11f17e6cf8..7f1a6ed49a4 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt @@ -5,7 +5,6 @@ import android.util.Log import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions -import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA @@ -21,7 +20,6 @@ import org.hamcrest.Matchers.allOf import org.wikipedia.R import org.wikipedia.TestConstants import org.wikipedia.TestConstants.SUGGESTED_EDITS -import org.wikipedia.TestUtil.childAtPosition import org.wikipedia.base.TestConfig import org.wikipedia.base.TestThemeColorType import org.wikipedia.base.TestWikipediaColors @@ -106,6 +104,17 @@ class ExploreFeedRobot : BaseRobot() { delay(TestConfig.DELAY_MEDIUM) } + fun clickBecauseYouReadArticle(position: Int) = apply { + onView(withId(R.id.feed_view)) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + position, + list.clickNestedItem(R.id.view_list_card_list, 0) + ) + ) + delay(TestConfig.DELAY_MEDIUM) + } + fun verifyFeedViewSize(expectedCount: Int) = apply { list.verifyRecyclerViewItemCount( viewId = R.id.feed_view, @@ -122,16 +131,6 @@ class ExploreFeedRobot : BaseRobot() { click.onDisplayedViewWithContentDescription("Navigate up") } - fun clickBecauseYouReadArticle() = apply { - onView( - allOf( - withId(R.id.view_list_card_list), - childAtPosition(withId(R.id.view_list_card_list_container), 0) - ) - ) - .perform(actionOnItemAtPosition(0, click())) - } - fun clickAddArticleDescription() = apply { click.onDisplayedViewWithContentDescription(description = "Add article descriptions") } diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt index 143fd423313..e0d6908b2fd 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt @@ -76,6 +76,11 @@ class SearchRobot : BaseRobot() { delay(TestConfig.DELAY_MEDIUM) } + fun closeFilterList() = apply { + onView(withId(androidx.appcompat.R.id.action_mode_close_button)).perform(click()) + delay(TestConfig.DELAY_SHORT) + } + fun removeTextByTappingTrashIcon() = apply { onView(withId(androidx.appcompat.R.id.search_close_btn)) .check(matches(isDisplayed())) diff --git a/app/src/androidTest/java/org/wikipedia/robots/screen/SavedScreenRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/screen/SavedScreenRobot.kt index 4223a8e641e..0ba91fb87dd 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/screen/SavedScreenRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/screen/SavedScreenRobot.kt @@ -6,6 +6,7 @@ import android.util.Log import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions @@ -102,4 +103,9 @@ class SavedScreenRobot : BaseRobot() { goBack() delay(TestConfig.DELAY_SHORT) } + + fun closeFilterList() = apply { + onView(withId(androidx.appcompat.R.id.action_mode_close_button)).perform(click()) + delay(TestConfig.DELAY_SHORT) + } } diff --git a/app/src/androidTest/java/org/wikipedia/tests/OfflinePageLoadTest.kt b/app/src/androidTest/java/org/wikipedia/tests/OfflinePageLoadTest.kt index ba87c0768e4..adc10528382 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/OfflinePageLoadTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/OfflinePageLoadTest.kt @@ -19,15 +19,17 @@ class OfflinePageLoadTest : BaseTest( systemRobot .clickOnSystemDialogWithText("Allow") exploreFeedRobot - .scrollToAndPerform(title = FEATURED_ARTICLE) - .clickOnFeaturedArticle() - .pressBack() + .scrollAndPerform(title = FEATURED_ARTICLE) { position -> + clickOnFeaturedArticle(position) + pressBack() + } systemRobot .turnOffInternet() exploreFeedRobot - .scrollToAndPerform(title = FEATURED_ARTICLE) - .clickOnFeaturedArticle() - .pressBack() + .scrollAndPerform(title = FEATURED_ARTICLE) { position -> + clickOnFeaturedArticle(position) + pressBack() + } systemRobot .turnOnInternet() } diff --git a/app/src/androidTest/java/org/wikipedia/tests/SearchTest.kt b/app/src/androidTest/java/org/wikipedia/tests/SearchTest.kt index 89924ea9d35..3ef57367b57 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/SearchTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/SearchTest.kt @@ -59,14 +59,14 @@ class SearchTest : BaseTest( .dismissBigEnglishDialog() .dismissContributionDialog() searchRobot - .backToHistoryScreen() + .pressBack() + .navigateUp() .verifyHistoryArticle(ARTICLE_TITLE_WORLD_CUP) .clickFilterHistoryButton() .typeTextInView(SEARCH_TERM2) .verifyHistoryArticle(ARTICLE_TITLE_WORLD_CUP) .assertColorOfTitleInTheHistoryList(position = 1, theme = Theme.LIGHT) - .pressBack() - .pressBack() + .closeFilterList() .clickOnItemFromHistoryList(2) .pressBack() .longClickOnItemFromHistoryList(2) diff --git a/app/src/androidTest/java/org/wikipedia/tests/articles/ArticleTabTest.kt b/app/src/androidTest/java/org/wikipedia/tests/articles/ArticleTabTest.kt index f5116c47f33..522b686f822 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/articles/ArticleTabTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/articles/ArticleTabTest.kt @@ -35,13 +35,15 @@ class ArticleTabTest : BaseTest( .clickOnSystemDialogWithText("Allow") .disableDarkMode(context) exploreFeedRobot - .scrollToAndPerform(title = TestConstants.FEATURED_ARTICLE) - .clickOnFeaturedArticle() + .scrollAndPerform(title = TestConstants.FEATURED_ARTICLE) { position -> + clickOnFeaturedArticle(position) + } pageRobot .navigateUp() exploreFeedRobot - .scrollToAndPerform(title = TestConstants.FEATURED_ARTICLE) - .clickOnFeaturedArticle() + .scrollAndPerform(title = TestConstants.FEATURED_ARTICLE) { position -> + clickOnFeaturedArticle(position) + } dialogRobot .dismissBigEnglishDialog() .dismissContributionDialog() diff --git a/app/src/androidTest/java/org/wikipedia/tests/articles/SavedArticleTest.kt b/app/src/androidTest/java/org/wikipedia/tests/articles/SavedArticleTest.kt index b06177d34f8..35a0b7f90a7 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/articles/SavedArticleTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/articles/SavedArticleTest.kt @@ -37,19 +37,17 @@ class SavedArticleTest : BaseTest( .clickSave(action = { isSaved -> if (isSaved) { searchRobot - .pressBack() - .pressBack() + .pressBackUntilExploreFeed() } else { searchRobot - .pressBack() - .pressBack() - .pressBack() + .pressBackUntilExploreFeed() } }) exploreFeedRobot - .scrollToAndPerform(title = FEATURED_ARTICLE) - .longClickFeaturedArticleCardContainer() - .clickSave() + .scrollAndPerform(title = FEATURED_ARTICLE) { + longClickFeaturedArticleCardContainer() + clickSave() + } setDeviceOrientation(isLandscape = true) bottomNavRobot .navigateToSavedPage() @@ -57,7 +55,7 @@ class SavedArticleTest : BaseTest( .clickFilterList() searchRobot .typeTextInView(TestConstants.SEARCH_TERM) - .pressBack() + savedScreenRobot .pressBack() setDeviceOrientation(isLandscape = false) savedScreenRobot @@ -69,8 +67,7 @@ class SavedArticleTest : BaseTest( savedScreenRobot .clickItemOnReadingList(0) .pressBack() - .pressBack() - .pressBack() + .closeFilterList() .swipeToDelete(2) .verifySavedArticleIsRemoved(TestConstants.SEARCH_TERM) } diff --git a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/BecauseYouReadTest.kt b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/BecauseYouReadTest.kt index f70a1c07c16..8c90a706fac 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/explorefeed/BecauseYouReadTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/explorefeed/BecauseYouReadTest.kt @@ -32,12 +32,13 @@ class BecauseYouReadTest : BaseTest( // Because you read, requires users to read some article for 30 seconds exploreFeedRobot - .scrollToAndPerform(title = FEATURED_ARTICLE) + .scrollAndPerform(title = FEATURED_ARTICLE) .stayOnFeaturedArticleFor(milliseconds = 30000) .pressBack() .swipeToRefresh() - .scrollToAndPerform(title = BECAUSE_YOU_READ) - .clickBecauseYouReadArticle() - .pressBack() + .scrollAndPerform(title = BECAUSE_YOU_READ) { position -> + clickBecauseYouReadArticle(position) + pressBack() + } } } diff --git a/app/src/androidTest/java/org/wikipedia/tests/settings/ShowImageTest.kt b/app/src/androidTest/java/org/wikipedia/tests/settings/ShowImageTest.kt index 9509bdf322b..519beee79d0 100644 --- a/app/src/androidTest/java/org/wikipedia/tests/settings/ShowImageTest.kt +++ b/app/src/androidTest/java/org/wikipedia/tests/settings/ShowImageTest.kt @@ -38,10 +38,10 @@ class ShowImageTest : BaseTest( .toggleShowImages() exploreFeedRobot .pressBack() - .scrollToAndPerform(title = "Featured article") { + .scrollAndPerform(title = "Featured article") { verifyFeaturedArticleImageIsNotVisible() } - .scrollToAndPerform(title = "Top read") { + .scrollAndPerform(title = "Top read") { verifyTopReadArticleIsGreyedOut(theme = Theme.LIGHT) } } From 8695ac157bbda68d60f8b45ad44b5830f8f2fbf5 Mon Sep 17 00:00:00 2001 From: williamrai Date: Thu, 4 Dec 2025 15:30:34 -0500 Subject: [PATCH 15/16] - replaces DELAY_MEDIUM with DELAY_SHORT --- .../org/wikipedia/robots/AppThemeRobot.kt | 28 +++++----- .../java/org/wikipedia/robots/SystemRobot.kt | 10 ++-- .../wikipedia/robots/feature/EditorRobot.kt | 10 ++-- .../robots/feature/ExploreFeedRobot.kt | 14 ++--- .../wikipedia/robots/feature/LoginRobot.kt | 2 +- .../wikipedia/robots/feature/MediaRobot.kt | 4 +- .../wikipedia/robots/feature/MoreMenuRobot.kt | 2 +- .../robots/feature/OnboardingRobot.kt | 4 +- .../org/wikipedia/robots/feature/PageRobot.kt | 52 ++++++++----------- .../robots/feature/ReadingListRobot.kt | 20 +++---- .../wikipedia/robots/feature/SearchRobot.kt | 8 +-- .../wikipedia/robots/feature/SettingsRobot.kt | 38 +++++++------- .../org/wikipedia/robots/feature/TabsRobot.kt | 4 +- .../robots/navigation/BottomNavRobot.kt | 12 ++--- .../robots/screen/HomeScreenRobot.kt | 6 +-- .../robots/screen/LanguageListRobot.kt | 4 +- .../robots/screen/NotificationScreenRobot.kt | 18 ------- .../robots/screen/SavedScreenRobot.kt | 4 +- .../screen/SuggestedEditsScreenRobot.kt | 8 +-- 19 files changed, 111 insertions(+), 137 deletions(-) delete mode 100644 app/src/androidTest/java/org/wikipedia/robots/screen/NotificationScreenRobot.kt diff --git a/app/src/androidTest/java/org/wikipedia/robots/AppThemeRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/AppThemeRobot.kt index 724f780c6f3..4dcc29b09af 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/AppThemeRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/AppThemeRobot.kt @@ -13,7 +13,7 @@ import org.wikipedia.base.TestConfig class AppThemeRobot : BaseRobot() { fun toggleTheme() = apply { click.onDisplayedView(R.id.page_theme) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun switchOffMatchSystemTheme() = apply { @@ -27,7 +27,7 @@ class AppThemeRobot : BaseRobot() { fun selectBlackTheme() = apply { scroll.toViewAndClick(R.id.button_theme_black) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun verifyBackgroundIsBlack() = apply { @@ -38,62 +38,62 @@ class AppThemeRobot : BaseRobot() { click.onViewWithId(R.id.page_theme) delay(TestConfig.DELAY_SHORT) scroll.toViewAndClick(R.id.button_theme_light) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickThemeIconOnEditPage() = apply { click.onDisplayedView(R.id.menu_edit_theme) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun increaseTextSize() = apply { scroll.toViewAndClick(R.id.buttonIncreaseTextSize) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun decreaseTextSize() = apply { scroll.toViewAndClick(R.id.buttonDecreaseTextSize) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun applySerif() = apply { scroll.toViewAndClick(R.id.button_font_family_serif) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun applySansSerif() = apply { scroll.toViewAndClick(R.id.button_font_family_sans_serif) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun toggleReadingFocusMode() = apply { scroll.toViewAndClick(R.id.theme_chooser_reading_focus_mode_switch) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun applySepiaTheme() = apply { scroll.toViewAndClick(R.id.button_theme_sepia) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun applyLightTheme() = apply { scroll.toViewAndClick(R.id.button_theme_light) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun applyDarkTheme() = apply { scroll.toViewAndClick(R.id.button_theme_dark) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun applyBlackTheme() = apply { scroll.toViewAndClick(R.id.button_theme_black) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun toggleMatchSystemTheme() = apply { scroll.toViewAndClick(R.id.theme_chooser_match_system_theme_switch) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun backToHomeScreen() = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/SystemRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/SystemRobot.kt index 008b881fda4..fe401052972 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/SystemRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/SystemRobot.kt @@ -16,22 +16,22 @@ import org.wikipedia.base.TestConfig class SystemRobot : BaseRobot() { fun turnOnAirplaneMode() = apply { TestUtil.setAirplaneMode(true) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun turnOffAirplaneMode() = apply { TestUtil.setAirplaneMode(false) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun turnOffInternet() = apply { TestUtil.toggleInternet(false) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun turnOnInternet() = apply { TestUtil.toggleInternet(true) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickOnSystemDialogWithText(text: String) = apply { @@ -45,7 +45,7 @@ class SystemRobot : BaseRobot() { } catch (e: Exception) { Log.d("dialog", "Dialog did not appear or couldn't be clicked.") } - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun enableDarkMode(context: Context) = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/EditorRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/EditorRobot.kt index 6204b5aa4c4..826f238c52c 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/EditorRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/EditorRobot.kt @@ -19,7 +19,7 @@ class EditorRobot : BaseRobot() { fun replaceTextInEditWindow(text: String) = apply { input.replaceTextInView(R.id.edit_section_text, text) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickEditWindow() = apply { @@ -43,12 +43,12 @@ class EditorRobot : BaseRobot() { fun tapNext() = apply { // can be flaky click.onDisplayedView(R.id.edit_actionbar_button_text) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickDefaultEditSummaryChoices() = apply { scroll.toTextAndClick("Fixed typo") - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun navigateUp() = apply { @@ -176,7 +176,7 @@ class EditorRobot : BaseRobot() { fun clickInsertMediaButton() = apply { click.onViewWithId(R.id.wikitext_button_insert_media) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickInsertLinkButton() = apply { @@ -223,7 +223,7 @@ class EditorRobot : BaseRobot() { } fun verifyEditPublished(context: Context) = apply { - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) val rawText = context.getString(R.string.edit_saved_successfully) val processedText = StringUtil.fromHtml(rawText).trim().toString() verify.messageOfSnackbar(processedText) diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt index 7f1a6ed49a4..4e78f37e217 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt @@ -36,7 +36,7 @@ class ExploreFeedRobot : BaseRobot() { ) ) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickTodayOnWikipedia(position: Int) = apply { @@ -79,7 +79,7 @@ class ExploreFeedRobot : BaseRobot() { list.clickNestedItem(R.id.news_cardview_recycler_view, 0) ) ) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickOnThisDayCard(position: Int) = apply { @@ -90,7 +90,7 @@ class ExploreFeedRobot : BaseRobot() { list.moveClickIntoViewAndClick(R.id.on_this_day_card_view_click_container) ) ) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickRandomArticle(position: Int) = apply { @@ -101,7 +101,7 @@ class ExploreFeedRobot : BaseRobot() { list.moveClickIntoViewAndClick(R.id.view_featured_article_card_content_container) ) ) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickBecauseYouReadArticle(position: Int) = apply { @@ -112,7 +112,7 @@ class ExploreFeedRobot : BaseRobot() { list.clickNestedItem(R.id.view_list_card_list, 0) ) ) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun verifyFeedViewSize(expectedCount: Int) = apply { @@ -142,7 +142,7 @@ class ExploreFeedRobot : BaseRobot() { fun verifyFeaturedArticleImageIsNotVisible() = apply { verify.viewWithIdIsNotVisible(viewId = R.id.articleImage) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun stayOnFeaturedArticleFor(milliseconds: Long) = apply { @@ -245,7 +245,7 @@ class ExploreFeedRobot : BaseRobot() { } fun verifyTopReadArticleIsGreyedOut(theme: Theme) = apply { - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) onView( allOf( withId(R.id.view_list_card_list), diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/LoginRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/LoginRobot.kt index 32783427f95..0f84e9c03e4 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/LoginRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/LoginRobot.kt @@ -47,7 +47,7 @@ class LoginRobot : BaseRobot() { private fun clickLoginButton() = apply { click.onDisplayedViewWithText(viewId = R.id.create_account_login_button, text = "Log in") - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } private fun setLoginUserNameFromBuildConfig() = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/MediaRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/MediaRobot.kt index 09006f89e4c..bdf0f0cb8e0 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/MediaRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/MediaRobot.kt @@ -55,12 +55,12 @@ class MediaRobot : BaseRobot() { fun verifyOverlayVisibility(isVisible: Boolean) = apply { if (isVisible) { verify.viewExists(R.id.toolbar_container) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) return@apply } verify.viewWithIdIsNotVisible(R.id.toolbar_container) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun navigateUp() = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/MoreMenuRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/MoreMenuRobot.kt index 5bf2d3ee78b..fa690c5f37e 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/MoreMenuRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/MoreMenuRobot.kt @@ -56,7 +56,7 @@ class MoreMenuRobot : BaseRobot() { fun clickTalk() = apply { click.onViewWithId(R.id.main_drawer_talk_container) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickWatchList() = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/OnboardingRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/OnboardingRobot.kt index 2216b8441e4..99421026370 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/OnboardingRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/OnboardingRobot.kt @@ -17,13 +17,13 @@ class OnboardingRobot : BaseRobot() { fun checkPrimaryTextViewColor(theme: Theme) = apply { val color = TestWikipediaColors.getGetColor(theme = theme, colorType = TestThemeColorType.PRIMARY) verify.textViewColor(textViewId = R.id.primaryTextView, colorResId = color) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun checkSecondaryTextViewColor(theme: Theme) = apply { val color = TestWikipediaColors.getGetColor(theme = theme, colorType = TestThemeColorType.SECONDARY) verify.textViewColor(textViewId = R.id.secondaryTextView, colorResId = color) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun moveAllTheWayToEndUsingTapButton() = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/PageRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/PageRobot.kt index 4068a62e338..976078de0ad 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/PageRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/PageRobot.kt @@ -32,17 +32,9 @@ import org.wikipedia.base.TestConfig import org.wikipedia.base.utils.AssertJavascriptAction class PageRobot(private val context: Context) : BaseRobot() { - - fun clickEditPencilAtTopOfArticle() = apply { - onWebView() - .withElement(findElement(Locator.CSS_SELECTOR, "a[data-id='0'].pcs-edit-section-link")) - .perform(webClick()) - delay(TestConfig.DELAY_SHORT) - } - fun clickLink(linkTitle: String) = apply { web.clickWebLink(linkTitle) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun verifyArticleTitle(expectedTitle: String) = apply { @@ -51,12 +43,12 @@ class PageRobot(private val context: Context) : BaseRobot() { fun verifyPreviewArticleDialogAppears() = apply { click.onDisplayedView(R.id.link_preview_toolbar) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun openPreviewLinkInNewTab() = apply { click.onDisplayedView(R.id.link_preview_secondary_button) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun dismissTooltip(activity: Activity) = apply { @@ -71,17 +63,17 @@ class PageRobot(private val context: Context) : BaseRobot() { fun clickLeadImage() = apply { delay(TestConfig.DELAY_SHORT) click.onDisplayedView(R.id.view_page_header_image) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickOverflowMenu(description: String) = apply { click.onDisplayedViewWithContentDescription(description) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun visitImagePage() = apply { click.onDisplayedViewWithText(viewId = R.id.title, text = "Go to image page") - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun verifyLeadImageIsNotVisible() = apply { @@ -90,12 +82,12 @@ class PageRobot(private val context: Context) : BaseRobot() { fun swipePagerLeft() = apply { swipe.left(R.id.pager) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun swipeLeftToShowTableOfContents() = apply { swipe.left(R.id.page_web_view) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun pressBack() = apply { @@ -105,12 +97,12 @@ class PageRobot(private val context: Context) : BaseRobot() { fun goBackToGalleryView() = apply { goBack() - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun goBackToOriginalArticle() = apply { goBack() - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun enableJavaScript() = apply { @@ -139,23 +131,23 @@ class PageRobot(private val context: Context) : BaseRobot() { fun swipeTableOfContentsAllTheWayToBottom() = apply { swipe.up(R.id.toc_list) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickAboutThisArticleTextInTOC() = apply { click.onViewWithText("About this article") - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun goToTalkPage() = apply { onWebView().withElement(findElement(Locator.CSS_SELECTOR, "a[title='View talk page']")) .perform(webClick()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickThirdTopic() = apply { list.clickRecyclerViewItemAtPosition(R.id.talkRecyclerView, 2) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun openOverflowMenu() = apply { @@ -165,7 +157,7 @@ class PageRobot(private val context: Context) : BaseRobot() { fun navigateBackToExploreFeed() = apply { click.onViewWithText("Explore") - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun removeArticleFromReadingList() = apply { @@ -208,14 +200,14 @@ class PageRobot(private val context: Context) : BaseRobot() { onWebView() .withElement(findElement(Locator.CSS_SELECTOR, ".pcs-table-infobox")) .perform(webScrollIntoView()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickToExpandQuickFactsTable() = apply { onWebView() .withElement(findElement(Locator.CSS_SELECTOR, ".pcs-table-infobox")) .perform(webClick()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } @SuppressLint("CheckResult") @@ -299,28 +291,28 @@ class PageRobot(private val context: Context) : BaseRobot() { onWebView() .withElement(findElement(Locator.ID, "pcs-footer-container-menu-heading")) .perform(webScrollIntoView()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun goToViewEditHistory() = apply { onWebView() .withElement(findElement(Locator.CSS_SELECTOR, "a[title='View edit history']")) .perform(webClick()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun scrollToLegalSection() = apply { onWebView() .withElement(findElement(Locator.ID, "pcs-footer-container-legal")) .perform(webScrollIntoView()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun scrollToAdministrativeDivisionOfIndiaArticle() = apply { onWebView() .withElement(findElement(Locator.ID, "Administrative_divisions")) .perform(webClick()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun scrollToAndhraPradeshOnIndiaArticle() = apply { @@ -328,7 +320,7 @@ class PageRobot(private val context: Context) : BaseRobot() { .withElement(findElement(Locator.CSS_SELECTOR, "a[title='Andhra Pradesh']")) .perform(webScrollIntoView()) .perform(webClick()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickLegalLink() = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/ReadingListRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/ReadingListRobot.kt index b1cd0a41a30..471584d5f58 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/ReadingListRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/ReadingListRobot.kt @@ -27,7 +27,7 @@ class ReadingListRobot : BaseRobot() { listId = R.id.recycler_view, position ) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickOnReadingLists(title: String) = apply { @@ -80,7 +80,7 @@ class ReadingListRobot : BaseRobot() { fun typeNameOfTheList(title: String, context: Context) = apply { input.replaceTextInView(viewId = R.id.text_input, title) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) if (verify.isViewWithTextVisible(context.getString(R.string.reading_list_title_exists, title))) { input.replaceTextInView(viewId = R.id.text_input, "$title${Math.random()}") } @@ -88,12 +88,12 @@ class ReadingListRobot : BaseRobot() { fun saveTheList(context: Context) = apply { click.onViewWithText(context.getString(R.string.text_input_dialog_ok_button_text)) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun viewTheList(context: Context) = apply { click.onViewWithText(context.getString(R.string.reading_list_added_view_button)) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun dismissTooltip(activity: Activity) = apply { @@ -103,7 +103,7 @@ class ReadingListRobot : BaseRobot() { fun clickOnGotIt() = apply { try { click.onViewWithText("Got it") - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } catch (e: Exception) { Log.e("ReadingListRobot:", "Text does not exist.") } @@ -122,7 +122,7 @@ class ReadingListRobot : BaseRobot() { } fun verifyArticleHasNotDownloaded() = apply { - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) onView(withId(R.id.reading_list_recycler_view)) .perform(RecyclerViewActions.scrollToPosition(1)) .check( @@ -141,7 +141,7 @@ class ReadingListRobot : BaseRobot() { } fun verifyArticleHasDownloaded() = apply { - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) onView(withId(R.id.reading_list_recycler_view)) .perform(RecyclerViewActions.scrollToPosition(1)) .check( @@ -181,7 +181,7 @@ class ReadingListRobot : BaseRobot() { fun clickNoThanks(context: Context) = apply { try { click.onViewWithText(context.getString(R.string.reading_list_prompt_turned_sync_on_dialog_no_thanks)) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } catch (e: Exception) { Log.e("ReadingListRobot: ", "${e.message}") } @@ -189,11 +189,11 @@ class ReadingListRobot : BaseRobot() { fun clickCreateList() = apply { click.onViewWithId(R.id.create_button) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun pressBack() = apply { - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) goBack() } } diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt index e0d6908b2fd..73e3b4e1adf 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/SearchRobot.kt @@ -73,7 +73,7 @@ class SearchRobot : BaseRobot() { fun clickFilterHistoryButton() = apply { click.onViewWithId(R.id.history_filter) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun closeFilterList() = apply { @@ -85,7 +85,7 @@ class SearchRobot : BaseRobot() { onView(withId(androidx.appcompat.R.id.search_close_btn)) .check(matches(isDisplayed())) .perform(click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun verifySearchTermIsCleared() = apply { @@ -119,7 +119,7 @@ class SearchRobot : BaseRobot() { fun clickLanguage(languageCode: String) = apply { val language = WikipediaApp.instance.languageState.getAppLanguageLocalizedName(languageCode) ?: "" list.selectTabWithText(R.id.horizontal_scroll_languages, language) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun checkSearchListItemHasRTLDirection() = apply { @@ -178,7 +178,7 @@ class SearchRobot : BaseRobot() { ViewActions.swipeLeft() ) ) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun verifyArticleRemoved(title: String) = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt index 447cee77611..fcc4ba38fa2 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt @@ -40,7 +40,7 @@ class SettingsRobot : BaseRobot() { ) .perform(actionOnItemAtPosition(2, click())) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun openMoreOptionsToolbar() = apply { @@ -49,7 +49,7 @@ class SettingsRobot : BaseRobot() { childAtPosition(childAtPosition(withId(R.id.toolbar), 2), 0), isDisplayed() )) .perform(click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun hideAllExploreFeeds() = apply { @@ -57,19 +57,19 @@ class SettingsRobot : BaseRobot() { onView(allOf(withId(R.id.title), withText("Hide all"), childAtPosition(childAtPosition(withId(androidx.appcompat.R.id.content), 0), 0), isDisplayed())) .perform(click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun showAllExploreFeeds() = apply { onView(allOf(withId(R.id.title), withText("Show all"), childAtPosition(childAtPosition(withId(androidx.appcompat.R.id.content), 0), 0), isDisplayed())) .perform(click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickAboutWikipediaAppOptionItem() = apply { scrollToSettingsPreferenceItem(R.string.about_description, click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun activateDeveloperMode(context: Context) = apply { @@ -77,37 +77,37 @@ class SettingsRobot : BaseRobot() { for (i in 1 until 8) { composeTestRule.onNodeWithContentDescription(context.getString(R.string.about_logo_content_description)) .performClick() - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickDeveloperMode() = apply { // Assert that developer mode is activated click.onDisplayedViewWithIdAndContentDescription(R.id.developer_settings, "Developer settings") - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun assertWeAreInDeveloperSettings() = apply { verify.viewWithTextDisplayed("Developer settings") - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickLanguages() = apply { scrollToSettingsPreferenceItem(R.string.preference_title_language, click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickExploreFeed() = apply { scrollToSettingsPreferenceItem(R.string.preference_title_customize_explore_feed, click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickLogOut(context: Context) = apply { try { scrollToSettingsPreferenceItem(R.string.preference_title_logout, scrollTo()) click.onViewWithText(context.getString(R.string.preference_title_logout)) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } catch (e: Exception) { Log.e("SettingsRobotError:", "User is not logged in.") } @@ -115,27 +115,27 @@ class SettingsRobot : BaseRobot() { fun toggleShowLinkPreviews() = apply { scrollToSettingsPreferenceItem(R.string.preference_title_show_link_previews, click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun toggleCollapseTables() = apply { scrollToSettingsPreferenceItem(R.string.preference_title_collapse_tables, click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickAppTheme() = apply { scrollToSettingsPreferenceItem(R.string.preference_title_app_theme, click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun toggleDownloadReadingList() = apply { scrollToSettingsPreferenceItem(R.string.preference_title_download_reading_list_articles, click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun toggleShowImages() = apply { scrollToSettingsPreferenceItem(R.string.preference_title_show_images, click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun verifyExploreFeedIsEmpty(context: Context) = apply { @@ -165,7 +165,7 @@ class SettingsRobot : BaseRobot() { fun pressBack() = apply { goBack() - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } private fun scrollToSettingsPreferenceItem(@IdRes preferenceTitle: Int, viewAction: ViewAction) = apply { @@ -173,6 +173,6 @@ class SettingsRobot : BaseRobot() { .perform( RecyclerViewActions.actionOnItem (hasDescendant(withText(preferenceTitle)), viewAction)) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } } diff --git a/app/src/androidTest/java/org/wikipedia/robots/feature/TabsRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/TabsRobot.kt index 48d45b1c32f..b22fd5eec4d 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/feature/TabsRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/feature/TabsRobot.kt @@ -18,12 +18,12 @@ class TabsRobot : BaseRobot() { fun launchTabsScreen() = apply { click.onDisplayedView(R.id.page_toolbar_button_tabs) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun createNewTabWithContentDescription(text: String) = apply { click.onDisplayedViewWithContentDescription(text) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun verifyTabCount(count: Int) = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/navigation/BottomNavRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/navigation/BottomNavRobot.kt index 73780c8c95e..bca7216826a 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/navigation/BottomNavRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/navigation/BottomNavRobot.kt @@ -33,7 +33,7 @@ class BottomNavRobot : BaseRobot() { childAtPosition(childAtPosition(withId(R.id.main_nav_tab_layout), 0), 1), isDisplayed() ) ).perform(click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun navigateToSearchPage() = apply { @@ -43,7 +43,7 @@ class BottomNavRobot : BaseRobot() { childAtPosition(childAtPosition(withId(R.id.main_nav_tab_layout), 0), 2), isDisplayed() ) ).perform(click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun navigateToActivityTab() = apply { @@ -62,19 +62,19 @@ class BottomNavRobot : BaseRobot() { fun navigateToMoreMenu() = apply { onView(allOf(withId(R.id.nav_tab_more), withContentDescription("More"), isDisplayed())).perform(click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun goToSettings() = apply { // Click on `Settings` option onView(allOf(withId(R.id.main_drawer_settings_container), isDisplayed())).perform(click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickLoginMenuItem() = apply { try { click.onViewWithId(R.id.main_drawer_login_button) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } catch (e: Exception) { Log.e("BottomNavRobotError:", "User logged in.") } @@ -83,7 +83,7 @@ class BottomNavRobot : BaseRobot() { fun clickEditsMenuItem() = apply { try { click.onViewWithId(R.id.main_drawer_edit_container) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } catch (e: Exception) { Log.e("BottomNavRobotError:", "Cannot find edits container.") } diff --git a/app/src/androidTest/java/org/wikipedia/robots/screen/HomeScreenRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/screen/HomeScreenRobot.kt index 6cb2d0544b2..111574b28ff 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/screen/HomeScreenRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/screen/HomeScreenRobot.kt @@ -29,13 +29,13 @@ class HomeScreenRobot : BaseRobot() { fun pressBack() = apply { goBack() - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun assertAllFeedCardsAreHidden() = apply { onView(allOf(withId(R.id.empty_container), withParent(withParent(withId(R.id.swipe_refresh_layout))), isDisplayed())) .check(matches(isDisplayed())) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun assertEmptyMessageIsNotVisible() = apply { @@ -48,7 +48,7 @@ class HomeScreenRobot : BaseRobot() { // Assert that images arent shown anymore onView(allOf(withId(R.id.articleImage), withParent(allOf(withId(R.id.articleImageContainer), withParent(withId(R.id.view_wiki_article_card)))), isDisplayed())).check(ViewAssertions.doesNotExist()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun dismissTooltip(activity: Activity) = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/screen/LanguageListRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/screen/LanguageListRobot.kt index 37d2828b91e..94dcb299b61 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/screen/LanguageListRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/screen/LanguageListRobot.kt @@ -31,7 +31,7 @@ class LanguageListRobot : BaseRobot() { textViewId = R.id.wiki_language_title ) click.onViewWithText("Add language") - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun assertAddLanguageTextColor(theme: Theme) = apply { @@ -65,7 +65,7 @@ class LanguageListRobot : BaseRobot() { composeTestRule.onNodeWithTag(title) .performClick() - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun assertJapaneseLanguageTextColor(context: Context, theme: Theme) = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/screen/NotificationScreenRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/screen/NotificationScreenRobot.kt deleted file mode 100644 index 1d8c32590bf..00000000000 --- a/app/src/androidTest/java/org/wikipedia/robots/screen/NotificationScreenRobot.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.wikipedia.robots.screen - -import BaseRobot -import org.wikipedia.R -import org.wikipedia.base.TestConfig - -class NotificationScreenRobot : BaseRobot() { - - fun clickSearchBar() = apply { - list.clickRecyclerViewItemAtPosition(viewId = R.id.notifications_recycler_view, position = 0) - delay(TestConfig.DELAY_MEDIUM) - } - - fun pressBack() = apply { - goBack() - delay(TestConfig.DELAY_SHORT) - } -} diff --git a/app/src/androidTest/java/org/wikipedia/robots/screen/SavedScreenRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/screen/SavedScreenRobot.kt index 0ba91fb87dd..058bfd6aecb 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/screen/SavedScreenRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/screen/SavedScreenRobot.kt @@ -60,7 +60,7 @@ class SavedScreenRobot : BaseRobot() { ViewActions.swipeLeft() ) ) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun verifySavedArticleIsRemoved(title: String) = apply { @@ -96,7 +96,7 @@ class SavedScreenRobot : BaseRobot() { fun clickFilterList() = apply { click.onViewWithId(R.id.menu_search_lists) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun pressBack() = apply { diff --git a/app/src/androidTest/java/org/wikipedia/robots/screen/SuggestedEditsScreenRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/screen/SuggestedEditsScreenRobot.kt index 946c5e9073e..f8c252253dc 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/screen/SuggestedEditsScreenRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/screen/SuggestedEditsScreenRobot.kt @@ -10,17 +10,17 @@ import org.wikipedia.settings.Prefs class SuggestedEditsScreenRobot : BaseRobot() { fun clickArticleDescriptions() = apply { list.scrollToRecyclerViewInsideNestedScrollView(recyclerViewId = R.id.tasksRecyclerView, position = 0, viewAction = click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickImageCaptions() = apply { list.scrollToRecyclerViewInsideNestedScrollView(recyclerViewId = R.id.tasksRecyclerView, position = 2, viewAction = click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickImageTags() = apply { list.scrollToRecyclerViewInsideNestedScrollView(recyclerViewId = R.id.tasksRecyclerView, position = 3, viewAction = click()) - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun clickSuggestedEdits() = apply { @@ -30,7 +30,7 @@ class SuggestedEditsScreenRobot : BaseRobot() { fun pressBack() = apply { goBack() - delay(TestConfig.DELAY_MEDIUM) + delay(TestConfig.DELAY_SHORT) } fun verifyArticleDescriptionDoesNotExist(context: Context) = apply { From 594396844e1bc58fac30c789a2aadd276779f4b2 Mon Sep 17 00:00:00 2001 From: williamrai Date: Thu, 4 Dec 2025 15:40:55 -0500 Subject: [PATCH 16/16] - updates smoke test timeout for testing --- .github/workflows/android_smoke_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/android_smoke_test.yml b/.github/workflows/android_smoke_test.yml index c984ed88307..90f6bbf00b8 100644 --- a/.github/workflows/android_smoke_test.yml +++ b/.github/workflows/android_smoke_test.yml @@ -8,7 +8,7 @@ env: jobs: instrumentation-tests: runs-on: ubuntu-latest - timeout-minutes: 45 + timeout-minutes: 60 strategy: fail-fast: false matrix: