Converts Appium Android XPath locators to UiSelector (-android uiautomator): exact conversions only, the rest skipped with a reason; checked on an emulator
翻译准备中
正在加载完整 README。
查看完整 README ↗Converts Appium XPath locators in your Android tests to UiAutomator2 UiSelector locators
(-android uiautomator), and refuses to convert the ones that have no exact UiSelector.
UiSelector was faster than XPath in 30 of 30 benchmark cases and has no 70-level depth limit
(benchmark).
These are the versions of the Sauce Labs image appium3-2026-10. A quarterly upstream watch
opens an issue when the next Sauce Labs image brings a driver or server that changes a file the conversion rules
depend on. Full environment: docs/environment.md.
Nothing to install. The bare command only previews; review the diff, then add --write.
npx xpath-to-uiselector ./tests # preview
npx xpath-to-uiselector ./tests --write # save it
npx xpath-to-uiselector ./tests --json # report with every skip and warning
Three modes, from strictest to widest; each previews until you add --write:
| Mode | Command | Writes | Converts* |
|---|---|---|---|
| Exact only | npx xpath-to-uiselector ./tests --exact | only conversions that find the same elements as the XPath | 64 % |
| Default | npx xpath-to-uiselector ./tests | exact ones, and close ones with a warning (//A//B → childSelector) | 73 % |
| Loose | npx xpath-to-uiselector ./tests --loose | also simpler, wider forms with a warning: case-insensitive textContains / textStartsWith, //A/B → childSelector | 77 % |
* of 1645 real Android XPaths. The rest is skipped with a reason. --exact wins over --loose.
Or try XPaths in the playground, in the browser (the loose box
switches the mode).
Point it at your Android Appium tests (Java, Kotlin, Python, JS/TS, C#, Ruby, JSON, YAML…). Node.js 20+, no dependencies. The strategy call is rewritten together with the string:
- AppiumBy.xpath("(//android.widget.TextView[@content-desc='price'])[2]")
+ AppiumBy.androidUIAutomator("new UiSelector().className(\"android.widget.TextView\").description(\"price\").instance(1)")
- (AppiumBy.XPATH, '//*[@content-desc="Cart" and contains(@text, "USD")]')
+ (AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().description("Cart").textMatches("(?s).*USD.*")')
[skip] UiSelector has no direct-child criterion: //android.widget.ListView/android.widget.TextView[1]
UiSelector: attributes, booleans, @index, contains() / starts-with() /
ends-with() / matches(), or on one attribute, (//X)[n], (//A//B)[1], existence tests such as [@text].//A//B → childSelector. --exact leaves these alone.contains(@text,"Total") becomes textMatches("(?s).*Total.*"), case-sensitive like the
XPath. --loose writes textContains instead, which ignores case (see the modes above).hint on how to rewrite it by hand: direct child //A/B, sibling
positions, /hierarchy/… paths, parent and sibling axes, and more.Checked on 1645 real Android XPaths from 20 open-source projects: 73 % convert (64 % exact, 9 % close), and every
converted one (1169 distinct XPaths) ran on an Android emulator with 0 wrong results. --write changes 54 % in
place; the other converted ones have no strategy call around the string (a constant, a data file) and are only
reported, with their UiSelector.
%s, {0}, ${x}) that contain " or \ break the UiSelector (warned); (//X)[${i}] becomes
instance(${i - 1}) only where the string syntax can compute it.UiSelectorGoogle plans to deprecate and remove
UiSelector from UiAutomator, and the UiAutomator2 driver
warns that this will make every -android uiautomator locator invalid. No date or version is set yet (UiAutomator
2.4.0, July 2026, still ships it), and the driver's server bundles UiAutomator 2.3.0, so today's locators keep
working until Appium moves to a version without it. Appium has not announced a replacement:
appium-uiautomator2-driver#812 is open. XPath
locators are not affected. The quarterly upstream watch reports any change to that issue.
iOS counterpart: xpath-to-class-chain.
MIT