diff --git a/.github/workflows/android_offline_test.yml b/.github/workflows/android_offline_test.yml index e1b3de31423..193fd715646 100644 --- a/.github/workflows/android_offline_test.yml +++ b/.github/workflows/android_offline_test.yml @@ -7,8 +7,19 @@ env: jobs: instrumentation-tests: - runs-on: macos-latest + 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 @@ -19,15 +30,24 @@ jobs: cache: 'gradle' - uses: gradle/gradle-build-action@v3 - - name: Instrumentation Tests + - 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: run 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.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/.github/workflows/android_smoke_test.yml b/.github/workflows/android_smoke_test.yml index 3c4f0c35081..c984ed88307 100644 --- a/.github/workflows/android_smoke_test.yml +++ b/.github/workflows/android_smoke_test.yml @@ -7,8 +7,19 @@ env: jobs: instrumentation-tests: - runs-on: macos-latest - timeout-minutes: 30 + runs-on: ubuntu-latest + timeout-minutes: 45 + 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 @@ -19,15 +30,24 @@ 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 - script: ./gradlew connectedDevDebugAndroidTest --stacktrace --no-daemon + 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/** 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/BaseTest.kt b/app/src/androidTest/java/org/wikipedia/base/BaseTest.kt index 59bf433eadb..b415ca3d729 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 @@ -40,6 +41,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( @@ -56,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 @@ -67,11 +72,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 } @@ -93,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/base/actions/ClickActions.kt b/app/src/androidTest/java/org/wikipedia/base/actions/ClickActions.kt index b4fb820962b..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( @@ -134,9 +149,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/base/actions/ListActions.kt b/app/src/androidTest/java/org/wikipedia/base/actions/ListActions.kt index fc31f7e7e78..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 @@ -13,7 +14,6 @@ import androidx.test.espresso.Espresso.onView 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 @@ -22,17 +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 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 { @@ -53,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( @@ -78,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( @@ -170,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 @@ -223,13 +199,29 @@ class ListActions { ) } - private fun verifyItemAtPosition( - recyclerViewId: Int, - position: Int, - itemMatcher: Matcher - ): ViewInteraction { - return onView(withId(recyclerViewId)) - .check(matches(atPosition(position, itemMatcher))) + 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 clickChildViewWithId(@IdRes id: Int) = object : ViewAction { @@ -254,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/DialogRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt index 17e0e1bca74..dc532711672 100644 --- a/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt +++ b/app/src/androidTest/java/org/wikipedia/robots/DialogRobot.kt @@ -2,16 +2,28 @@ 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() { + 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.") } fun dismissBigEnglishDialog() = apply { - click.ifDialogShown("Maybe later", errorString = "No Big English dialog shown.") + try { + click.onDisplayedViewWithIdAndContentDescription(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/ExploreFeedRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/ExploreFeedRobot.kt index 3212facaf43..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,26 +2,21 @@ 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.NoMatchingViewException 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.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 import org.hamcrest.Matchers.allOf import org.wikipedia.R import org.wikipedia.TestConstants @@ -34,18 +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_page), childAtPosition( - allOf( - withId(R.id.event_layout), - childAtPosition(withId(R.id.on_this_day_card_view_click_container), 0) - ), 3 - ), isDisplayed() + 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) + ) ) - ) - .perform(click()) delay(TestConfig.DELAY_MEDIUM) } @@ -56,15 +113,6 @@ 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 - ) - delay(TestConfig.DELAY_MEDIUM) - } - fun pressBack() = apply { goBack() delay(TestConfig.DELAY_LARGE) @@ -74,31 +122,6 @@ class ExploreFeedRobot : BaseRobot() { click.onDisplayedViewWithContentDescription("Navigate up") } - 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", "") - } - } - fun clickBecauseYouReadArticle() = apply { onView( allOf( @@ -109,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") } @@ -134,24 +146,6 @@ class ExploreFeedRobot : BaseRobot() { delay(TestConfig.DELAY_MEDIUM) } - fun clickPictureOfTheDay() = apply { - click.onViewWithId(R.id.view_featured_image_card_content_container) - delay(TestConfig.DELAY_SHORT) - } - - fun clickTodayOnWikipedia() = apply { - click.onViewWithIdAndContainsString(R.id.footerActionButton, text = "View main page") - delay(TestConfig.DELAY_LARGE) - } - - fun clickOnFeaturedArticle() = apply { - scroll.toViewAndMakeVisibleAndClick( - viewId = R.id.view_featured_article_card_content_container, - parentViewId = R.id.feed_view - ) - delay(TestConfig.DELAY_MEDIUM) - } - fun stayOnFeaturedArticleFor(milliseconds: Long) = apply { scroll.toViewAndMakeVisibleAndClick( viewId = R.id.view_featured_article_card_content_container, @@ -170,58 +164,65 @@ 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()) delay(TestConfig.DELAY_SWIPE_TO_REFRESH) } - fun scrollToItem( - recyclerViewId: Int = R.id.feed_view, + fun scrollToAndPerform( title: String, - textViewId: Int = R.id.view_card_header_title, - verticalOffset: Int = 200 + shouldSwipeMore: Boolean = false, + action: (ExploreFeedRobot.() -> Unit)? = null ) = apply { - list.scrollToRecyclerView( - recyclerViewId, - title, - textViewId, - verticalOffset - ) + 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 { 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) } @@ -234,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/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 bb7cba86938..143fd423313 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 @@ -112,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) } @@ -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/robots/feature/SettingsRobot.kt b/app/src/androidTest/java/org/wikipedia/robots/feature/SettingsRobot.kt index 6fa4957f45f..447cee77611 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.onDisplayedViewWithIdAndContentDescription(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) } 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/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/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() 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..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,38 +42,44 @@ class FeedScreenTest : BaseTest( // Feed Test flow exploreFeedRobot - .scrollToItem(title = FEATURED_ARTICLE) - .assertFeaturedArticleTitleColor(theme = Theme.LIGHT) - .clickOnFeaturedArticle() - .pressBack() - .scrollToItem(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE, verticalOffset = -100) - .clickTodayOnWikipedia() - dialogRobot - .dismissBigEnglishDialog() - .dismissContributionDialog() - exploreFeedRobot - .pressBack() + .scrollAndPerform(title = FEATURED_ARTICLE) { position -> + assertFeaturedArticleTitleColor(theme = Theme.LIGHT) + clickOnFeaturedArticle(position) + pressBack() + } + .scrollAndPerform(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE) { position -> + clickTodayOnWikipedia(position) + dialogRobot + .dismissBigEnglishDialog() + .dismissContributionDialog() + pressBack() + } systemRobot .enableDarkMode(context) exploreFeedRobot - .scrollToItem(title = TODAY_ON_WIKIPEDIA_MAIN_PAGE, verticalOffset = 400) - .scrollToItem(title = TOP_READ_ARTICLES, verticalOffset = 400) - .assertTopReadTitleColor(theme = Theme.DARK) - .clickTopReadArticle() - .scrollToItem(title = PICTURE_OF_DAY) - .clickPictureOfTheDay() - .pressBack() + .scrollAndPerform(title = TOP_READ_ARTICLES) { position -> + assertTopReadTitleColor(theme = Theme.DARK) + clickTopReadArticle(position) + pressBack() + } + .scrollAndPerform(title = PICTURE_OF_DAY) { position -> + clickPictureOfTheDay(position) + pressBack() + } systemRobot - .enableDarkMode(context) + .disableDarkMode(context) exploreFeedRobot - .scrollToItem(title = NEWS_CARD) - .clickNewsArticle() - .pressBack() - .scrollToItem(title = ON_THIS_DAY_CARD) - .clickOnThisDayCard() - .pressBack() - .scrollToItem(title = RANDOM_CARD) - .clickRandomArticle() - .pressBack() + .scrollAndPerform(title = NEWS_CARD) { position -> + clickNewsArticle(position) + pressBack() + } + .scrollAndPerform(title = ON_THIS_DAY_CARD) { position -> + clickOnThisDayCard(position) + pressBack() + } + .scrollAndPerform(title = RANDOM_CARD) { position -> + clickRandomArticle(position) + pressBack() + } } } 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() 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..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 @@ -68,6 +69,9 @@ class SavedArticleOnlineOfflineTest : BaseTest( .pressBack() bottomNavRobot .navigateToExploreFeed() + dialogRobot + .dismissSurveyDialog() + bottomNavRobot .navigateToSavedPage() savedScreenRobot .clickItemOnTheList(0) 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) + } } } diff --git a/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt b/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt index 1860c1ade61..7c96fd15310 100644 --- a/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt +++ b/app/src/androidTest/java/org/wikipedia/testsuites/TestSuite.kt @@ -16,8 +16,6 @@ import org.wikipedia.tests.SuggestedEditScreenTest SettingsTestSuite::class, ExploreFeedTestSuite::class, SearchTest::class, - SuggestedEditScreenTest::class, - SettingsTestSuite::class, DeepLinkingTest::class, ArticlesTestSuite::class )