Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Properly fetch view interaction if it was found under a root #824

Merged
merged 6 commits into from
Oct 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ import io.appium.espressoserver.lib.model.AppiumParams
import io.appium.espressoserver.lib.model.EspressoElement

import androidx.test.espresso.action.ViewActions.clearText
import io.appium.espressoserver.lib.handlers.exceptions.AppiumException
import io.appium.espressoserver.lib.handlers.exceptions.StaleElementException
import io.appium.espressoserver.lib.helpers.getNodeInteractionById

class Clear : RequestHandler<AppiumParams, Unit> {

override fun handleEspresso(params: AppiumParams): Unit {
override fun handleEspresso(params: AppiumParams) {
val viewInteraction = EspressoElement.getViewInteractionById(params.elementId)
try {
viewInteraction.perform(clearText())
Expand All @@ -38,7 +37,7 @@ class Clear : RequestHandler<AppiumParams, Unit> {
}
}

override fun handleCompose(params: AppiumParams): Unit {
override fun handleCompose(params: AppiumParams) {
try {
getNodeInteractionById(params.elementId).performTextClearance()
} catch (e: AssertionError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class ElementEquals : RequestHandler<AppiumParams, Boolean> {
val elementId = params.elementId
val otherElementId = params.getUriParameterValue("otherId")
?: throw InvalidArgumentException("'otherElementId' query parameter not found")
val viewOne = EspressoElement.getViewById(elementId)
val viewTwo = EspressoElement.getViewById(otherElementId)
val viewOne = EspressoElement.getCachedViewStateById(elementId).view
val viewTwo = EspressoElement.getCachedViewStateById(otherElementId).view
return viewOne == viewTwo
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ElementScreenshot : RequestHandler<AppiumParams, String> {

@Throws(AppiumException::class)
override fun handleInternal(params: AppiumParams): String {
val view = EspressoElement.getViewById(params.elementId)
val view = EspressoElement.getCachedViewStateById(params.elementId).view
return ScreenshotsHelper(view).screenshot
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ import io.appium.espressoserver.lib.viewaction.ViewTextGetter

class ElementValue(private val isReplacing: Boolean) : RequestHandler<TextValueParams, Unit> {

override fun handleEspresso(params: TextValueParams): Unit {
override fun handleEspresso(params: TextValueParams) {
val value: String = extractTextToEnter(params)

val elementId = params.elementId
val view = EspressoElement.getViewById(elementId)
val view = EspressoElement.getCachedViewStateById(elementId).view

try {
if (view is ProgressBar) {
Expand Down Expand Up @@ -76,7 +76,7 @@ class ElementValue(private val isReplacing: Boolean) : RequestHandler<TextValueP
}
}

override fun handleCompose(params: TextValueParams): Unit {
override fun handleCompose(params: TextValueParams) {
val value: String = extractTextToEnter(params)
try {
if (isReplacing) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ import io.appium.espressoserver.lib.model.AppiumParams
import io.appium.espressoserver.lib.model.EspressoElement

import io.appium.espressoserver.lib.helpers.ViewFinder.findActive
import io.appium.espressoserver.lib.helpers.ViewState

class FindActive : RequestHandler<AppiumParams, EspressoElement> {
@Throws(AppiumException::class)
override fun handleInternal(params: AppiumParams): EspressoElement {
val view = findActive() ?: throw NoSuchElementException("No elements are currently focused")
return EspressoElement(view)
return EspressoElement(ViewState(view))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class FindElement : RequestHandler<Locator, BaseElement> {
ViewGetter().getView(EspressoElement.getViewInteractionById(it))
}
// Test the selector
val view = ViewFinder.findBy(
val viewState = ViewFinder.findBy(
parentView,
params.using ?: throw InvalidSelectorException("Locator strategy cannot be empty"),
params.value ?: throw InvalidArgumentException()
Expand All @@ -44,7 +44,7 @@ class FindElement : RequestHandler<Locator, BaseElement> {
)

// If we have a match, return success
return EspressoElement(view)
return EspressoElement(viewState)
}

override fun handleCompose(params: Locator): BaseElement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,12 @@ class FindElements : RequestHandler<Locator, List<BaseElement>> {
parentView,
params.using ?: throw InvalidSelectorException("Locator strategy cannot be empty"),
params.value ?: throw InvalidArgumentException()
)
.map { EspressoElement(it) }
).map { EspressoElement(it) }
}

override fun handleCompose(params: Locator): List<BaseElement> {
val nodeInteractions = toNodeInteractionsCollection(params)
return nodeInteractions.fetchSemanticsNodes(false)
.mapIndexed { index, _ -> ComposeElement(nodeInteractions[index]) }
return List(nodeInteractions.fetchSemanticsNodes(false).size)
{ index -> ComposeElement(nodeInteractions[index]) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ class GetAttribute : RequestHandler<AppiumParams, String?> {

private fun getEspressoAttribute(elementId: String, attributeName: String): String? {
val viewElementGetter: () -> ViewElement =
{ ViewElement(EspressoElement.getViewById(elementId)) }
{ ViewElement(EspressoElement.getCachedViewStateById(elementId).view) }
val uncheckedViewElementGetter: () -> ViewElement =
{ ViewElement(EspressoElement.getViewById(elementId, false)) }
{ ViewElement(EspressoElement.getCachedViewStateById(elementId, false).view) }
val viewInteractionGetter: () -> ViewInteraction =
{ EspressoElement.getViewInteractionById(elementId) }
val checkToAttributeValue: (() -> Unit) -> String = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import io.appium.espressoserver.lib.model.ViewElement
class GetDisplayed : RequestHandler<AppiumParams, Boolean> {

override fun handleEspresso(params: AppiumParams): Boolean =
ViewElement(EspressoElement.getViewById(params.elementId, false)).isVisible
ViewElement(EspressoElement.getCachedViewStateById(params.elementId, false).view).isVisible

override fun handleCompose(params: AppiumParams): Boolean =
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import io.appium.espressoserver.lib.model.ComposeNodeElement
class GetEnabled : RequestHandler<AppiumParams, Boolean> {

override fun handleEspresso(params: AppiumParams): Boolean =
ViewElement(EspressoElement.getViewById(params.elementId)).isEnabled
ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view).isEnabled

override fun handleCompose(params: AppiumParams): Boolean =
ComposeNodeElement(getSemanticsNode(params.elementId!!)).isEnabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import io.appium.espressoserver.lib.model.ComposeNodeElement
class GetLocation : RequestHandler<AppiumParams, Location> {

override fun handleEspresso(params: AppiumParams): Location {
val viewElement = ViewElement(EspressoElement.getViewById(params.elementId))
val viewElement = ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view)
return Location(viewElement.bounds.left, viewElement.bounds.top)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import io.appium.espressoserver.lib.model.ViewElement
class GetLocationInView : RequestHandler<AppiumParams, Location> {

override fun handleEspresso(params: AppiumParams): Location {
val viewElement = ViewElement(EspressoElement.getViewById(params.elementId))
val viewElement = ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view)
return Location(viewElement.relativeLeft, viewElement.relativeTop)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import io.appium.espressoserver.lib.model.ComposeNodeElement
class GetName : RequestHandler<AppiumParams, String?> {

override fun handleEspresso(params: AppiumParams): String? {
val view = EspressoElement.getViewById(params.elementId)
return ViewElement(view).contentDescription?.toString()
val viewState = EspressoElement.getCachedViewStateById(params.elementId)
return ViewElement(viewState.view).contentDescription?.toString()
}

override fun handleCompose(params: AppiumParams): String? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import io.appium.espressoserver.lib.model.ViewElement
class GetRect : RequestHandler<AppiumParams, Rect> {

override fun handleEspresso(params: AppiumParams): Rect =
ViewElement(EspressoElement.getViewById(params.elementId)).rect
ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view).rect

override fun handleCompose(params: AppiumParams): Rect =
ComposeNodeElement(getSemanticsNode(params.elementId!!)).rect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import io.appium.espressoserver.lib.model.ViewElement
class GetSelected : RequestHandler<AppiumParams, Boolean> {

override fun handleEspresso(params: AppiumParams): Boolean =
ViewElement(EspressoElement.getViewById(params.elementId)).isSelected
ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view).isSelected

override fun handleCompose(params: AppiumParams): Boolean =
ComposeNodeElement(getSemanticsNode(params.elementId!!)).isSelected
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import io.appium.espressoserver.lib.model.ComposeNodeElement
class GetSize : RequestHandler<AppiumParams, Size> {

override fun handleEspresso(params: AppiumParams): Size {
val bounds = ViewElement(EspressoElement.getViewById(params.elementId)).bounds
val bounds = ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view).bounds
return Size(bounds.width(), bounds.height())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package io.appium.espressoserver.lib.handlers

import androidx.compose.ui.test.performTextInput
import androidx.test.espresso.UiController
import io.appium.espressoserver.lib.handlers.exceptions.AppiumException
import io.appium.espressoserver.lib.handlers.exceptions.InvalidElementStateException
import io.appium.espressoserver.lib.handlers.exceptions.StaleElementException
import io.appium.espressoserver.lib.helpers.getNodeInteractionById
Expand All @@ -37,7 +36,7 @@ import io.appium.espressoserver.lib.model.TextValueParams

class Keys : RequestHandler<TextValueParams, Unit> {

override fun handleEspresso(params: TextValueParams): Unit {
override fun handleEspresso(params: TextValueParams) {
val keys = params.value ?: emptyList()

val runnable = object : UiControllerRunnable<Void?> {
Expand Down Expand Up @@ -81,7 +80,7 @@ class Keys : RequestHandler<TextValueParams, Unit> {
UiControllerPerformer(runnable).run()
}

override fun handleCompose(params: TextValueParams): Unit {
override fun handleCompose(params: TextValueParams) {
try {
val keys = params.value ?: return
keys.forEach {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.appium.espressoserver.lib.handlers

import io.appium.espressoserver.lib.handlers.exceptions.AppiumException
import io.appium.espressoserver.lib.handlers.exceptions.InvalidArgumentException
import io.appium.espressoserver.lib.helpers.ActivityHelpers
import io.appium.espressoserver.lib.helpers.InvocationOperation
Expand All @@ -11,7 +10,6 @@ import io.appium.espressoserver.lib.model.MobileBackdoorParams

class MobileBackdoor : RequestHandler<MobileBackdoorParams, Any?> {

@Throws(AppiumException::class)
override fun handleInternal(params: MobileBackdoorParams): Any? {
params.target?.let {target ->
val activity = ActivityHelpers.currentActivity
Expand All @@ -21,7 +19,9 @@ class MobileBackdoor : RequestHandler<MobileBackdoorParams, Any?> {
val result = when (target) {
InvocationTarget.ACTIVITY -> invokeBackdoorMethods(activity, ops)
InvocationTarget.APPLICATION -> invokeBackdoorMethods(activity.application, ops)
InvocationTarget.ELEMENT -> invokeBackdoorMethods(EspressoElement.getViewById(params.targetElement), ops)
InvocationTarget.ELEMENT -> invokeBackdoorMethods(
EspressoElement.getCachedViewStateById(params.targetElement).view, ops
)
else -> throw InvalidArgumentException("target cannot be '$target'")
} ?: return null

Expand All @@ -38,12 +38,11 @@ class MobileBackdoor : RequestHandler<MobileBackdoorParams, Any?> {
throw InvalidArgumentException("Target must not be empty and must be of type: 'activity', 'application'")
}

@Throws(AppiumException::class)
@Suppress("RedundantNullableReturnType")
private fun invokeBackdoorMethods(invokeOn: Any, ops: List<InvocationOperation>): Any? {
return ops.fold(invokeOn) { invocationTarget, operation -> operation.apply(invocationTarget) }
}

@Throws(InvalidArgumentException::class)
private fun getBackdoorOperations(params: MobileBackdoorParams): List<InvocationOperation> {
return params.methods.map {method ->
InvocationOperation(method.name, method.arguments, method.argumentTypes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import io.appium.espressoserver.lib.model.EspressoElement
import io.appium.espressoserver.lib.model.MobileClickActionParams
import io.appium.espressoserver.lib.viewaction.UiControllerPerformer
import io.appium.espressoserver.lib.viewaction.UiControllerRunnable
import io.appium.espressoserver.lib.viewaction.ViewGetter

class MobileClickAction : RequestHandler<MobileClickActionParams, Void?> {

Expand All @@ -37,7 +38,8 @@ class MobileClickAction : RequestHandler<MobileClickActionParams, Void?> {
params.inputDevice,
params.buttonState
)
clickAction.perform(uiController, EspressoElement.getViewById(params.elementId))
val viewInteraction = EspressoElement.getViewInteractionById(params.elementId)
clickAction.perform(uiController, ViewGetter().getView(viewInteraction))

return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import io.appium.espressoserver.lib.model.MobileSwipeParams
import io.appium.espressoserver.lib.model.MobileSwipeParams.Direction.*
import io.appium.espressoserver.lib.viewaction.UiControllerPerformer
import io.appium.espressoserver.lib.viewaction.UiControllerRunnable
import io.appium.espressoserver.lib.viewaction.ViewGetter

class MobileSwipe : RequestHandler<MobileSwipeParams, Void?> {

Expand Down Expand Up @@ -59,7 +60,7 @@ class MobileSwipe : RequestHandler<MobileSwipeParams, Void?> {
swiper=[${params.swiper}] startCoordinates=[${params.startCoordinates}]
endCoordinates=[${params.endCoordinates}] precisionDescriber=[${params.precisionDescriber}]
""".trimIndent())
swipeAction.perform(uiController, EspressoElement.getViewById(params.elementId))
swipeAction.perform(uiController, ViewGetter().getView(viewInteraction))
return null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class MobileViewFlash : RequestHandler<ViewFlashParams, Void?> {
val duration = params.durationMillis
val repeatCount = params.repeatCount

val view = EspressoElement.getViewById(params.elementId)
val view = EspressoElement.getCachedViewStateById(params.elementId).view
val latch = CountDownLatch(1)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
val animation = AlphaAnimation(1f, 0f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class PointerEventHandler(private val touchType: TouchType) : RequestHandler<Mot
var startY = displayMetrics.heightPixels / 2 - params.y / 2

params.targetElement?.let {
val view = EspressoElement.getViewById(it)
val view = EspressoElement.getCachedViewStateById(it).view
val viewElement = ViewElement(view)
startX = viewElement.bounds.left.toLong()
startY = viewElement.bounds.top.toLong()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class WebAtoms : RequestHandler<WebAtomsParams, Void?> {
// Initialize onWebView with web view matcher (if webviewEl provided)
params.webviewElement.let{ webviewElement ->
AndroidLogger.info("Initializing webView interaction on webview with el: '$webviewElement'")
val matcher = withView(EspressoElement.getViewById(webviewElement))
val viewState = EspressoElement.getCachedViewStateById(webviewElement)
val matcher = withView(viewState.view)
webViewInteraction = onWebView(matcher)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,22 @@ package io.appium.espressoserver.lib.helpers
import android.util.LruCache
import android.view.View
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.test.espresso.Root
import org.hamcrest.Matcher

const val MAX_CACHE_SIZE = 500

data class ViewState(val view: View, val initialContentDescription: CharSequence?)
data class ViewState(
val view: View,
val initialContentDescription: CharSequence? = view.contentDescription,
val rootMatcher: Matcher<Root>? = null
)

object EspressoViewsCache {
private val cache = LruCache<String, ViewState>(MAX_CACHE_SIZE)

fun put(id: String, view: View) {
cache.put(id, ViewState(view, view.contentDescription))
fun put(id: String, viewState: ViewState) {
cache.put(id, viewState)
}

fun get(id: String): ViewState? = cache.get(id)
Expand Down
Loading