Add doc written by @prattpratt

Tweaked syntax. Added section on appium predicate helpers
This commit is contained in:
bootstraponline
2014-05-22 23:51:09 -04:00
parent 9fb79d4614
commit d6c95ef91f
+239
View File
@@ -0,0 +1,239 @@
# iOS Predicate
I'd like to share my experience in using *'-ios uiautomation'* search strategy with **Predicates**. After using Instruments/UIAutomation tool for a long time I paid attention on the following methods in [UIAutomation JavaScript API](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html):
```java
(UIAElement) UIAElementArray.firstWithPredicate(PredicateString predicateString)
(UIAElementArray) UIAElementArray.withPredicate(PredicateString predicateString)
```
And it turned out that native JS search strategy (powered by Apple) provides much more flexibility than I was thinking before, it is almost just like Xpath.
**[Predicates](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/predicates.html)** can be used to restrict an elements set to select only those ones for which some condition is true.
For example:
```java
appiumDriver.findElementsByIosUIAutomation("collectionViews()[0].cells().withPredicate(\"ANY staticTexts.isVisible == TRUE\")")
```
\- will select only those ```UIACollectionCell``` elements that have visible ```UIAStaticText``` child elements, and themselves are childs of 1st ```UIACollectionView``` element that should be located under the main app window. Here ```staticTexts()``` and ```isVisible()``` are methods available in ```UIAElementArray``` and ```UIAElement``` classes respectively. **Note that ```UIAElementArray``` numbering begins with ```0``` unlike Xpath where indexes counting starts from ```1```**
Here's a list of available Predicates (mostly taken from [Predicates Programming Guide](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/predicates.html))
## Basic Comparisons
= , ==
- The left-hand expression is equal to the right-hand expression:
```javascript
tableViews()[1].cells().firstWithPredicate("label == 'Olivia' ")
same in Xpath: /UIATableView[2]/UIATableCell[@label = 'Olivia'][1]
```
\>= , =\>
- The left-hand expression is greater than or equal to the right-hand expression.
<= , =<
- The left-hand expression is less than or equal to the right-hand expression.
\>
- The left-hand expression is greater than the right-hand expression.
<
- The left-hand expression is less than the right-hand expression.
!= , <\>
- The left-hand expression is not equal to the right-hand expression.
BETWEEN
- The left-hand expression is between, or equal to either of, the values specified in the right-hand side. The right-hand side is a two value array (an array is required to specify order) giving upper and lower bounds. For example, ```1 BETWEEN { 0 , 33 }```, or ```$INPUT BETWEEN { $LOWER, $UPPER }```.
In Objective-C, you could create a BETWEEN predicate as shown in the following example:
```objectivec
NSPredicate *betweenPredicate =
[NSPredicate predicateWithFormat: @"attributeName BETWEEN %@", @[@1, @10]];
```
This creates a predicate that matches ```( ( 1 <= attributeValue ) && ( attributeValue <= 10 ) )```
## Boolean Value Predicates
TRUEPREDICATE
- A predicate that always evaluates to ```TRUE``` .
FALSEPREDICATE
- A predicate that always evaluates to ```FALSE```.
## Basic Compound Predicates
AND , &&
- Logical AND.
OR , ||
- Logical OR.
NOT , !
- Logical NOT.
## String Comparisons
String comparisons are by default case and diacritic sensitive. You can modify an operator using the key characters ```c``` and ```d``` within square braces to specify case and diacritic insensitivity respectively, for example ```firstName BEGINSWITH[cd] $FIRST_NAME```
BEGINSWITH
- The left-hand expression begins with the right-hand expression.
```javascript
scrollViews()[3].buttons().firstWithPredicate("name BEGINSWITH 'results toggle' ")
same in Xpath: /UIAScrollView[4]/UIAButton[starts-with(@name, 'results toggle')][1]
```
CONTAINS
- The left-hand expression contains the right-hand expression.
```javascript
tableViews()[1].cells().withPredicate("ANY collectionViews[0].buttons.name CONTAINS 'opera'")
same in Xpath: /UIATableView[2]/UIATableCell[UIACollectionView[1]/UIAButton[contains(@name, 'opera')]]
```
ENDSWITH
- The left-hand expression ends with the right-hand expression.
LIKE
- The left hand expression equals the right-hand expression: ? and * are allowed as wildcard characters, where ? matches 1 character and * matches 0 or more characters. In Mac OS X v10.4, wildcard characters do not match newline characters.
```javascript
tableViews()[0].cells().firstWithPredicate("name LIKE '*Total: $*' ")
same in Xpath: /UIATableView[1]/UIATableCell[matches(@name, '.*Total: \$.*')][1]
```
MATCHES
- The left hand expression equals the right hand expression using a regex -style comparison according to ICU v3 (for more details see the ICU User Guide for [Regular Expressions](http://userguide.icu-project.org/strings/regexp)).
```javascript
tableViews().firstWithPredicate("value MATCHES '.*of 7' ")
same in Xpath: /UIATableView[matches(@value, '.*of 7')][1]
```
## Aggregate Operations
ANY , SOME
- Specifies any of the elements in the following expression. For example ```ANY children.age < 18``` .
```javascript
tableViews()[0].cells().firstWithPredicate("SOME staticTexts.name = 'red'").staticTexts().withName('red')
same in Xpath: /UIATableView[1]/UIATableCell[UIAStaticText/@name = 'red'][1]/UIAStaticText[@name = 'red']
```
ALL
- Specifies all of the elements in the following expression. For example ```ALL children.age < 18``` .
NONE
- Specifies none of the elements in the following expression. For example, ```NONE children.age < 18``` . This is logically equivalent to ```NOT (ANY ...)``` .
IN
- Equivalent to an SQL IN operation, the left-hand side must appear in the collection specified by the right-hand side. For example, ```name IN { 'Ben', 'Melissa', 'Matthew' }``` . The collection may be an array, a set, or a dictionary—in the case of a dictionary, its values are used.
array[index]
- Specifies the element at the specified index in the array.
array[FIRST]
- Specifies the first element in the array.
array[LAST]
- Specifies the last element in the array.
array[SIZE]
- Specifies the size of the array
```javascript
elements()[0].tableViews()[0].cells().withPredicate("staticTexts[SIZE] > 2")
same in Xpath: /*[1]/UIATableView[1]/UIATableCell[count(UIAStaticText) > 2]
```
## Identifiers
**C style identifier**
- Any C style identifier that is not a reserved word.
**\#symbol**
- Used to escape a reserved word into a user identifier.
**[\\]{octaldigit}{3}**
- Used to escape an octal number ( ```\``` followed by 3 octal digits).
**[\\][xX]{hexdigit}{2}**
- Used to escape a hex number ( ```\x``` or ```\X``` followed by 2 hex digits).
**[\\][uU]{hexdigit}{4}**
- Used to escape a Unicode number ( ```\u``` or ```\U``` followed by 4 hex digits).
## Literals
Single and double quotes produce the same result, but they do not terminate each other. For example, ```"abc"``` and ```'abc'``` are identical, whereas ```"a'b'c"``` is equivalent to a space-separated concatenation of ```a, 'b', c```.
FALSE , NO
- Logical false.
TRUE , YES
- Logical true.
NULL , NIL
- A null value.
SELF
- Represents the object being evaluated.
"text"
- A character string.
'text'
- A character string.
**Comma-separated literal array**
- For example, ```{ 'comma', 'separated', 'literal', 'array' }``` .
**Standard integer and fixed-point notations**
- For example, ```1 , 27 , 2.71828 , 19.75``` .
**Floating-point notation with exponentiation**
- For example, ```9.2e-5``` .
0x
- Prefix used to denote a hexadecimal digit sequence.
0o
- Prefix used to denote an octal digit sequence.
0b
- Prefix used to denote a binary digit sequence.
## Reserved Words
The following words are reserved:
`AND, OR, IN, NOT, ALL, ANY, SOME, NONE, LIKE, CASEINSENSITIVE, CI, MATCHES, CONTAINS, BEGINSWITH, ENDSWITH, BETWEEN, NULL, NIL, SELF, TRUE, YES, FALSE, NO, FIRST, LAST, SIZE, ANYKEY, SUBQUERY, CAST, TRUEPREDICATE, FALSEPREDICATE`
## Appium predicate helpers
Appium has [helpers for predicate search](https://github.com/appium/appium-uiauto/blob/3052dace828db2ab3d722281fb7853cbcbc3252f/uiauto/appium/app.js#L68) in app.js:
- `getFirstWithPredicate`
- `getFirstWithPredicateWeighted`
- `getAllWithPredicate`
- `getNameContains`
Here's a Ruby example:
```ruby
# Ruby example
text = 'Various uses'
predicate = "name contains[c] '#{text}' || label contains[c] '#{text}' || value contains[c] '#{text}'"
element = execute_script(%Q(au.mainApp().getFirstWithPredicate("#{predicate}");))
puts element.name # Buttons, Various uses of UIButton
```