Skip to content

Commit

Permalink
Merge branch 'main' into rabbit_holes_design
Browse files Browse the repository at this point in the history
  • Loading branch information
dbrant authored Nov 14, 2024
2 parents 5197ce3 + c46dc78 commit 1c1155d
Show file tree
Hide file tree
Showing 54 changed files with 2,610 additions and 1,332 deletions.
24 changes: 24 additions & 0 deletions app/src/androidTest/java/org/wikipedia/FakeData.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.wikipedia

import android.location.Location
import android.net.Uri
import org.wikipedia.dataclient.WikiSite
import org.wikipedia.history.HistoryEntry
import org.wikipedia.page.PageTitle

object FakeData {
val site = WikiSite(
uri = Uri.parse("https://en.wikipedia.org")
)
val title = PageTitle(
_displayText = "Hopf_fibration",
_text = "Hopf fibration",
description = "Fiber bundle of the 3-sphere over the 2-sphere, with 1-spheres as fibers",
thumbUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Hopf_Fibration.png/320px-Hopf_Fibration.png",
wikiSite = site
)
val inNewTab = false
val position = 0
val location: Location? = null
val historyEntry = HistoryEntry(title, HistoryEntry.SOURCE_SEARCH)
}
30 changes: 30 additions & 0 deletions app/src/androidTest/java/org/wikipedia/TestSuite.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.wikipedia

import org.junit.runner.RunWith
import org.junit.runners.Suite
import org.junit.runners.Suite.SuiteClasses
import org.wikipedia.robots.feature.SettingsRobot
import org.wikipedia.test.loggedinuser.ContributionScreenTest
import org.wikipedia.test.loggedinuser.NotificationScreenTest
import org.wikipedia.test.loggedoutuser.EditArticleTest
import org.wikipedia.test.loggedoutuser.ExploreFeedTest
import org.wikipedia.test.loggedoutuser.HistoryScreenTest
import org.wikipedia.test.loggedoutuser.HomeScreenTest
import org.wikipedia.test.loggedoutuser.OnboardingTest
import org.wikipedia.test.loggedoutuser.PageTest
import org.wikipedia.test.loggedoutuser.SavedScreenTest

@RunWith(Suite::class)
@SuiteClasses(
EditArticleTest::class,
ExploreFeedTest::class,
HistoryScreenTest::class,
HomeScreenTest::class,
OnboardingTest::class,
PageTest::class,
SavedScreenTest::class,
SettingsRobot::class,
ContributionScreenTest::class,
NotificationScreenTest::class
)
class TestSuite
301 changes: 301 additions & 0 deletions app/src/androidTest/java/org/wikipedia/base/BaseRobot.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
package org.wikipedia.base

import android.app.Activity
import android.graphics.Rect
import android.view.View
import androidx.annotation.IdRes
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
import androidx.test.espresso.action.ViewActions.replaceText
import androidx.test.espresso.action.ViewActions.scrollTo
import androidx.test.espresso.action.ViewActions.swipeLeft
import androidx.test.espresso.action.ViewActions.swipeRight
import androidx.test.espresso.action.ViewActions.swipeUp
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.RootMatchers.withDecorView
import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.web.assertion.WebViewAssertions
import androidx.test.espresso.web.sugar.Web.onWebView
import androidx.test.espresso.web.webdriver.DriverAtoms
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
import androidx.test.espresso.web.webdriver.DriverAtoms.webClick
import androidx.test.espresso.web.webdriver.Locator
import org.hamcrest.Matcher
import org.hamcrest.Matchers
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.not
import org.wikipedia.TestUtil
import org.wikipedia.TestUtil.isDisplayed
import org.wikipedia.TestUtil.waitOnId
import java.util.concurrent.TimeUnit

abstract class BaseRobot {

protected fun clickOnViewWithId(@IdRes viewId: Int) {
onView(withId(viewId)).perform(click())
}

protected fun clickOnDisplayedView(@IdRes viewId: Int) {
onView(allOf(withId(viewId), isDisplayed())).perform(click())
}

protected fun clicksOnDisplayedViewWithText(@IdRes viewId: Int, text: String) {
onView(allOf(withId(viewId), withText(text), isDisplayed())).perform(click())
}

protected fun clickOnDisplayedViewWithContentDescription(description: String) {
onView(allOf(withContentDescription(description), isDisplayed())).perform(click())
}

protected fun clickOnDisplayedViewWithIdAnContentDescription(
@IdRes viewId: Int,
description: String
) {
onView(allOf(withId(viewId), withContentDescription(description), isDisplayed())).perform(
click()
)
}

protected fun clickOnViewWithText(text: String) {
onView(withText(text)).perform(click())
}

protected fun typeTextInView(@IdRes viewId: Int, text: String) {
onView(allOf(withId(viewId), isDisplayed()))
.perform(replaceText(text), closeSoftKeyboard())
}

protected fun clickOnItemInList(@IdRes listId: Int, position: Int) {
onView(withId(listId))
.perform(
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(
position,
click()
)
)
}

protected fun scrollToView(@IdRes viewId: Int) {
onView(withId(viewId)).perform(scrollTo())
}

protected fun scrollToViewAndClick(@IdRes viewId: Int) {
onView(withId(viewId)).perform(scrollTo(), click())
}

protected fun scrollToTextAndClick(text: String) {
onView(allOf(withText(text))).perform(scrollTo(), click())
}

protected fun checkViewExists(@IdRes viewId: Int) {
onView(withId(viewId)).check(matches(isDisplayed()))
}

protected fun checkViewDoesNotExist(@IdRes viewId: Int) {
onView(withId(viewId)).check(matches(not(isDisplayed())))
}

protected fun checkViewWithTextDisplayed(text: String) {
onView(withText(text)).check(matches(isDisplayed()))
}

protected fun checkViewWithIdDisplayed(@IdRes viewId: Int) {
onView(withId(viewId)).check(matches(isDisplayed()))
}

protected fun isViewWithTextVisible(text: String): Boolean {
var isDisplayed = false
onView(withText(text)).check { view, noViewFoundException ->
isDisplayed = noViewFoundException == null && view.isShown
}
return isDisplayed
}

protected fun isViewWithIdDisplayed(@IdRes viewId: Int): Boolean {
var isDisplayed = false
onView(withId(viewId)).check { view, noViewFoundException ->
isDisplayed = noViewFoundException == null && view.isShown
}
return isDisplayed
}

protected fun checkViewWithIdAndText(@IdRes viewId: Int, text: String) {
onView(allOf(withId(viewId), withText(text))).check(matches(isDisplayed()))
}

protected fun delay(seconds: Long) {
onView(isRoot()).perform(waitOnId(TimeUnit.SECONDS.toMillis(seconds)))
}

protected fun checkWithTextIsDisplayed(@IdRes viewId: Int, text: String) {
onView(allOf(withId(viewId), withText(text), isDisplayed()))
.check(matches(withText(text)))
}

protected fun checkIfViewIsDisplayingText(@IdRes viewId: Int, text: String) {
onView(allOf(withId(viewId), isDisplayed()))
.check(matches(withText(text)))
}

protected fun swipeLeft(@IdRes viewId: Int) {
onView(withId(viewId)).perform(swipeLeft())
}

protected fun swipeRight(@IdRes viewId: Int) {
onView(withId(viewId)).perform(swipeRight())
}

protected fun goBack() {
pressBack()
}

protected fun clickWebLink(linkTitle: String) = apply {
onWebView()
.withElement(findElement(Locator.CSS_SELECTOR, "a[title='$linkTitle']"))
.perform(webClick())
}

protected fun verifyH1Title(expectedTitle: String) = apply {
onWebView()
.withElement(findElement(Locator.CSS_SELECTOR, "h1"))
.check(
WebViewAssertions.webMatches(
DriverAtoms.getText(),
Matchers.`is`(expectedTitle)
)
)
}

protected fun verifyWithMatcher(@IdRes viewId: Int, matcher: Matcher<View>) {
onView(withId(viewId))
.check(matches(matcher))
}

protected fun swipeDownOnTheWebView(@IdRes viewId: Int) {
onView(withId(viewId)).perform(TestUtil.swipeDownWebView())
delay(TestConfig.DELAY_LARGE)
}

protected fun performIfDialogShown(
dialogText: String,
action: () -> Unit
) {
try {
onView(withText(dialogText))
.inRoot(isDialog())
.check(matches(isDisplayed()))
action()
} catch (e: Exception) {
// Dialog not shown or text not found
}
}

protected fun swipeUp(@IdRes viewId: Int) {
onView(allOf(withId(viewId))).perform(swipeUp())
}

protected fun clickRecyclerViewItemAtPosition(@IdRes viewId: Int, position: Int) {
onView(withId(viewId))
.perform(
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(
position,
click()
)
)
}

protected fun scrollToPositionInRecyclerView(@IdRes viewId: Int, position: Int) {
onView(withId(viewId))
.perform(
RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(position)
)
}

protected fun makeViewVisibleAndLongClick(@IdRes viewId: Int, @IdRes parentViewId: Int) {
onView(allOf(withId(viewId), isDescendantOfA(withId(parentViewId))))
.perform(scrollAndLongClick())
}

private fun scrollAndLongClick() = object : ViewAction {
override fun getConstraints(): Matcher<View> {
return isDisplayingAtLeast(10)
}

override fun getDescription(): String {
return "Scroll item into view and long click"
}

override fun perform(uiController: UiController, view: View) {
if (!isDisplayingAtLeast(90).matches(view)) {
view.requestRectangleOnScreen(
Rect(0, 0, view.width, view.height),
true
)
uiController.loopMainThreadForAtLeast(500)
}

view.performLongClick()
uiController.loopMainThreadForAtLeast(1000)
}
}

protected fun clickIfVisible(): ViewAction {
return object : ViewAction {
override fun getConstraints(): Matcher<View> {
return isDisplayingAtLeast(90)
}

override fun getDescription(): String {
return "Click if Visible"
}

override fun perform(uiController: UiController, view: View) {
if (view.isShown && view.isEnabled) {
view.performClick()
uiController.loopMainThreadForAtLeast(500)
}
}
}
}

protected fun dismissTooltipIfAny(activity: Activity, @IdRes viewId: Int) = apply {
onView(allOf(withId(viewId))).inRoot(withDecorView(not(Matchers.`is`(activity.window.decorView))))
.perform(click())
}

private fun scrollAndClick() = object : ViewAction {
override fun getConstraints(): Matcher<View> {
return isDisplayingAtLeast(10)
}

override fun getDescription(): String {
return "Scroll item into view and click"
}

override fun perform(uiController: UiController, view: View) {
if (!isDisplayingAtLeast(90).matches(view)) {
view.requestRectangleOnScreen(
Rect(0, 0, view.width, view.height),
true
)
uiController.loopMainThreadForAtLeast(500)
}

view.performClick()
uiController.loopMainThreadForAtLeast(1000)
}
}
}
Loading

0 comments on commit 1c1155d

Please sign in to comment.