feature request: Selected Element Find By xpath
dazhuangge opened this issue · 11 comments
Current Behavior
App Version:2024.3.1
Selected Element Find By xpath is not full xpath , sometimes we need it.
Suggested Solution
Selected Element add Find By full xpath
like this
/hierarchy/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.widget.FrameLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout[2]/android.view.ViewGroup/android.widget.FrameLayout[1]/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.TextView
Additional Information
No response
I guess it shouldn't be too difficult to add, but I don't see a reason why it would be needed, could you please give an example?
Out of curiosity I checked the dev tools of both Chrome and Firefox - Chrome does allow copying the full xpath, but it does not offer a 'recommended' xpath. Meanwhile Firefox seems to be the opposite.
it is logically incorrect to attempt to locate this widget and retrieve the actual result solely by searching for "蓝牙" as the text
I don't understand this point. The suggested xpath shown by the Inspector is always unique, so in this case //android.widget.TextView[@resource-id="com.android.settings:id/title" and @text="蓝牙"]
is guaranteed to find exactly this one element. If the element does not have other unique attributes, the suggested xpath will change accordingly. If there are no attributes at all, the suggested xpath is likely going to resemble the full xpath. The Inspector tries to avoid using the full path wherever possible, because it is more brittle than using the element's attributes.
if there is a bug text="黑牙",but the expected result is text="蓝牙"
If you expect to find 蓝牙, but the text is actually 黑牙, and therefore the test fails, it sounds like the test is working correctly, because it caught a bug. Or do you mean that both texts should be accepted in your case?
In this case
Expected result:element.text = '黑牙'
Actual result:element.text = '蓝牙'
Now i can find the element by "/hierarchy/..." and there is a bug was found.
But i can not find the element by '//android.widget.TextView[@resource-id="com.android.settings:id/title" and @text="黑牙"]' and the program was terminated.
_2024_03_11_16_21_10_857.mp4
"""
open the "settings“ app, Check if the text of the element is "黑牙"
Expected result:element.text = '黑牙'
Actual result:element.text = '蓝牙'
Now i can find the element by
'/hierarchy/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[
2]/android.widget.FrameLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout[
1]/android.view.ViewGroup/android.widget.FrameLayout[
2]/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.RelativeLayout
/android.widget.TextView' and there is a bug was found.
but i can not find the element by '//android.widget.TextView[@resource-id="com.android.settings:id/title" and @text="黑牙"]'
because there is a bug that actual result:element.text = '蓝牙' and the program was terminated.
"""
import time
from appium import webdriver
class TestAppDemo:
def setup_method(self):
# 准备appium url
url = 'http://127.0.0.1:4723/wd/hub'
# 准备参数 desired_caps字典
data = {
'platformName': 'Android', # 平台名
'deviceName': 'x86_64', # 设备名
'appPackage': 'com.android.settings', # app包名
'appActivity': '.Settings' # app窗口名
}
# 创建驱动,启动app
self.driver = webdriver.Remote(url, data)
time.sleep(5)
def teardown_method(self):
# 关闭驱动,同时关闭APP
self.driver.quit()
def test_app_demo(self):
# 定位蓝牙,并点击
element = self.driver.find_element('xpath',
'//android.widget.TextView[@resource-id="com.android.settings:id/title" and @text="黑牙"]')
# element = self.driver.find_element('xpath',
# '/hierarchy/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.widget.FrameLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout[1]/android.view.ViewGroup/android.widget.FrameLayout[2]/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.TextView')
time.sleep(2)
assert element.text == '黑牙'
Ok, so if I understand correctly, your problem isn't that the test fails, but that it fails with a different error than the one you expect from the assert
call. Then yes, you would want to use a different xpath that does not include the text value.
But that does not mean that the Inspector's provided xpath should be changed. The Inspector's xpaths are only suggestions, and aim to provide the most robust way to identify the element with minimal reference to other elements, which is achieved by relying the element's attributes. The fact that the resulting xpath does not apply for your use case is... not really an issue with the Inspector.
Even if this was changed, using the full xpath is also not a good solution - like I said before, such xpaths are very fragile and should be avoided wherever possible. In this case I would simply not use the suggested xpath and instead create my own - I have had this situation multiple times myself and that is the approach I used. You can do this e.g. by using other element attributes, or by relying on the nearest parent element.
_2024_03_11_20_05_45_231.mp4
小 | 保持横向 | |||
---|---|---|---|---|
Attribute | Value | Attribute | Value | 小.Attribute.Value = 保持横向.Attribute.Value? |
elementId | 00000000-0000-0023-7fff-ffff00000208 | elementId | 00000000-0000-0023-7fff-ffff00000205 | 0 |
index | 1 | index | 1 | 1 |
package | com.android.settings | package | com.android.settings | 1 |
class | android.widget.TextView | class | android.widget.TextView | 1 |
text | 小 | text | 保持横向 | 0 |
resource-id | android:id/summary | resource-id | android:id/summary | 1 |
checkable | FALSE | checkable | FALSE | 1 |
checked | FALSE | checked | FALSE | 1 |
clickable | FALSE | clickable | FALSE | 1 |
enabled | TRUE | enabled | TRUE | 1 |
focusable | FALSE | focusable | FALSE | 1 |
focused | FALSE | focused | FALSE | 1 |
long-clickable | FALSE | long-clickable | FALSE | 1 |
password | FALSE | password | FALSE | 1 |
scrollable | FALSE | scrollable | FALSE | 1 |
selected | FALSE | selected | FALSE | 1 |
bounds | [23,476][40,499] | bounds | [23,574][91,597] | 0 |
displayed | TRUE | displayed | TRUE | 1 |
"""
"""
import time
from appium import webdriver
class TestAppDemo2:
def setup_method(self):
# 准备appium url
url = 'http://127.0.0.1:4723/wd/hub'
# 准备参数 desired_caps字典
data = {
'platformName': 'Android', # 平台名
'deviceName': 'x86_64', # 设备名
'appPackage': 'com.android.settings', # app包名
'appActivity': '.Settings' # app窗口名
}
# 创建驱动,启动app
self.driver = webdriver.Remote(url, data)
time.sleep(5)
def teardown_method(self):
# 关闭驱动,同时关闭APP
self.driver.quit()
def test_app_demo_2(self):
self.driver.find_element('xpath', '//*[@text="显示"]').click()
time.sleep(2)
element = self.driver.find_element('xpath', '//android.widget.TextView[@resource-id="android:id/summary" and @elementId="00000000-0000-0023-7fff-ffff00000208"]')
print(element.text)
time.sleep(2)
assert element.text == '小'
in this case the element which text = "小" and the element which text = "保持横向 " .They have the same attributes, except for elementId,text,bounds.Now we should use the full xpath.
I only gave examples on how you can assemble xpaths, the actual implementation depends on your application code. If you have questions on how to create xpaths, please refer to online guides or ask on the Appium Forum. This is not a bug and the question is outside the scope of the Inspector.
I only gave examples on how you can assemble xpaths, the actual implementation depends on your application code. If you have questions on how to create xpaths, please refer to online guides or ask on the Appium Forum. This is not a bug and the question is outside the scope of the Inspector.
Certainly, this is not a bug.
Sometimes, there's no other way to locate an element except using an full xpath that starts from the root node, as the attributes of the targeted element are not unique.
I've noticed that another person also has a similar need on the Appium Forum.
Get FULL Xpath from Appium Inspector
Maybe my personal experience is limited, but I have never needed a full xpath from the root node. In situations like yours I usually look for combinations based on the element class names and indices:
- any parent node with a unique class
- unique combination of parent class + child class
- unique combination of siblings
- use indices if it's not possible to retrieve a fully unique combination
Any of these approaches will be less brittle than the full xpath.