Skip to main content
Version: 10.3.1

Apply element selectors

Selectors (or Locators) are used to find and match the elements of a web page or desktop app that a robot needs to interact with. Using the right selector ensures the bots are faster, more reliable or has lower maintenance over releases. If you’re fortunate enough to be working with unique IDs and Classes, then you’re usually all set. It can be a real challenge to verify that you have the right selectors to accomplish what you want.

This tutorial explains different selectors, how, when and ideal strategies to use these selectors.

Web selectors

ID selector

IDs are the most preferred way to locate elements on a page, as each ID is supposed to be unique which makes IDs a very fast and reliable way to locate elements.

With this strategy, the first element with the ID attribute value matching the selector will be returned. If no element has a matching ID attribute value, NoSuchElementException is raised.

For example, if an element is given like this:

<form name="loginForm">
Login Username:
<input id="username" type="text" name="login" />
Password:
<input id="password" type="password" name="pass" />
<input type="submit" name="signin" value="SignIn" />
</form>

You can easily choose the element with the help of ID selector from the above example:

  • id = username
  • id = password
WebElement elementUser = $(byId("username"))

elementUser.val("my_login")
$(byId("password")).sendKeys("secure_pass").pressEnter()

Even though this is a great selector, obviously it is not realistic for all objects on a page to have IDs. In some cases, developers make it having non-unique IDs on a page or auto-generate the iIDs, in both cases it should be avoided.

Name selector

This is also an efficient way to locate an element with name attribute, after IDs give it your second preference but likewise IDs, name attributes don’t have to be unique.

With this strategy, the first element with the name attribute value matching the selector will be returned. If no element has a matching name attribute, NoSuchElementException is raised.

WebElement elementUser = $(byName("login"))

elementUser.setValue("my_login")
$(byName("pass")).sendKeys("secure_pass").pressEnter()

Text selector

You can find elements by their inner text using the following selectors.

  • byText returns all elements with given text (exact match).
  • withText returns all elements containing given text (substring).
  • byLinkText returns all anchor <a> elements with given text (exact match).
  • byPartialLinkText returns all anchor <a> elements containing given text (substring).

Sample HTML element:

<a href="link.html">Name of the Link</a>
<button>Order now!</button>

To click the hyperlink or button using the tag’s text, you can use the following text selectors:

WebElement spanElement1 = $(byText("Order now!"))
WebElement spanElement2 = $(withText("Order"))

WebElement linkElement1 = $(byLinkText("Name of the Link"))
WebElement linkElement2 = $(byPartialLinkText("Name of"))

Tag and attribute selectors

Let's automate the following form using tag and attribute selectors:

<p><abbr title="World Health Organization">WHO</abbr> was founded in 1948.</p>
<form name="loginForm">
Login Username:
<input id="username" type="text" name="login" class="login" />
Password:
<input id="password" type="password" name="pass" />
<input type="submit" name="signin" value="SignIn" />
</form>

You can use the following selectors:

  • byTitle
  • byValue
  • byTagName
  • byClassName
  • byAttribute
  • by('attribute-name', 'attribute-value')
$(byTitle("World Health Organization")).getText()
$(byValue("SignIn")).click()
$(byTagName("p")).getText()
$(byClassName("login")).sendKeys("username")
$(byAttribute("type","text")).sendKeys("username")
$(by("value","SignIn")).click()

CSS selector

tip

The CSS selector can also be used for desktop.

See also:

Let's automate the following form using CSS selectors:

<form name="loginForm">
Login Username:
<input id="username" type="text" name="login" />
Password:
<input id="password" type="password" name="pass" />
<input type="submit" name="signin" value="SignIn" />
</form>

You can use both $() and $(byCssSelector) selectors, which are equivalent.

$("#password").sendKeys("secure_pass").pressEnter()
$(byCssSelector("form input:first-child")).sendKeys("my_login")

XPath selector

tip

The XPath selector can also be used for desktop.

See also:

While DOM is the recognized standard for navigation through an HTML element tree, XPath is the standard navigation tool for XML, and an HTML document is also an XML document (xHTML).

For example, to select the username from the above example, do as follows:

$(byXpath("//*[@id='username']"))
$(byXpath("//input[@id='username']"))
$(byXpath("//form[@name='loginForm']/input[1]"))
$(byXpath("//*[@name='loginForm']/input[1]"))

Desktop selectors

The following selectors are available for the desktop driver:

FindElement (\$) and FindElements (\$\$) commands

The difference between the findElement() and findElements() method is that the first returns an uiElement object. Otherwise, it throws an exception and the latter returns a list of uiElements. It can return an empty list if no elements match the query.

findElement() – \$()

  • On Zero match: throws NoSuchElementException
  • On One match: returns uiElement
  • On One+ match: returns the first uiElement matching the specified selector

findElements() – \$\$()

  • On Zero match: returns an empty list
  • On One match: returns a list of one uiElement only
  • On One+ match: returns a list with all matching instances
Expand to see the example with findElements
    import com.workfusion.odf2.compiler.BotTask;
import com.workfusion.odf2.core.task.AdHocTask;
import com.workfusion.odf2.core.task.TaskInput;
import com.workfusion.odf2.core.task.output.TaskRunnerOutput;
import com.workfusion.odf2.core.webharvest.rpa.RpaDriver;
import com.workfusion.odf2.core.webharvest.rpa.RpaFactory;
import com.workfusion.odf2.core.webharvest.rpa.RpaRunner;
import com.workfusion.rpa.helpers.RPA;
import com.workfusion.rpa.helpers.UiElement;
import com.workfusion.rpa.helpers.UiElementCollection;
import org.openqa.selenium.JavascriptExecutor;
import org.slf4j.Logger;

import javax.inject.Inject;

@BotTask(requireRpa = true)
public class ApplyElementSelector implements AdHocTask {
private final RpaRunner rpaRunner;
private final Logger logger;

@Inject
public ApplyElementSelector(RpaFactory rpaFactory, Logger logger){
this.rpaRunner = rpaFactory
.builder(RpaDriver.UNIVERSAL)
.closeOnCompletion(true)
.build();
this.logger = logger;
}

@Override
public TaskRunnerOutput run(TaskInput taskInput) {

rpaRunner.execute(driver->{
RPA.timeouts(15000);
RPA.openChrome("https://rpa-tutorial.s3.amazonaws.com/trainings/iframe/form.html");
UiElementCollection inputList = RPA.$$("input");
logger.debug("Number of form inputs on a page: " + inputList.size());
logger.debug("ID of the 12th input: " + inputList.get(11).getAttribute("id"));

driver.switchDriver("chrome");
JavascriptExecutor javaexec = (JavascriptExecutor)driver;

for(UiElement input : inputList){
driver.executeScript("alert(\"Input Tag's Attribute Name Value - \" + arguments[0])",input.getAttribute("name"));
RPA.sleep(1000);
RPA.switchTo().alert().accept();
}

});

return taskInput.asResult()
.withColumn("example_bot_task_output", "completed_successfully");
}

}

Win32 desktop applications

To accelerate finding UI elements of Win32 applications, you may use a specific search capability allowing to perform a search among components with a handle attribute only.

caution

Mind that the solution works best when used for applications built on the Win32 technology. When attempting to find elements without a handle attribute (for example, in applications like Microsoft Outlook 2016), you may face performance degradation instead.

There are two ways to enable advanced searching for UI elements.

  • Recommended: Add a specific capability to the robot plugin.

    ...
    <robotics-flow>
    <robot driver="desktop" name="driver1" close-on-completion="true">
    <capability name="desktopOptions">
    <script return="optionsMap"><![CDATA[
    optionsMap = ['FindByHandle':true]
    ]]></script>
    </capability>
    <script><![CDATA[
    ...
    ]]></script>
    </robot>
    </robotics-flow>
    ...
  • Optional: If you have a number of scripts and it's time-consuming to add a capability to each of them, you may launch a Worker with the option allowing to constantly execute a search by handle for all of these scripts. Mind that in this case, only the scripts automating Win32 applications should go to the specified Worker. To apply the option, launch the Worker adding the -DdesktopOptions.FindByHandle=true parameter.

note

If two of the above-mentioned ways are used, adding the capability is preferable, as a search like this can be applied not only for the entire Worker but for a specific script (a step in the business process).