-
-
Notifications
You must be signed in to change notification settings - Fork 769
FindBy doc update #1311
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
FindBy doc update #1311
Changes from 8 commits
3d9da73
60247c9
5589929
26b3ddc
26719ee
e0dfbd8
b660c28
67ba60b
0d3ff4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
# Standard Selectors | ||
|
||
## AndroidFindBy / iOSXCUITFindBy / WindowsFindBy | ||
|
||
# Advanced Selectors | ||
|
||
## iOS's String Predicates | ||
|
||
You can specify a [predicate](https://developer.apple.com/documentation/foundation/nspredicate) | ||
in your Class Chain to limit the number of matched items. There's | ||
[a detailed guide to that](https://appium.io/docs/en/writing-running-appium/ios/ios-predicate/index.html) | ||
on the Appium Docs website with some Appium-specific considerations. | ||
|
||
## iOS's Class Chain Queries | ||
|
||
Our XCUiTest integration has full support for the 'Class Chain' concept. This | ||
can do much of what XPath does...but faster. Note that many Class Chains leverage | ||
String Predicates too. | ||
|
||
### String Predicates in Class Chains | ||
|
||
There's a special array-style syntax for defining predicates in a Class Chain: | ||
|
||
``` | ||
// Start with [ | ||
// Followed by ` | ||
// Followed by some text | ||
// Followed by ` | ||
// End with ] | ||
[`label != 'a'`] | ||
[`isWDVisible == 1`] | ||
[`value < 0`] | ||
``` | ||
|
||
#### Searching Descendents | ||
|
||
If you replace the backticks (`` ` ``) around a predicate with dollar signs (`$`), | ||
then the Class Chain will match against the children, grandchildren, and other | ||
descendants of each element. | ||
|
||
``` | ||
// Find a cell element with the label 'here' | ||
XCUIElementTypeCell[`label == 'here'`] | ||
// Find a cell element which contains SOMETHING ELSE that has the label 'here' | ||
XCUIElementTypeCell[$label == 'here'$] | ||
``` | ||
|
||
#### Handling Quote Marks | ||
|
||
Most of the time, you can treat pairs of single quotes or double quotes | ||
interchangably. If you're searching with a string that contains quote marks, | ||
though, you [need to be careful](https://stackoverflow.com/q/14116217). | ||
|
||
```c | ||
// Make sure to escape each quote mark that matches your delimiter | ||
"text with \"some\" 'quote' marks" | ||
// To NSPredicate, the line above and the line below are equivalent | ||
'text with "some" \'quote\' marks' | ||
``` | ||
```java | ||
// When defining a iOSXCUITFindBy annotation, you'll be constrained by the | ||
// Java string-quoting rules too. | ||
@iOSXCUITFindBy(iOSClassChain = "**/SomeElement[`'text with \"some\" \\\'quote\\\' marks'`]") | ||
``` | ||
|
||
### External References | ||
|
||
Refer to [the official WebDriverAgent query docs](https://github.com/facebookarchive/WebDriverAgent/wiki/Class-Chain-Queries-Construction-Rules) | ||
to learn more about the general concept. | ||
|
||
### Sample usage: | ||
|
||
Cliabhach marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
```java | ||
// Selector for image elements | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeImage") | ||
|
||
// Selector for the first image element on screen | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeImage[1]") | ||
// Selector for the second image element on screen | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeImage[2]") | ||
// Selector for the last image element on screen | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeImage[-1]") | ||
// Selector for the penultimate image element on screen | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeImage[-2]") | ||
|
||
// Selector for every cell with the name 'Foo' (single quote style) | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeCell[`name == 'Foo'`]") | ||
// Selector for every cell with the name "Foo" (double quote style) | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeCell[`name == \"Foo\"`]") | ||
Cliabhach marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
// Selector for every cell with a name that starts with 'Foo' (single quote style) | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeCell[`name BEGINSWITH 'Foo'`]") | ||
// Selector for every cell with a name that starts with "Foo" (double quote style) | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeCell[`name BEGINSWITH \"Foo\"`]") | ||
Cliabhach marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Selector for every cell with a name that starts with "it's not" | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeCell[`name BEGINSWITH \"it's not\"`]") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. class chain also supports indexing, including negative indexes (e.g. -1 means the last matched element, -2 - the one before the last one, etc) . The only limitation is that the index should be the last specifier in the chain, for example There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are 1-based indices, it looks like. What does an index of 0 do? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. throws an error :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair enough. |
||
|
||
// Selector that'll match every top-level element on screen | ||
@iOSXCUITFindBy(iOSClassChain = "*") | ||
// Selector that'll match every leaf element on screen (watch out: this can be SLOW) | ||
@iOSXCUITFindBy(iOSClassChain = "**/*") | ||
|
||
// You can place an index after a predicate: the following finds the last image element with name 'Foo' | ||
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeCell[`name == 'Foo'`][-1]") | ||
``` | ||
|
||
## Android's uiAutomator String | ||
|
||
Available when using [AndroidBy](AndroidBy) and [AndroidFindBy](AndroidFindBy) with | ||
[appium-uiautomator2-server](https://github.com/appium/appium-uiautomator2-server). This | ||
string will be used by the server to construct a UiSelector or UiScrollable object. | ||
|
||
### External References | ||
|
||
For an overview of what the backend is capable of, please check out the | ||
|
||
* [Main UI Automator Guide](https://developer.android.com/training/testing/ui-automator) | ||
* [UiScrollable API docs](https://developer.android.com/reference/androidx/test/uiautomator/UiScrollable) | ||
and | ||
* [UiSelector API docs](https://developer.android.com/reference/androidx/test/uiautomator/UiSelector) | ||
|
||
### Sample Strings | ||
|
||
Here are some ways you could configure a UiSelector in your project: | ||
|
||
```java | ||
// Create a selector that looks for the text "Hello World": | ||
@AndroidFindBy(uiAutomator = "new UiSelector().text(\"Hello World\")") | ||
Cliabhach marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
// Create a selector that tries to find an ImageView: | ||
@AndroidFindBy(uiAutomator = "new UiSelector().className(\"android.widget.ImageView\")") | ||
|
||
// Create a selector that matches resource ids against a regular expression: | ||
private static final String looksLikeAPage = "page_number_\d*"; | ||
@AndroidFindBy(uiAutomator = "new UiSelector().resourceIdMatches(\"" + looksLikeAPage + "\")") | ||
|
||
// The agent also supports some abbreviated forms - all 3 of the below | ||
// strings are equivalent. | ||
@AndroidFindBy(uiAutomator = "new UiSelector().className(\"android.widget.EditText\")") | ||
@AndroidFindBy(uiAutomator = "UiSelector().className(\"android.widget.EditText\")") | ||
@AndroidFindBy(uiAutomator = ".className(\"android.widget.EditText\")") | ||
|
||
// You can connect up conditions to search for multiple things at once | ||
@AndroidFindBy(uiAutomator = ".resourceId(\"android:id/list\").classNameMatches(\"\.*RecyclerView\").index(3)") | ||
``` | ||
|
||
..and here are some that create UiScrollable objects: | ||
|
||
```java | ||
private static final String ourImageSelector = ".className(\"android.widget.ImageView\")"; | ||
private static final String ourListSelector = ".className(\"android.widget.ListView\")"; | ||
|
||
// Create a scrollable associated with a list (by itself, this doesn't do anything useful...) | ||
@AndroidFindBy(uiAutomator = "new UiScrollable(" + ourListSelector + ")") | ||
|
||
// Create a scrollable that scrolls forward along a list until it finds an ImageView: | ||
@AndroidFindBy(uiAutomator = "new UiScrollable(" + ourListSelector + ").scrollIntoView(" + ourImageSelector + ")") | ||
|
||
``` |
Uh oh!
There was an error while loading. Please reload this page.