Element Selectors

Every basic element and custom element requires a selector object that locates the element at run time. A container element doesn't require a selector, because it has a default.

When a selector is used for a custom element, the selector is reserved for the custom element’s type and can’t be reused for any other type.

UTAM supports all valid CSS selectors.

Selector Properties

A selector object has these properties:

{
    "elements": [
        {
            "name": "myDiv",
            "selector": {
                "css": ".myDiv"
            }
        },
        {
            "name": "listItems",
            "selector": {
                "css": "li a",
                "returnAll": true
            }
        }
    ]
}

Selector Parameters

A selector can depend on run-time information from the test, like a label or an index. To access this run-time information, use these format indicators inside a CSS selector. At run time, they’re replaced by values passed as parameters to the element’s public method.

{
    "elements": [
        {
            "name": "listItemWithTitle",
            "selector": {
                "css": "li a[title='%s']",
                "args": [
                    {
                        "name": "titleString",
                        "type": "string"
                    }
                ]
            },
            "public": true
        }
    ]
}

UTAM generates this code from the JSON.

  public Actionable getListItemWithTitle(String titleString) {
    // return instance of the element using selector with replaced parameter
  }

A nested element can inherit parameters from its parents. In this example, the nested input needs two integer parameters for the enclosing table cell.

{
    "elements": [
        {
            "name": "tableCell",
            "selector": {
                "css": "tr:nth-of-type(%d) td:nth-of-type(%d)",
                "args": [
                    {
                        "name": "rowIndex",
                        "type": "number"
                    },
                    {
                        "name": "colIndex",
                        "type": "number"
                    }
                ]
            },
            "elements": [
                {
                    "name": "inputInsideCell",
                    "selector": {
                        "css": "input"
                    },
                    "type": ["editable"],
                    "public": true
                }
            ]
        }
    ]
}

UTAM collects parameter names for every element from root that leads to the current element and adds them in order of appearance.

  public Editable getInputInsideCell(int rowIndex, int colIndex) {
    // Apply the provided parameters,
    // then find an input element inside a table cell.
  }

Mobile Selectors

The Salesforce mobile app is a mobile hybrid application. Some pages are WebView pages, and some pages are native pages. For a native page, CSS selectors aren't sufficient. To find elements in a mobile environment, Appium implements a number of locator strategies for specific mobile devices. For more information, see appium.io: Find Elements.

Appium is an open-source tool for automating native, mobile web, and hybrid applications. Appium allows you to write tests against multiple platforms, such as iOS and Android, using the same API.

Accessibility ID Selector

This selector is supported for Mobile Platform only.

Use the accessibility ID selector, which is a unique identifier for a UI element. This example uses an accessibility ID selector of searchCell.ViewAllCell.

{
  ...
  	"name": "searchCell",
    "selector": {
        "accessid": "searchCell.ViewAllCell"
    },
  ...
}

Generated Java code:

@Selector.Find(accessid = "searchCell.ViewAllCell")
private ElementLocation searchCell;

iOS Class Chain Selector

This selector is for iOS Platform only.

The iOS class chain selector finds a window element from the root of the page:

Page object JSON:

{
  ...
    "name": "appWindow",
    "selector": {
        "classchain": "XCUIElementTypeWindow"
    },
  ...
}

Generated Java code:

@Selector.Find(classchain = "XCUIElementTypeWindow")
private ElementLocation appWindow;

You can use double star and slash (**/) to define the next item descendant from the root. This strategy makes the configured locator not sensitive to the hierarchy of the page.

Page object json:

{
  ...
    "name": "button",
    "selector": {
        "classchain": "**/XCUIElementTypeButton"
    },
  ...
}

Generated Java code:

@Selector.Find(classchain = "**/XCUIElementTypeButton")
private ElementLocation button;

Sometimes, the element type isn't unique enough to identify a specific element. In those scenarios, you can combine the element type with an attribute using a predicate. The predicate string should always be enclosed in ` marks or $ characters inside square brackets. Use `` or $$ to escape a single ` or $ character inside a predicate expression.

A single backtick means the predicate expression is applied to the current children. A single dollar sign means the predicate expression is applied to all the descendants of the current element(s). String values, not the key words, can be enclosed by ' or ".

Page object JSON:

{
  ...
    "name": "newButton",
    "selector": {
        "classchain": "**/XCUIElementTypeButton[`name BEGINSWITH 'New'`]",
    },
  ...
}

Generated Java code:

@Selector.Find(classchain = "**/XCUIElementTypeButton[`name BEGINSWITH 'New'`])
private ElementLocation newButton;

This selector uses the $ character to apply the attribute to all the descendants of the current element(s).

Page object JSON:

{
  ...
    "name": "newButton",
    "selector": {
        "classchain": "**/XCUIElementTypeCell[$name == \"My Accounts\"$]",
    },
  ...
}

Generated Java code:

@Selector.Find(classchain = "**/XCUIElementTypeCell[$name == \"My Accounts\"$])
private ElementLocation newButton;

The following attributes are supported:

The following comparison types for an attribute are supported:

You can use the following operators to set conditions for multiple attributes in one expression.

For example:

{
  ...
    "name": "newButton",
    "selector": {
        "classchain": "**/XCUIElementTypeBotton[`name BEGINSWITH 'New' AND visible == true`]",
    },
  ...
}

Generated Java code:

@Selector.Find(
    classchain = "**/XCUIElementTypeBotton[`name BEGINSWITH 'New' AND visible == true`]"
)
private ElementLocation newButton;

Similarly to CSS selectors, you can use string or integer parameters. This example uses a string parameter.

{
  ...
    "name": "tabBarBtn",
    "selector": {
        "classchain": "**/XCUIElementTypeButton[`name == '%s'`]",
        "args": [
            {
                "name": "item",
                "type": "string"
            }
        ]
    }
}

Generated Java code:

@ElementMarker.Find(classchain = "**/XCUIElementTypeButton[`name == '%s'`]")
private ElementLocation tabBarBtn;

@Override
final TabBarBtnElement getTabBarBtnElement(String item) {
  return element(this.tabBarBtn).build(TabBarBtnElement.class, TabBarBtnElementImpl.class, item);
}

Here's more detailed examples for iOS Class Chain.

Android UIAutomator Selector

This selector is for Android Platform only.

Appium uses the UIAutomator selector to enable searching using UiSelectors. We support the following methods only from UiSelector to find elements:

This example uses the clickable method:

{
  ...
    "name": "acceptButton",
    "selector": {
        "uiautomator": "new UiSelector().clickable(true)"
    },
  ...
}

Generated Java code:

@Selector.Find(uiautomator = "new UiSelector().clickable(true)")
private ElementLocation acceptButton;

This example uses the className method:

{
  ...
  	"name": "newButton",
    "selector": {
        "uiautomator": "new UiSelector().className(\"android.widget.TextView\")"
    },
  ...
}

Generated Java code:

@Selector.Find(
    uiautomator = "new UiSelector().className(\"android.widget.TextView\")"
)
private ElementLocation newButton;

This example uses the descriptionContains method:

{
  ...
    "name": "vfLink",
    "selector": {
        "uiautomator": "new UiSelector().descriptionContains(\"VfPage Link\")"
    },
  ...
}

Generated Java code:

@Selector.Find(
    uiautomator = "new UiSelector().descriptionContains(\"VfPage Link\")"
)
private ElementLocation vfLink;

UIScrollable is a powerful Android class that performs element lookups in scrollable layouts. In most cases, you should use the scrollIntoView method, which performs a scroll action until the destination element is found on the screen. You can use UIScrollable swipe to:

The scrollIntoView method has UiSelector as search criteria input to allow you to find elements by supported methods. For example:

{
    "implements": "utam-salesforceapp/pageObjects/navigation/navItemsList",
    "elements": [
        {
            "name": "navItemWithLabel",
            "selector": {
                "uiautomator": "new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(className(\"android.widget.TextView\").text(\"%s\"))",
                "args": [
                    {
                        "name": "item",
                        "type": "string"
                    }
                ]
            }
        }

The generated Java code is:

  @ElementMarker.Find(
    uiautomator =
        "new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(className(\"android.widget.TextView\").text(\"%s\"))",
    nullable = true
  )
  private ElementLocation navItemWithLabel;

Here's more details for UIAutomator UiSelector and UiScrollable.