Skip to content

Commit deeb296

Browse files
fix: Properly fetch view interaction if it was found under a root (#824)
1 parent 2c560fc commit deeb296

27 files changed

+121
-97
lines changed

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/Clear.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,12 @@ import io.appium.espressoserver.lib.model.AppiumParams
2323
import io.appium.espressoserver.lib.model.EspressoElement
2424

2525
import androidx.test.espresso.action.ViewActions.clearText
26-
import io.appium.espressoserver.lib.handlers.exceptions.AppiumException
2726
import io.appium.espressoserver.lib.handlers.exceptions.StaleElementException
2827
import io.appium.espressoserver.lib.helpers.getNodeInteractionById
2928

3029
class Clear : RequestHandler<AppiumParams, Unit> {
3130

32-
override fun handleEspresso(params: AppiumParams): Unit {
31+
override fun handleEspresso(params: AppiumParams) {
3332
val viewInteraction = EspressoElement.getViewInteractionById(params.elementId)
3433
try {
3534
viewInteraction.perform(clearText())
@@ -38,7 +37,7 @@ class Clear : RequestHandler<AppiumParams, Unit> {
3837
}
3938
}
4039

41-
override fun handleCompose(params: AppiumParams): Unit {
40+
override fun handleCompose(params: AppiumParams) {
4241
try {
4342
getNodeInteractionById(params.elementId).performTextClearance()
4443
} catch (e: AssertionError) {

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/ElementEquals.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ class ElementEquals : RequestHandler<AppiumParams, Boolean> {
1212
val elementId = params.elementId
1313
val otherElementId = params.getUriParameterValue("otherId")
1414
?: throw InvalidArgumentException("'otherElementId' query parameter not found")
15-
val viewOne = EspressoElement.getViewById(elementId)
16-
val viewTwo = EspressoElement.getViewById(otherElementId)
15+
val viewOne = EspressoElement.getCachedViewStateById(elementId).view
16+
val viewTwo = EspressoElement.getCachedViewStateById(otherElementId).view
1717
return viewOne == viewTwo
1818
}
1919
}

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/ElementScreenshot.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ElementScreenshot : RequestHandler<AppiumParams, String> {
2525

2626
@Throws(AppiumException::class)
2727
override fun handleInternal(params: AppiumParams): String {
28-
val view = EspressoElement.getViewById(params.elementId)
28+
val view = EspressoElement.getCachedViewStateById(params.elementId).view
2929
return ScreenshotsHelper(view).screenshot
3030
}
3131
}

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/ElementValue.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ import io.appium.espressoserver.lib.viewaction.ViewTextGetter
3535

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

38-
override fun handleEspresso(params: TextValueParams): Unit {
38+
override fun handleEspresso(params: TextValueParams) {
3939
val value: String = extractTextToEnter(params)
4040

4141
val elementId = params.elementId
42-
val view = EspressoElement.getViewById(elementId)
42+
val view = EspressoElement.getCachedViewStateById(elementId).view
4343

4444
try {
4545
if (view is ProgressBar) {
@@ -76,7 +76,7 @@ class ElementValue(private val isReplacing: Boolean) : RequestHandler<TextValueP
7676
}
7777
}
7878

79-
override fun handleCompose(params: TextValueParams): Unit {
79+
override fun handleCompose(params: TextValueParams) {
8080
val value: String = extractTextToEnter(params)
8181
try {
8282
if (isReplacing) {

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/FindActive.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ import io.appium.espressoserver.lib.model.AppiumParams
2222
import io.appium.espressoserver.lib.model.EspressoElement
2323

2424
import io.appium.espressoserver.lib.helpers.ViewFinder.findActive
25+
import io.appium.espressoserver.lib.helpers.ViewState
2526

2627
class FindActive : RequestHandler<AppiumParams, EspressoElement> {
2728
@Throws(AppiumException::class)
2829
override fun handleInternal(params: AppiumParams): EspressoElement {
2930
val view = findActive() ?: throw NoSuchElementException("No elements are currently focused")
30-
return EspressoElement(view)
31+
return EspressoElement(ViewState(view))
3132
}
3233
}

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/FindElement.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class FindElement : RequestHandler<Locator, BaseElement> {
3131
ViewGetter().getView(EspressoElement.getViewInteractionById(it))
3232
}
3333
// Test the selector
34-
val view = ViewFinder.findBy(
34+
val viewState = ViewFinder.findBy(
3535
parentView,
3636
params.using ?: throw InvalidSelectorException("Locator strategy cannot be empty"),
3737
params.value ?: throw InvalidArgumentException()
@@ -44,7 +44,7 @@ class FindElement : RequestHandler<Locator, BaseElement> {
4444
)
4545

4646
// If we have a match, return success
47-
return EspressoElement(view)
47+
return EspressoElement(viewState)
4848
}
4949

5050
override fun handleCompose(params: Locator): BaseElement {

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/FindElements.kt

+3-4
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,12 @@ class FindElements : RequestHandler<Locator, List<BaseElement>> {
3737
parentView,
3838
params.using ?: throw InvalidSelectorException("Locator strategy cannot be empty"),
3939
params.value ?: throw InvalidArgumentException()
40-
)
41-
.map { EspressoElement(it) }
40+
).map { EspressoElement(it) }
4241
}
4342

4443
override fun handleCompose(params: Locator): List<BaseElement> {
4544
val nodeInteractions = toNodeInteractionsCollection(params)
46-
return nodeInteractions.fetchSemanticsNodes(false)
47-
.mapIndexed { index, _ -> ComposeElement(nodeInteractions[index]) }
45+
return List(nodeInteractions.fetchSemanticsNodes(false).size)
46+
{ index -> ComposeElement(nodeInteractions[index]) }
4847
}
4948
}

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetAttribute.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ class GetAttribute : RequestHandler<AppiumParams, String?> {
5252

5353
private fun getEspressoAttribute(elementId: String, attributeName: String): String? {
5454
val viewElementGetter: () -> ViewElement =
55-
{ ViewElement(EspressoElement.getViewById(elementId)) }
55+
{ ViewElement(EspressoElement.getCachedViewStateById(elementId).view) }
5656
val uncheckedViewElementGetter: () -> ViewElement =
57-
{ ViewElement(EspressoElement.getViewById(elementId, false)) }
57+
{ ViewElement(EspressoElement.getCachedViewStateById(elementId, false).view) }
5858
val viewInteractionGetter: () -> ViewInteraction =
5959
{ EspressoElement.getViewInteractionById(elementId) }
6060
val checkToAttributeValue: (() -> Unit) -> String = {

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetDisplayed.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import io.appium.espressoserver.lib.model.ViewElement
2525
class GetDisplayed : RequestHandler<AppiumParams, Boolean> {
2626

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

3030
override fun handleCompose(params: AppiumParams): Boolean =
3131
try {

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetEnabled.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import io.appium.espressoserver.lib.model.ComposeNodeElement
2525
class GetEnabled : RequestHandler<AppiumParams, Boolean> {
2626

2727
override fun handleEspresso(params: AppiumParams): Boolean =
28-
ViewElement(EspressoElement.getViewById(params.elementId)).isEnabled
28+
ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view).isEnabled
2929

3030
override fun handleCompose(params: AppiumParams): Boolean =
3131
ComposeNodeElement(getSemanticsNode(params.elementId!!)).isEnabled

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetLocation.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import io.appium.espressoserver.lib.model.ComposeNodeElement
2626
class GetLocation : RequestHandler<AppiumParams, Location> {
2727

2828
override fun handleEspresso(params: AppiumParams): Location {
29-
val viewElement = ViewElement(EspressoElement.getViewById(params.elementId))
29+
val viewElement = ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view)
3030
return Location(viewElement.bounds.left, viewElement.bounds.top)
3131
}
3232

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetLocationInView.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import io.appium.espressoserver.lib.model.ViewElement
2424
class GetLocationInView : RequestHandler<AppiumParams, Location> {
2525

2626
override fun handleEspresso(params: AppiumParams): Location {
27-
val viewElement = ViewElement(EspressoElement.getViewById(params.elementId))
27+
val viewElement = ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view)
2828
return Location(viewElement.relativeLeft, viewElement.relativeTop)
2929
}
3030
}

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetName.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ import io.appium.espressoserver.lib.model.ComposeNodeElement
2525
class GetName : RequestHandler<AppiumParams, String?> {
2626

2727
override fun handleEspresso(params: AppiumParams): String? {
28-
val view = EspressoElement.getViewById(params.elementId)
29-
return ViewElement(view).contentDescription?.toString()
28+
val viewState = EspressoElement.getCachedViewStateById(params.elementId)
29+
return ViewElement(viewState.view).contentDescription?.toString()
3030
}
3131

3232
override fun handleCompose(params: AppiumParams): String? {

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetRect.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import io.appium.espressoserver.lib.model.ViewElement
2626
class GetRect : RequestHandler<AppiumParams, Rect> {
2727

2828
override fun handleEspresso(params: AppiumParams): Rect =
29-
ViewElement(EspressoElement.getViewById(params.elementId)).rect
29+
ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view).rect
3030

3131
override fun handleCompose(params: AppiumParams): Rect =
3232
ComposeNodeElement(getSemanticsNode(params.elementId!!)).rect

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetSelected.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import io.appium.espressoserver.lib.model.ViewElement
2626
class GetSelected : RequestHandler<AppiumParams, Boolean> {
2727

2828
override fun handleEspresso(params: AppiumParams): Boolean =
29-
ViewElement(EspressoElement.getViewById(params.elementId)).isSelected
29+
ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view).isSelected
3030

3131
override fun handleCompose(params: AppiumParams): Boolean =
3232
ComposeNodeElement(getSemanticsNode(params.elementId!!)).isSelected

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetSize.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import io.appium.espressoserver.lib.model.ComposeNodeElement
2626
class GetSize : RequestHandler<AppiumParams, Size> {
2727

2828
override fun handleEspresso(params: AppiumParams): Size {
29-
val bounds = ViewElement(EspressoElement.getViewById(params.elementId)).bounds
29+
val bounds = ViewElement(EspressoElement.getCachedViewStateById(params.elementId).view).bounds
3030
return Size(bounds.width(), bounds.height())
3131
}
3232

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/Keys.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package io.appium.espressoserver.lib.handlers
1818

1919
import androidx.compose.ui.test.performTextInput
2020
import androidx.test.espresso.UiController
21-
import io.appium.espressoserver.lib.handlers.exceptions.AppiumException
2221
import io.appium.espressoserver.lib.handlers.exceptions.InvalidElementStateException
2322
import io.appium.espressoserver.lib.handlers.exceptions.StaleElementException
2423
import io.appium.espressoserver.lib.helpers.getNodeInteractionById
@@ -37,7 +36,7 @@ import io.appium.espressoserver.lib.model.TextValueParams
3736

3837
class Keys : RequestHandler<TextValueParams, Unit> {
3938

40-
override fun handleEspresso(params: TextValueParams): Unit {
39+
override fun handleEspresso(params: TextValueParams) {
4140
val keys = params.value ?: emptyList()
4241

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

84-
override fun handleCompose(params: TextValueParams): Unit {
83+
override fun handleCompose(params: TextValueParams) {
8584
try {
8685
val keys = params.value ?: return
8786
keys.forEach {

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/MobileBackdoor.kt

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.appium.espressoserver.lib.handlers
22

3-
import io.appium.espressoserver.lib.handlers.exceptions.AppiumException
43
import io.appium.espressoserver.lib.handlers.exceptions.InvalidArgumentException
54
import io.appium.espressoserver.lib.helpers.ActivityHelpers
65
import io.appium.espressoserver.lib.helpers.InvocationOperation
@@ -11,7 +10,6 @@ import io.appium.espressoserver.lib.model.MobileBackdoorParams
1110

1211
class MobileBackdoor : RequestHandler<MobileBackdoorParams, Any?> {
1312

14-
@Throws(AppiumException::class)
1513
override fun handleInternal(params: MobileBackdoorParams): Any? {
1614
params.target?.let {target ->
1715
val activity = ActivityHelpers.currentActivity
@@ -21,7 +19,9 @@ class MobileBackdoor : RequestHandler<MobileBackdoorParams, Any?> {
2119
val result = when (target) {
2220
InvocationTarget.ACTIVITY -> invokeBackdoorMethods(activity, ops)
2321
InvocationTarget.APPLICATION -> invokeBackdoorMethods(activity.application, ops)
24-
InvocationTarget.ELEMENT -> invokeBackdoorMethods(EspressoElement.getViewById(params.targetElement), ops)
22+
InvocationTarget.ELEMENT -> invokeBackdoorMethods(
23+
EspressoElement.getCachedViewStateById(params.targetElement).view, ops
24+
)
2525
else -> throw InvalidArgumentException("target cannot be '$target'")
2626
} ?: return null
2727

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

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

46-
@Throws(InvalidArgumentException::class)
4746
private fun getBackdoorOperations(params: MobileBackdoorParams): List<InvocationOperation> {
4847
return params.methods.map {method ->
4948
InvocationOperation(method.name, method.arguments, method.argumentTypes)

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/MobileClickAction.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import io.appium.espressoserver.lib.model.EspressoElement
2323
import io.appium.espressoserver.lib.model.MobileClickActionParams
2424
import io.appium.espressoserver.lib.viewaction.UiControllerPerformer
2525
import io.appium.espressoserver.lib.viewaction.UiControllerRunnable
26+
import io.appium.espressoserver.lib.viewaction.ViewGetter
2627

2728
class MobileClickAction : RequestHandler<MobileClickActionParams, Void?> {
2829

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

4244
return null
4345
}

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/MobileSwipe.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import io.appium.espressoserver.lib.model.MobileSwipeParams
2727
import io.appium.espressoserver.lib.model.MobileSwipeParams.Direction.*
2828
import io.appium.espressoserver.lib.viewaction.UiControllerPerformer
2929
import io.appium.espressoserver.lib.viewaction.UiControllerRunnable
30+
import io.appium.espressoserver.lib.viewaction.ViewGetter
3031

3132
class MobileSwipe : RequestHandler<MobileSwipeParams, Void?> {
3233

@@ -59,7 +60,7 @@ class MobileSwipe : RequestHandler<MobileSwipeParams, Void?> {
5960
swiper=[${params.swiper}] startCoordinates=[${params.startCoordinates}]
6061
endCoordinates=[${params.endCoordinates}] precisionDescriber=[${params.precisionDescriber}]
6162
""".trimIndent())
62-
swipeAction.perform(uiController, EspressoElement.getViewById(params.elementId))
63+
swipeAction.perform(uiController, ViewGetter().getView(viewInteraction))
6364
return null
6465
}
6566
}

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/MobileViewFlash.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class MobileViewFlash : RequestHandler<ViewFlashParams, Void?> {
1717
val duration = params.durationMillis
1818
val repeatCount = params.repeatCount
1919

20-
val view = EspressoElement.getViewById(params.elementId)
20+
val view = EspressoElement.getCachedViewStateById(params.elementId).view
2121
val latch = CountDownLatch(1)
2222
InstrumentationRegistry.getInstrumentation().runOnMainSync {
2323
val animation = AlphaAnimation(1f, 0f)

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/PointerEventHandler.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class PointerEventHandler(private val touchType: TouchType) : RequestHandler<Mot
124124
var startY = displayMetrics.heightPixels / 2 - params.y / 2
125125

126126
params.targetElement?.let {
127-
val view = EspressoElement.getViewById(it)
127+
val view = EspressoElement.getCachedViewStateById(it).view
128128
val viewElement = ViewElement(view)
129129
startX = viewElement.bounds.left.toLong()
130130
startY = viewElement.bounds.top.toLong()

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/WebAtoms.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class WebAtoms : RequestHandler<WebAtomsParams, Void?> {
4040
// Initialize onWebView with web view matcher (if webviewEl provided)
4141
params.webviewElement.let{ webviewElement ->
4242
AndroidLogger.info("Initializing webView interaction on webview with el: '$webviewElement'")
43-
val matcher = withView(EspressoElement.getViewById(webviewElement))
43+
val viewState = EspressoElement.getCachedViewStateById(webviewElement)
44+
val matcher = withView(viewState.view)
4445
webViewInteraction = onWebView(matcher)
4546
}
4647

Diff for: espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/helpers/EspressoViewsCache.kt

+9-3
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,22 @@ package io.appium.espressoserver.lib.helpers
1919
import android.util.LruCache
2020
import android.view.View
2121
import androidx.compose.ui.test.SemanticsNodeInteraction
22+
import androidx.test.espresso.Root
23+
import org.hamcrest.Matcher
2224

2325
const val MAX_CACHE_SIZE = 500
2426

25-
data class ViewState(val view: View, val initialContentDescription: CharSequence?)
27+
data class ViewState(
28+
val view: View,
29+
val initialContentDescription: CharSequence? = view.contentDescription,
30+
val rootMatcher: Matcher<Root>? = null
31+
)
2632

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

30-
fun put(id: String, view: View) {
31-
cache.put(id, ViewState(view, view.contentDescription))
36+
fun put(id: String, viewState: ViewState) {
37+
cache.put(id, viewState)
3238
}
3339

3440
fun get(id: String): ViewState? = cache.get(id)

0 commit comments

Comments
 (0)