-
Notifications
You must be signed in to change notification settings - Fork 12
Espresso operations
Simple espresso operation looks like this
onView(withId(R.id.send_button)).check(isDisplayed()).perform(click())
the same with Ultron
withId(R.id.send_button).isDisplayed().click()
Names of all Ultron operations are the same as espresso one. There are a lot of additional operations those simplifies test development.
//------ actions ------
click()
doubleClick()
longClick()
typeText(text: String)
replaceText(text: String)
clearText()
pressKey(keyCode: Int)
pressKey(key: EspressoKey)
closeSoftKeyboard()
swipeLeft()
swipeRight()
swipeUp()
swipeDown()
scrollTo()
perform(viewAction: ViewAction) // execute custom espresso action as Ultron one
perform(params: UltronEspressoActionParams? = null, block: (uiController: UiController, view: View) -> Unit)
<T> execute(params: UltronEspressoActionParams? = null, block: (uiController: UiController, view: View) -> T): T
//------ get View property actions ------
getText() : String?
getContentDescription() : String?
getDrawable() : Drawable?
//------ assertions ------
exists()
doesNotExist()
isDisplayed()
isNotDisplayed()
isCompletelyDisplayed()
isDisplayingAtLeast(percentage: Int)
doesNotExist()
isEnabled()
isNotEnabled()
isSelected()
isNotSelected()
isClickable()
isNotClickable()
isChecked()
isNotChecked()
isFocusable()
isNotFocusable()
hasFocus()
isJavascriptEnabled()
hasText(text: String)
hasText(resourceId: Int)
hasText(stringMatcher: Matcher<String>)
textContains(text: String)
hasContentDescription(text: String)
hasContentDescription(resourceId: Int)
hasContentDescription(charSequenceMatcher: Matcher<CharSequence>)
contentDescriptionContains(text: String)
assertMatches(condition: Matcher<View>) // execute custom espresso assertion as Ultron one
hasDrawable(@DrawableRes resourceId: Int)
hasAnyDrawable()
hasCurrentTextColor(@ColorRes colorRes: Int)
hasCurrentHintTextColor(@ColorRes colorRes: Int)
hasShadowColor(@ColorRes colorRes: Int)
hasHighlightColor(@ColorRes colorRes: Int)
assertMatches(params: UltronEspressoAssertionParams? = null, block: (view: View) -> Boolean)
//------ general ------
withTimeout(timeoutMs: Long) // set custom timeout for operations
withResultHandler(resultHandlerBlock) // set custom result handler and process operation result
withAssertion(assertion: OperationAssertion) // define custom assertion of action success
withAssertion(name: String = "", isListened: Boolean = false, block: () -> Unit)
//------ custom clicks ------
clickTopLeft(offsetX: Int, offsetY: Int)
clickTopCenter(offsetY: Int)
clickTopRight(offsetX: Int, offsetY: Int)
clickCenterRight(offsetX: Int)
clickBottomRight(offsetX: Int, offsetY: Int)
clickBottomCenter(offsetY: Int)
clickBottomLeft(offsetX: Int, offsetY: Int)
clickCenterLeft(offsetX: Int)
Specify page elements as properties of PageObject class.
object SomePage : Page<SomePage>() {
private val button = withId(R.id.button1)
private val eventStatus = withId(R.id.last_event_status)
}
Use this properties in page steps
object SomePage : Page<SomePage>() {
//page elements
fun someUserStepOnPage(expectedEventText: String){
button.click()
eventStatus.hasText(expectedEventText)
}
}
withId(R.id.last_event_status).withTimeout(10_000).isDisplayed()
There are 2 ways of using custom timeout:
- Specify it for page property and it will be applied for all operations with this element
object SomePage : Page<SomePage>() {
private val eventStatus = withId(R.id.last_event_status).withTimeout(10_000)
}
- Specify it inside special step there the element operation could take more time. This timeout value will be applied only once for single operation.
object SomePage : Page<SomePage>() {
fun someLongUserStep(expectedEventText: String){
longRequestButton.click()
eventStatus.withTimeout(20_000).hasText(expectedEventText)
}
}
There is isSuccess
method that allows us to get the result of any operation as boolean value. In case of false it could be executed to long (5 sec by default). So it reasonable to specify custom timeout for some operations.
val isButtonDisplayed = withId(R.id.button).isSuccess { withTimeout(2_000).isDisplayed() }
if (isButtonDisplayed) {
//do some reasonable actions
}
To execute any operation inside dialog or popup with espresso you have to specify correct root element
onView(withText("OK"))).inRoot(isDialog()).perform(click())
onView(withText("Cancel")).inRoot(isPlatformPopup()).perform(click())
Here is a point we need to put our minds on.
Ultron extends not only Matcher<View>
object but also ViewInteraction
and DataInteraction
objects
onView(withText("OK"))).inRoot(isDialog())
returns ViewInteraction. Therefore it's possible to use Ultron operations with dialogs.
So the best way would be a following
object DialogPage : Page<DialogPage>() {
val okButton = onView(withText(R.string.ok_button))).inRoot(isDialog())
val cancelButton = onView(withText(R.string.cancel_button))).inRoot(isDialog())
}
...
fun someUserStepInsideSomePage(){
DialogPage.okButton.click()
somePageElement.isDisplayed()
}
Under the hood all espresso Ultron operations are described in UltronEspressoInteraction
class. That is why you just need to extend this class using kotlin extension function, e.g.
fun <T> UltronEspressoInteraction<T>.appendText(text: String) = apply {
executeAction(
operationBlock = getInteractionActionBlock(AppendTextAction(text)),
name = "Append text '$text' to ${getInteractionMatcher()}",
description = "${interaction!!::class.simpleName} APPEND_TEXT to ${getInteractionMatcher()} during $timeoutMs ms",
)
}
AppendTextAction
is a custom ViewAction, smth like that
class AppendTextAction(private val value: String) : ViewAction {
override fun getConstraints() = allOf(isDisplayed(), isAssignableFrom(TextView::class.java))
override fun perform(uiController: UiController, view: View) {
(view as TextView).apply {
this.text = "$text$value"
}
uiController.loopMainThreadUntilIdle()
}
...
}
To make your custom operation 100% native for Ultron framework it's required to add 3 lines more
//support action for all Matcher<View>
fun Matcher<View>.appendText(text: String) = UltronEspressoInteraction(onView(this)).appendText(text)
//support action for all ViewInteractions
fun ViewInteraction.appendText(text: String) = UltronEspressoInteraction(this).appendText(text)
//support action for all DataInteractions
fun DataInteraction.appendText(text: String) = UltronEspressoInteraction(this).appendText(text)
Finally you are able to use this custom operation
withId(R.id.text_input).appendText("some text to append")
View sample code UltronEspressoExt
There are several build in methods that extends Matcher<View>, ViewInteraction, DataInteraction
:
getText() : String?
getContentDescription() : String?
getDrawable() : Drawable?
And you are able to get any other property. There is an example how it could be done - GetTextAction