appium-espresso-driver 2.13.2 → 2.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,7 @@ import io.appium.espressoserver.lib.handlers.exceptions.StaleElementException
26
26
  import io.appium.espressoserver.lib.model.Locator
27
27
  import io.appium.espressoserver.lib.model.SourceDocument
28
28
  import io.appium.espressoserver.lib.model.AttributesEnum
29
+ import io.appium.espressoserver.lib.model.compileXpathExpression
29
30
  import io.appium.espressoserver.lib.viewmatcher.fetchIncludedAttributes
30
31
 
31
32
  /**
@@ -63,9 +64,12 @@ fun semanticsMatcherForLocator(locator: Locator): SemanticsMatcher =
63
64
  }
64
65
 
65
66
  private fun hasXpath(locator: Locator): SemanticsMatcher {
67
+ val xpath = locator.value!!
68
+ val expression = compileXpathExpression(xpath)
69
+ val attributes = fetchIncludedAttributes(xpath)
66
70
  val matchingIds = SourceDocument(
67
- locator.elementId?.let { getSemanticsNode(it) }, fetchIncludedAttributes(locator.value!!)
68
- ).matchingNodeIds(locator.value!!, AttributesEnum.RESOURCE_ID.toString())
71
+ locator.elementId?.let { getSemanticsNode(it) }, attributes
72
+ ).findMatchingNodeIds(expression, AttributesEnum.RESOURCE_ID.toString())
69
73
 
70
74
  return SemanticsMatcher("Matches Xpath ${locator.value}") {
71
75
  matchingIds.contains(it.id)
@@ -80,6 +80,12 @@ private fun toXmlNodeName(className: String?): String {
80
80
  return fixedName
81
81
  }
82
82
 
83
+ fun compileXpathExpression(selector: String): XPathExpression =
84
+ try {
85
+ XPATH.compile(selector)
86
+ } catch (xe: XPathExpressionException) {
87
+ throw XPathLookupException(selector, xe.message!!)
88
+ }
83
89
 
84
90
  class SourceDocument constructor(
85
91
  private val root: Any? = null,
@@ -324,8 +330,8 @@ class SourceDocument constructor(
324
330
  throw AppiumException(lastError!!)
325
331
  }
326
332
 
327
- private fun rootSemanticNodes(): List<SemanticsNode> {
328
- return try {
333
+ private fun rootSemanticNodes(): List<SemanticsNode> =
334
+ try {
329
335
  listOf(EspressoServerRunnerTest.composeTestRule.onRoot(useUnmergedTree = true).fetchSemanticsNode())
330
336
  } catch (e: AssertionError) {
331
337
  // Ideally there should be on `root` node but on some cases e.g:overlays screen, there can be more than 1 root.
@@ -337,7 +343,6 @@ class SourceDocument constructor(
337
343
  } as SelectionResult
338
344
  result.selectedNodes
339
345
  }
340
- }
341
346
 
342
347
  private fun performCleanup() {
343
348
  tmpXmlName?.let {
@@ -346,8 +351,8 @@ class SourceDocument constructor(
346
351
  }
347
352
  }
348
353
 
349
- fun toXMLString(): String {
350
- return RESOURCES_GUARD.withPermit({
354
+ fun toXMLString(): String =
355
+ RESOURCES_GUARD.withPermit({
351
356
  toStream().use { xmlStream ->
352
357
  val sb = StringBuilder()
353
358
  val reader = BufferedReader(InputStreamReader(xmlStream, XML_ENCODING))
@@ -359,24 +364,17 @@ class SourceDocument constructor(
359
364
  sb.toString()
360
365
  }
361
366
  }, { performCleanup() })
362
- }
363
367
 
364
- fun findViewsByXPath(xpathSelector: String): List<View> =
365
- matchingNodeIds(xpathSelector, VIEW_INDEX).map { viewMap.get(it) }
368
+ fun findViewsByXPath(expression: XPathExpression): List<View> =
369
+ findMatchingNodeIds(expression, VIEW_INDEX).map { viewMap.get(it) }
366
370
 
367
- fun matchingNodeIds(xpathSelector: String, attributeName: String): List<Int> {
368
- val expr = try {
369
- XPATH.compile(xpathSelector)
370
- } catch (xe: XPathExpressionException) {
371
- throw XPathLookupException(xpathSelector, xe.message!!)
372
- }
373
- return RESOURCES_GUARD.withPermit({
371
+ fun findMatchingNodeIds(expression: XPathExpression, attributeName: String): List<Int> =
372
+ RESOURCES_GUARD.withPermit({
374
373
  toStream().use { xmlStream ->
375
- val list = expr.evaluate(InputSource(xmlStream), XPathConstants.NODESET) as NodeList
374
+ val list = expression.evaluate(InputSource(xmlStream), XPathConstants.NODESET) as NodeList
376
375
  (0 until list.length).map { index ->
377
376
  list.item(index).attributes.getNamedItem(attributeName).nodeValue.toInt()
378
377
  }
379
378
  }
380
379
  }, { performCleanup() })
381
- }
382
380
  }
@@ -16,12 +16,15 @@
16
16
  package io.appium.espressoserver.lib.viewmatcher
17
17
 
18
18
  import android.view.View
19
+ import io.appium.espressoserver.lib.helpers.extensions.withPermit
19
20
  import io.appium.espressoserver.lib.model.SourceDocument
20
21
  import io.appium.espressoserver.lib.model.AttributesEnum
21
22
  import io.appium.espressoserver.lib.model.EspressoAttributes
23
+ import io.appium.espressoserver.lib.model.compileXpathExpression
22
24
  import org.hamcrest.Description
23
25
  import org.hamcrest.Matcher
24
26
  import org.hamcrest.TypeSafeMatcher
27
+ import java.util.concurrent.Semaphore
25
28
 
26
29
  fun fetchIncludedAttributes(xpath: String): Set<AttributesEnum>? {
27
30
  if (xpath.contains("@*")) {
@@ -37,13 +40,24 @@ fun fetchIncludedAttributes(xpath: String): Set<AttributesEnum>? {
37
40
  }
38
41
 
39
42
  fun withXPath(root: View?, xpath: String, index: Int? = null): Matcher<View> {
43
+ val expression = compileXpathExpression(xpath)
44
+ val attributes = fetchIncludedAttributes(xpath)
40
45
  val matchedXPathViews = mutableListOf<View>()
46
+ var didLookup = false
47
+ val lookupGuard = Semaphore(1)
41
48
  return object : TypeSafeMatcher<View>() {
42
49
  override fun matchesSafely(item: View): Boolean {
50
+ lookupGuard.withPermit {
51
+ if (!didLookup) {
52
+ matchedXPathViews.addAll(
53
+ SourceDocument(root ?: item.rootView, attributes).findViewsByXPath(expression)
54
+ )
55
+ didLookup = true
56
+ }
57
+ }
58
+
43
59
  if (matchedXPathViews.isEmpty()) {
44
- matchedXPathViews.addAll(
45
- SourceDocument(root ?: item.rootView, fetchIncludedAttributes(xpath)).findViewsByXPath(xpath)
46
- )
60
+ return false
47
61
  }
48
62
 
49
63
  return if (index != null) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "appium-espresso-driver",
3
- "version": "2.13.2",
3
+ "version": "2.13.3",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "appium-espresso-driver",
9
- "version": "2.13.2",
9
+ "version": "2.13.3",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@babel/runtime": "^7.4.3",
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "automated testing",
8
8
  "android"
9
9
  ],
10
- "version": "2.13.2",
10
+ "version": "2.13.3",
11
11
  "author": "Appium Contributors",
12
12
  "license": "Apache-2.0",
13
13
  "repository": {