Skip to content

Commit

Permalink
Make ui tests less flaky
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszkwiecinski committed Jan 6, 2022
1 parent b2b7740 commit 31b784b
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.matcher.ViewMatchers.withText
import io.github.wykopmobilny.tests.base.Page
import io.github.wykopmobilny.utils.waitVisible
import org.hamcrest.Matchers.startsWith

object AboutDialog : Page {

private val appInfo = withText(startsWith("Wypok"))

fun tapAppInfo() {
onView(appInfo).perform(click())
onView(appInfo).waitVisible().perform(click())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import io.github.wykopmobilny.utils.waitVisible

object AppearanceSettingsPage {

fun assertVisible() {
onView(withText("Ustawienia")).check(matches(isDisplayed()))
onView(withText("Ustawienia")).waitVisible().check(matches(isDisplayed()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package io.github.wykopmobilny.tests.pages

import androidx.test.espresso.Espresso.onView
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.matcher.ViewMatchers.hasSibling
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isSelected
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.espresso.matcher.ViewMatchers.withText
import io.github.wykopmobilny.R
import io.github.wykopmobilny.utils.waitNotVisible
import io.github.wykopmobilny.utils.waitVisible
import org.hamcrest.CoreMatchers.allOf

object BlacklistPage {
Expand All @@ -24,46 +24,46 @@ object BlacklistPage {
)

fun tapUsersTab() {
onView(usersTab).perform(click())
onView(usersTab).waitVisible().perform(click())
}

fun tapTagsTab() {
onView(tagsTab).perform(click())
onView(tagsTab).waitVisible().perform(click())
}

fun tapUnblockTag(tag: String) {
onView(lockIcon(tag)).perform(click())
onView(lockIcon(tag)).waitVisible().perform(click())
}

fun tapUnblockUser(user: String) {
onView(lockIcon(user)).perform(click())
onView(lockIcon(user)).waitVisible().perform(click())
}

fun tapImportButton() {
onView(withText("Zaimportuj")).perform(click())
onView(withText("Zaimportuj")).waitVisible().perform(click())
}

fun assertVisible() {
onView(withText("Zarządzaj czarną listą")).check(matches(isDisplayed()))
onView(withText("Zarządzaj czarną listą")).waitVisible()
}

fun assertBlockedUserVisible(user: String) {
onView(usersTab).check(matches(isSelected()))
onView(withText(user)).check(matches(isDisplayed()))
onView(usersTab).waitVisible().check(matches(isSelected()))
onView(withText(user)).waitVisible()
}

fun assertBlockedUserNotVisible(user: String) {
onView(usersTab).check(matches(isSelected()))
onView(withText(user)).check(doesNotExist())
onView(usersTab).waitVisible().check(matches(isSelected()))
onView(withText(user)).waitNotVisible()
}

fun assertBlockedTagVisible(tag: String) {
onView(tagsTab).check(matches(isSelected()))
onView(withText(tag)).check(matches(isDisplayed()))
onView(tagsTab).waitVisible().check(matches(isSelected()))
onView(withText(tag)).waitVisible()
}

fun assertBlockedTagNotVisible(tag: String) {
onView(tagsTab).check(matches(isSelected()))
onView(withText(tag)).check(doesNotExist())
onView(tagsTab).waitVisible().check(matches(isSelected()))
onView(withText(tag)).waitNotVisible()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.test.espresso.contrib.NavigationViewActions.navigateTo
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import com.google.android.material.navigation.NavigationView
import io.github.wykopmobilny.tests.base.Page
import io.github.wykopmobilny.utils.waitVisible

object MainPage : Page {

Expand All @@ -17,14 +18,14 @@ object MainPage : Page {
private val navigationView = isAssignableFrom(NavigationView::class.java)

fun tapDrawerOption(@IdRes option: Int) {
onView(navigationView).perform(navigateTo(option))
onView(navigationView).waitVisible().perform(navigateTo(option))
}

fun openDrawer() {
onView(drawer).perform(open())
onView(drawer).waitVisible().perform(open())
}

fun closeDrawer() {
onView(drawer).perform(close())
onView(drawer).waitVisible().perform(close())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText
import io.github.wykopmobilny.R
import io.github.wykopmobilny.tests.matchers.onPreference
import io.github.wykopmobilny.tests.matchers.tapPreference
import io.github.wykopmobilny.utils.waitVisible

object SettingsPage {

Expand All @@ -30,18 +31,19 @@ object SettingsPage {

fun tapBlacklistSettings() {
onView(withId(R.id.recycler_view))
.waitVisible()
.perform(actionOnItem<ViewHolder>(hasDescendant(manageBlacklistOption), click()))
}

fun assertConfirmationOptionChecked() {
onPreference(confirmationOption).check(matches(isChecked()))
onPreference(confirmationOption).waitVisible().check(matches(isChecked()))
}

fun assertConfirmationOptionNotChecked() {
onPreference(confirmationOption).check(matches(isNotChecked()))
onPreference(confirmationOption).waitVisible().check(matches(isNotChecked()))
}

fun assertVisible() {
onView(withText("Ustawienia")).check(matches(isDisplayed()))
onView(withText("Ustawienia")).waitVisible().check(matches(isDisplayed()))
}
}
55 changes: 55 additions & 0 deletions app/src/androidTest/java/io/github/wykopmobilny/utils/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.github.wykopmobilny.utils

import androidx.test.espresso.NoMatchingViewException
import androidx.test.espresso.ViewAssertion
import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.util.HumanReadables
import junit.framework.AssertionFailedError
import java.util.concurrent.TimeoutException

internal fun ViewInteraction.waitVisible(timeout: Long = 2000L): ViewInteraction {
val startTime = System.currentTimeMillis()
val endTime = startTime + timeout

do {
try {
check(matches(isDisplayed()))
return this
} catch (e: AssertionFailedError) {
Thread.sleep(50)
} catch (e: NoMatchingViewException) {
Thread.sleep(50)
}
} while (System.currentTimeMillis() < endTime)

throw TimeoutException()
}

internal fun ViewInteraction.waitNotVisible(timeout: Long = 2000L): ViewInteraction {
val startTime = System.currentTimeMillis()
val endTime = startTime + timeout

do {
try {
check(isNotDisplayed())
return this
} catch (e: AssertionFailedError) {
Thread.sleep(50)
} catch (e: NoMatchingViewException) {
Thread.sleep(50)
}
} while (System.currentTimeMillis() < endTime)

throw TimeoutException()
}

fun isNotDisplayed() = ViewAssertion { view, _ ->
if (isDisplayed().matches(view)) {
throw AssertionError(
"View is present in the hierarchy and Displayed: " +
HumanReadables.describe(view),
)
}
}

0 comments on commit 31b784b

Please sign in to comment.