Skip to content

Commit

Permalink
add more matchers (#124)
Browse files Browse the repository at this point in the history
* add more matchers

* fix
  • Loading branch information
QAutomatron authored Jun 8, 2022
1 parent a4409c0 commit 87da943
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package com.qautomatron.kopi.library.element
import androidx.test.espresso.NoMatchingViewException
import androidx.test.espresso.PerformException

/**
* Timeout for perform
*/
object ElementActionConfig {
var timeout = 3_000L
var interval: Long = 250
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package com.qautomatron.kopi.library.element
import androidx.test.espresso.NoMatchingViewException
import androidx.test.espresso.PerformException

/**
* Timeout for check()
*/
object ElementAssertionConfig {
var timeout = 3_000L
var interval: Long = 250
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.*
import com.qautomatron.kopi.library.matcher.IsNotMovingMatcher
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.hamcrest.Matchers.anyOf
Expand Down Expand Up @@ -105,4 +106,10 @@ interface ElementAssertions<T> {
* @param resourceId expected text by resourceId
*/
fun sameAs(resourceId: Int) = check(matches(withText(resourceId)))

/**
* Check element is not moving
* @param timeoutInMills
*/
fun isNotMoving(timeoutInMills: Int? = null) = check(matches(IsNotMovingMatcher(timeoutInMills)))
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ package com.qautomatron.kopi.library.element
import android.view.View
import androidx.test.espresso.Root
import androidx.test.espresso.ViewAssertion
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.qautomatron.kopi.library.wait.ViewMatcherWaiter
import org.hamcrest.CoreMatchers
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.Matcher
import org.hamcrest.Matchers

Expand Down Expand Up @@ -54,6 +58,20 @@ interface ElementWaits<T> {
return this as T
}

fun waitToBeSelected() = this.waitFor(ViewAssertions.matches(ViewMatchers.isSelected()))
fun waitToBeEnabled() = this.waitFor(ViewAssertions.matches(ViewMatchers.isEnabled()))
fun waitToBeDisabled() = this.waitFor(
ViewAssertions.matches(
CoreMatchers.not(
ViewMatchers.isEnabled()
)
)
)
fun waitToBeClickable() = this.waitFor(ViewAssertions.matches(ViewMatchers.isClickable()))
fun waitForText(text: String) = this.waitFor(withText(text))
fun waitForText(resId: Int) = this.waitFor(withText(resId))
fun waitForTextContains(text: String) = this.waitFor(withText(containsString(text)))

/**
* Wait for specific matcher
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.qautomatron.kopi.library.matcher

import android.view.View
import android.view.ViewGroup
import androidx.test.espresso.matcher.BoundedMatcher
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher

fun withChildViewCount(count: Int, childMatcher: Matcher<View?>): Matcher<View?> {
return object : BoundedMatcher<View?, ViewGroup>(ViewGroup::class.java) {
override fun matchesSafely(viewGroup: ViewGroup): Boolean {
var matchCount = 0
for (i in 0 until viewGroup.childCount) {
if (childMatcher.matches(viewGroup.getChildAt(i))) {
matchCount++
}
}
return matchCount == count
}

override fun describeTo(description: Description) {
description.appendText("ViewGroup with child-count=$count and")
childMatcher.describeTo(description)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.qautomatron.kopi.library.matcher

import android.os.SystemClock
import android.view.View
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.hamcrest.Description
import org.hamcrest.TypeSafeMatcher

class IsNotMovingMatcher(private val timeoutInMills: Int?) : TypeSafeMatcher<View>() {

private val defaultTimeoutInMills: Int = 1000
private val pollingIntervalInMills: Int = 250

override fun matchesSafely(view: View): Boolean {
val endTime = SystemClock.elapsedRealtime() + (timeoutInMills ?: defaultTimeoutInMills)
val oldLocation = IntArray(2)
val newLocation = IntArray(2)

do {
view.getLocationOnScreen(oldLocation)
runBlocking { delay(pollingIntervalInMills.toLong()) }
view.getLocationOnScreen(newLocation)

if (newLocation.contentEquals(oldLocation)) {
return true
}
} while (SystemClock.elapsedRealtime() < endTime)

return false
}

override fun describeTo(description: Description) {
description.appendText("which is stop moving before $timeoutInMills")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.qautomatron.kopi.library.matcher

import android.view.View
import android.view.ViewGroup
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher

class NthChildOfMatcher(val parentMatcher: Matcher<View>, val childPosition: Int) : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("with $childPosition child view of type parentMatcher")
}

override fun matchesSafely(view: View): Boolean {
if (view.parent !is ViewGroup) {
return parentMatcher.matches(view.parent)
}
val group = view.parent as ViewGroup
return parentMatcher.matches(view.parent) && group.getChildAt(childPosition) == view
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.qautomatron.kopi.library.matcher

import android.view.WindowManager
import androidx.test.espresso.Root
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher

class ToastMatcher : TypeSafeMatcher<Root>() {

override fun describeTo(description: Description) {
description.appendText("is toast")
}

public override fun matchesSafely(root: Root): Boolean {
val type = root.windowLayoutParams.get().type
if (type == WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY) {
val windowToken = root.decorView.windowToken
val appToken = root.decorView.applicationWindowToken
if (windowToken === appToken) {
// windowToken == appToken means this window isn't contained by any other windows.
// if it was a window for an activity, it would have TYPE_BASE_APPLICATION.
return true
}
}
return false
}

fun isToast(): Matcher<Root> {
return ToastMatcher()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object DeviceSteps {
/**
* Wait for activity by name
*/
fun waitForActivity(fullName: String, timeoutInMillis: Int?, pollingInMillis: Int?) =
fun waitForActivity(fullName: String, timeoutInMillis: Int? = null, pollingInMillis: Int? = null) =
Watcher.waitForCondition(WaitForActivity(fullName), timeoutInMillis, pollingInMillis)

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.qautomatron.kopi.sample

import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers.withId
import com.qautomatron.kopi.library.element.Element
import com.qautomatron.kopi.library.matcher.IsNotMovingMatcher
import com.qautomatron.kopi.library.matcher.first
import org.junit.Test

Expand All @@ -10,10 +12,16 @@ import org.junit.Test
*/
class ElementMatcherTest: BaseTest() {

private val element = Element(first(withId(R.id.sameId1)))

@Test
fun should_get_first_element() {
val element = Element(first(withId(R.id.sameId1)))
element.sameAs("Text1")
}

@Test
fun should_is_not_moving_matcher() {
val element = Element(withId(R.id.message_text))
val timeoutInMills = 1000
element.isNotMoving(timeoutInMills)
}
}

0 comments on commit 87da943

Please sign in to comment.