Appearance
Targeting Elements in UI-licious
UI-licious is built around a simple philosophy.
Test user journeys, not HTML.
UI-licious encourages you to write tests the same way users think — by targeting elements using human-visible labels and intent, rather than brittle DOM structures.
Why not CSS selectors or XPath?
Traditional test automation relies on CSS selectors or XPath expressions that are tightly coupled to the DOM structure.
Even a relatively friendly-looking selector often looks like this:
css
button.btn-primary.add-to-cartThis already assumes:
- specific class names remain stable
- the button is uniquely identifiable
- styling classes double as behavioral identifiers
As the UI evolves, this frequently degrades into more deeply nested selectors in order to make targeting more specific. For example:
css
#app div.products > div:nth-child(3) > div.card > div.card-footer > button.btn.btn-primaryThese selectors break easily when the UI layout changes—even though the button still looks and behaves the same to users.
They also make tests harder to read, understand, and maintain. Over time, many test projects erode because new developers cannot easily tell what a test is trying to do by reading selector-heavy code. As a result, tests are neglected, brittle, or rewritten from scratch.
UI-licious helps you write more readable and maintainable tests by allowing you to target elements using user-visible labels, which are more stable than DOM paths.
How UI-licious finds elements with text
When you provide a text label to target an element, UI-licious scans the DOM and scores possible matches using multiple signals:
- Associated labels
UI-licious looks for explicit labels meant for humans:
- elements associated via
<label for='...'> - visible text in links, buttons, dropdown options
- placeholder text in
inputelements
These carry the strongest intent and are prioritized.
- Accessibility attributes
Accessibility metadata is treated as first-class information:
aria-labelaria-labelledby
- Descriptive attributes
UI-licious also considers descriptive attributes, such as:
nameattribute oninputelementstitleattribute (used for setting tooltip)alt-texton image elements
- Nearby visible text
If no explicit label exists, UI-licious analyzes text near the element in the visual layout to infer meaning.
Targeting visual elements (icons and images)
Not all UI elements have visible text.
For icon buttons, images, or custom controls, UI-licious can still target them by text using:
titleattribute (tooltips)aria-labelalt-texton images
Example:
js
I.goTo("https://maps.google.com")
I.fill("Search", "Merlion, Singapore")
I.click("Search") // clicks the magnifying glass iconThis works even if the button is just a magnifying glass icon — as long as it has a tooltip, ARIA label, or alt text describing its purpose.
Resolving ambiguity when multiple elements match
Sometimes a page contains multiple elements with the same label — for example, a product page that displays several “Add to cart” buttons.
When this happens, UI-licious resolves the ambiguity using a scoring system to find the best match:
- Semantics
For click actions, elements with clear interactive intent — such as <button>, <a>, and input elements — are given higher relevance than generic elements like <div> or <span>.
- Intentionality
Explicit labels meant for humans, such as <label> elements and aria-label attributes, have higher relevance than inferred descriptions like name attributes.
- Context from previous steps
UI-licious also uses context from earlier commands to infer intent. Elements that are closer to or logically associated with previously targeted elements are considered more relevant.
Using I.see.hint() to guide targeting
When ambiguity still exists, you can give UI-licious a hint.
I.see.hint() marks an element as context for subsequent commands.
Example:
js
I.see.hint("Product A")
I.click("Add to cart")This tells UI-licious:
“The next action is related to the element associated with Product A.”
If there are multiple "Add to cart" button on the page, UI-licious will prefer one closer to "Product A".
Scoping targets with UI.context()
You can explicitly scope element targeting to a specific container using UI.context().
This is useful when the same labels appear in different sections and you want to limit targeting to a specific container element - like a panel, card or dialog.
js
I.click("Login") // this opens a modal
I.see(".modal")
UI.context(".modal", ()=>{
I.fill("Username", "john")
I.fill("Password", "supersecretpassword")
I.click("Login") // clicks the button inside the modal
})In this example, the page contains two “Login” buttons:
- one in the navigation menu that opens a login modal
- one inside the modal that submits the form
UI.context() is used to scope subsequent commands to elements within the modal, ensuring that the click targets the submission button inside the modal rather than the one on the page
Targeting elements within an iframe
Iframe elements exists in a isolated execution context from the top-level page. In order to interact with the contents of an iframe, you must use UI.context() to switch into the iframe.
Example:
js
UI.context("iframe[title='Payment Method']", ()=>{
I.fill("Card number", "4111 1111 1111 1111")
})In this example, the test switches into the context of the secure iframe "iframe[title='Payment Method']", and fills the "Card Number" input field.