# Elements and self-healing
Finding and targeting elements is one of the most important and difficult aspects of building and maintaining end-to-end tests.
Virtuoso makes this easy in two ways:
- Intelligent element selection: With plain English hints, you can target the element you want, even without knowing an explicit selector for the element.
- Automatic self-healing: As your tests execute over time, Virtuoso will automatically maintain and heal the underlying selectors for the element to make sure that your tests continue to work.
Stay with us, and by the end of this guide, you’ll be the Sherlock Holmes of element selection in Virtuoso.
# Finding an element
When Virtuoso attempts to interact with an element (e.g., Click on "Checkout"
), it will use the clues (hints about the element) that you provide for finding the element in your application.
Typically, you will start with just a plain English hint (e.g., "Checkout"
). Based on that, Virtuoso will apply some smart logic and heuristics to determine which element is the one that you are probably referring to.
# Intelligent element selection
Think about trying to find someone in a crowded place with just a name. That’s how Virtuoso feels with just a plain English hint like "email". Web pages are bustling with elements, from buttons to labels to divs.
For example, there may be various HTML elements that match "email". There may be a <label>
with the text "User email" and an <input>
beside it for the user to write the email address. There may even be a <button>
with the text "Send email" somewhere else on the page, or a <div>
with the text "Enter your email here" in it.
Since our only selector is a plain English hint ("email"
), how can Virtuoso write the content in the desired element? This is where the concept of intelligent element selection comes into play.
Virtuoso not only considers the selectors, but it will also do a smarter analysis to infer the target element. For example, if we want to target an input given with write ... in ...
, it can infer that the email input is the most-likely element to target. By leveraging our expertise behind the scenes, Virtuoso autonomously targets the correct element – to the best of its ability – and does what you were expecting it to do with your test step.
How sharp is Virtuoso's detective skill?
In all honesty, Virtuoso's making an educated guess based on the clues you give. Our track record? Spot on most of the time! But remember, the clearer the hint, the closer Virtuoso gets to the truth.
# How to improve intelligent element selection
Want to make Virtuoso even smarter? Here are some tricks:
- Element type: Specify the type of element (like 'button' or 'input').
- Position, relative to the previous element selected: Give a hint about its location (Is it on top? Bottom right?).
- Element visibility: Let Virtuoso know if it should be visible on the screen.
# Priority of element selectors
If you provide multiple selectors, Virtuoso always starts with the Hint selector. It’s like having a preferred lead. But if that doesn’t work out, Virtuoso will consider the other selectors (e.g., XPath, XPath ID, ID, etc), from top to bottom.
Later on in this guide, we will go deeper into scenarios with broken selectors.
Did you know?
The Hint
selector can also take XPaths or CSS selectors. So if you have a specific selector in mind, just drop it in, and Virtuoso will follow your lead.
# Element selection context and relative element selection
Imagine a store with many "Buy" buttons. How does Virtuoso know which one you want to click? The answer: proximity.
Think of Virtuoso as a human. If you tell a friend, "See that item named ‘Shiny Gadget’? Now click its 'Buy' button,” they’d get it. Similarly, guide Virtuoso to the right area, and it'll nail the selection. For example:
see "Shiny Gadget"
click on button "Buy"
# Resolving ambiguity
Ambiguity is caused by elements that in a first pass look identical in terms of how to select them (e.g., same text, same style). This can cause Virtuoso to target a different element from the one we want.
When this happens, and we have no other way that dealing with ambiguity, please consider the following:
- Try to set a more specific context as described in the previous section, this resolves many of the common cases of ambiguity.
- Think about what are you using to identify the element. Is there a different attribute that makes it unique compared to others? E.g., text, ID, attributes, element type, etc.
- If none of the above works, there is always the possibility of just using a more explicit XPath or CSS selector. It will match the exact element you want.
Balancing specific vs. open identifiers
A challenge in testing is to find a balance between how strict or open you define your targets. Although having an explicit XPath selector for targeting an element will indeed work deterministically on the first try, it might start to fail as soon as your application structure changes.
# Element healing
When you add a test step by typing something like Click on "Save"
, Virtuoso will add a new test step with a hint selector (e.g., "Save"
in our example).
The first time you execute this test step without adding any other selector, Virtuoso will try to infer more specific element identifiers (e.g., XPath, XPath ID, CSS) to ensure stable and accurate element selectors. This is done to prevent issues if the webpage changes over time. For example, if the webpage changes and the "Save"
button is now "Create"
, Virtuoso will still be able to find the button and click on it.
As you continue to use Virtuoso, the elements on the page under test might change between test step executions. For example, new element attributes (e.g., CSS class names) may be used, or the structure of the page could change. When this happens, Virtuoso will attempt to suggest new selectors that find your originally intended element and update the existing ones.
By default, if the selectors you added remain stable and consistently match the target element, Virtuoso will keep using them. Only when Virtuoso has trouble finding the target element or is uncertain about the accuracy of the existing selectors will the self-healing mechanism come into play, proposing new selectors.
# Where to enable self-healing and where to rely on hints
Hints in Virtuoso are self-healing element selectors. However, as mentioned in the previous section, the healing mechanism also exists to maintain deterministic selectors for your elements as well.
However, sometimes, the deterministic selectors can be counterproductive. For example, the deterministic selector may be targeting the 4th link in a list, but the target has been removed. When you explicitly want Virtuoso to target a given named element reference, particularly when the text reference you use is unique, it is best to use only the hint selector and disable healing, by locking the explicit selectors of the element and removing any explicit selectors, so that only a single Hint
selector is left.
Relying only on a hint selector with selector-healing disabled guarantees that if the target element is missing, or is being loaded, deterministic selectors (e.g., XPath / CSS) are simply not present to point to the wrong element.
# How to review healing
Virtuoso will always keep your Hint
selector(s); for the rest of the selectors (non-hint ones), it will remove those no longer working but also will suggest new and valid alternatives. After executing the test step, you can review the execution report. If Virtuoso has suggested new selectors, you will see a "healing" icon
On top of this page, you will notice one of these two messages:
- "Element healed automatically": This means that the changes were applied automatically to the element model and the selectors. The changes can be reverted if you "Reject" them.
- "Element can be healed": This means that changes are suggested, but not yet applied. They will be applied only after you "Accept" them.
You have the option to Accept
or Reject
the suggestions. Choosing not to take any action will not cause any issues; Virtuoso will keep trying to assist you in subsequent executions.
Accept
will let Virtuoso know that it's okay to keep the suggested selectors (if applied automatically) or to apply the changes (if not yet applied), and that you are satisfied with the healing suggestions.Reject
will let Virtuoso know that you do not want to keep the newly added selectors, considering them to be bad suggestions. This will improve the accuracy of future suggestions for similar scenarios.
# When is healing automatically applied
Element healing in Virtuoso is applied only when:
- Your element has a hint selector (e.g.,
"Save"
in our example). - Healing has not been disabled for the test step.
- The element is not selected with low confidence (compared to our model of the element).
- No element before this element was selected with low confidence (i.e., in case of a previous incorrect selection, we do not want to heal the next element, as it might be a consequence of the previous incorrect selection).
Therefore, if you have an element that only has a deterministic selector (with the explicit type such as "Xpath"), then by design, Virtuoso will not attempt to heal it.
Virtuoso will also never heal your hint selector. This means that if you have a hint selector, and it is not working, you will have to explicitly update that selector manually.
# Common element selection challenges
In this section, we go over a number of common element selection challenges and patterns.
# How to select an input box to write into
Wondering how to pick an input box? Here are some tricks:
- If there's a placeholder (like a hint saying "Write your name here"), use it! Just type
write "John" in "Write your name here"
. - Found a label? Great! If it says "Name", go with
write "John" in input "Name"
. Theinput
hint will get Virtuoso to pick the nearest input to the label "Name". - In cases where you have multiple options, like "first name" and "last name", use the most-unique text. Try:
write "John" in input "first name"
. - And if you're confident that there's only one input box, simply
write "John" in "input"
. Easy, right?
# What if my input does not have a label or placeholder?
Please refer to the previous section on element selection on how to target elements with relative context.
# What if my application does not use real HTML inputs?
Behind the scenes, Virtuoso's interaction with pages is based on the underlying HTML structure. Some applications may not use real HTML inputs (such as <input>
or <textarea>
) and rely instead on other elements (such as <div>
or <span>
) to simulate inputs.
In this case, you should a) not use the "write" command, and instead use the Keyboard (press
) command to trigger keyboard interactions, and b) make sure you target the right element when interacting.
For example, you should not use input "Name"
as no such element exists but instead target the specific element. If there is both a label and a separate element that is used for the input (for example, if that element has a class custom_input
), you should use a contextual interaction, such as:
click "Name"
press "Hello"
Pro tip: Tab button
Struggling to focus on the right element? Try the TAB button! e.g., first click "Name"
(where name is just a basic text label), then press TAB
, and then press "Hello"
.
# How to select items in a calendar picker
It depends actually on the nature of the calendar picker. If it's a browser-native calendar (e.g., <input type="date">
), then set the value
property of the native input
element. For other cases, the application might be using a native input element under the hood, but it will be noted if the other elements for the actual representation can be interacted with using the usual NLP commands of Virtuoso (e.g., click
).
# Here are a couple of examples:
Native calendar:
execute "document.querySelector('#birthday').value = '2023-10-11'"
Non-native calendar:
click on "date"
click "Year"
click "2023"
click "Month"
click "April"
click on "11"
Note that if the Year / Month are native HTML dropdowns, you can use the pick command instead (e.g.,
pick "2023" from "Year"
)
# How to select an item in a dropdown
Drop-downs can be sneaky! While many use the standard HTML <select>
element, others might be custom-made.
In this case, you must use the click
command to open the dropdown, and then use the click
command to select the item you want.
If the element appears like a native browser dropdown, your first attempt should be to rely on the select/pick command, e.g., pick second option from "list"
or pick "value" from "list"
.
Otherwise, you can use a couple of regular click or mouse commands. The first click would be to just open the dropdown. Then, the subsequent click would pick the item in the dropdown that you want.
# How to select an element without visible text
Many elements on a page do not have visible text. For example, an icon, or a button with an icon. In this case, you can use a number of different strategies.
First, the target element may have a hover text, a title text, or an accessibility text. In this case, you can simply target the element by using the text. For example, if there is a basket icon that has the text "Checkout" as part of its attributes (whether it is the "title" or another attribute), you can target it normally, such as: click "Checkout"
.
If the element does not have any user-friendly text, it is often likely that there are distinguishable attributes that you can use to target the element. Use the Advanced Mode in Live Authoring, and using the DevTools interface, pick the element on the page, and look at its attributes.
For example, if the element has a unique ID (e.g., id="checkout"
), you can use that. If the element has a unique class, you can use that (e.g., class="button icon-checkout"
could mean that you click "icon-checkout"
or even, just click "checkout"
).
If you struggle to find an element attribute to use as a hint, remember that you can always use relative element selection to your advantage. You can target a nearby element (e.g., see "nearby element") and then use a generic attribute of the element, such as its tag or one of its attributes/classes as the target (e.g., click "i"
or click "btn-icon"
).
Ultimately, if you struggle to find any way to differentiate the element, you can always resort to deterministic selectors such as XPath/CSS selectors. Note that using the "pick element" feature in Live Authoring, you can simply pick the element, and then simply write "click" and the autocomplete will offer you the element you just picked.
Accessibility
If you stumble upon an element with no visible text or accessibility attributes, consider it an "accessibility bug." It's good to raise this issue with your dev team.
# How to select an icon or visually accessible element
As best practice, web applications should include at least the attribute title
for this type of element. If that's the case, then you can target them just by typing the title. For example, look for "Icon title"
.
If there is no title, we have to rely on another type of selector. First, try to find a unique identifier for the element, such as a unique ID (e.g., look for "Icon ID"
). If this is not possible, you can rely on an XPath selector (e.g., look for "/html/body/div/svg[3]"
).
# How to select an element with variables
It is possible to target elements based on variable values (e.g., test data, environment variables, Flow, etc.). The value can be either a hint, a deterministic selector (e.g., XPath/CSS), or an element stored previously using the store element command.
Using it can be done by just putting the variable where the hint typically goes in the NLP command:
look for $variable
;click on $variable
.
# How to select an element where the data is dynamic
It depends.
If you want to select a dynamic element based on data, this is simple. For example, if you are buying a phone with different storage sizes, you can simply use a variable containing the storage (e.g., $storage with value of "256GB"). Then, you can use the variable in the command, such as click on $storage
.
Then, if the data of that variable becomes "512GB", the command will still work.
However, if you want to select a fixed element that is dynamic concerning other data, this is more complicated. For example, if you want to select the "Edit" button, but the button is only visible when the basket has items, then you need to use a different strategy.
In this case, you have a few options to consider:
- Locate a nearby element, e.g.,
see $recordValue
and thenclick "Edit"
, where Virtuoso would click the nearest edit button to the target data; however, it is important to note that this is NOT necessarily on the same row as the target data. For example, if there is no "Edit" button on your desired row, but there is one on the row above or below, Virtuoso will click one of those instead. - Find a fixed Xpath or CSS selector for your element, and use that instead of the text. For example, if the element is a button, you can use
click "/html/body/div/button"
. However, be mindful that Xpath and CSS selectors are brittle and can break easily if the application changes. - Locate the nearby relative element and store it. E.g.,
store element details of $recordValue in $element
and then using an extension, extract the selectors, and create a relative selector to the target element, using XPath.
# How to wait for a dynamic element state
There are cases where the element already exists, and you only want to interact with it after a state change. For example, clicking on a button after it becomes enabled.
This scenario is a bit tricky but there are different ways to overcome it:
- Find something that can identify the element in the desired state and not in the other. For example, is there an attribute or a class name or something that is only present when the element is in the desired state? If so, you can use the wait command with that specific identifier.
- Use a custom JS extension that checks the state of the element repeatedly until it is in the desired state. Check our extension library's wait for element attribute for an example of this.
# How to select a specific action item for a row in a table
We provided an example of how to achieve this in the dynamic data section above.
Acting over a specific row in a table can be a challenge sometimes given the similarity of the elements. However, you can follow the principle of relative element selection explained in this guide and apply it to the table.
For example, you can try to locate a unique value of that row (e.g., "e-mail address") and make Virtuoso look for it (e.g., look for "[email protected]"
). Now Virtuoso is focused on that specific row, and now you can probably just do the action you were wanting to do (e.g., click on "the action button of the row"
).
However, you may wish to target the element with relative XPath/CSS selectors, or use deterministic selectors to target the same element, as explained in previous sections.
# How to select an element in a pop-up / modal
Pop-ups and modals are common elements in web applications, and they can be tricky to test since they are often outside the context of the rest of the page.
In the case of Virtuoso, given the context-based nature of the NLP commands, a challenge may be present due to the positioning of elements. For example, you click a Delete button and get a confirmation to delete the item. However, the text "confirm" may exist in multiple places on the page, and Virtuoso would pick the element that is closest to the button you clicked (which may not be the confirmation button).
To overcome this, when a modal appears, make sure to first look for or wait for something about the modal (e.g., wait for "Are you sure you want to delete 'XYZ'?"
), which would focus Virtuoso in the context of the modal, and then you can click on the confirmation button (e.g., click "Confirm"
).
Note that once the context has closed, you may need to re-focus Virtuoso's context on the part of the page that seems most relevant.
Dismiss command
This section is specifically talking about non-native modals and pop-ups. If you have native browser alerts or confirm modals, you should use the Dismiss command.
# How to interact with the same element in multiple steps
Sometimes you may wish to do several actions over the same element, for example, you may want to check an attribute of a button, and then click on it. For doing that, you can store the element in a variable using the store element command.
store element details of "the button" in $button
see $button
assert $button.isDisplayed equals "true"
click $button
Note that sometimes you may wish to store the element in a variable, but not interact with it right away. In these instances, this same approach would come very handy.
# Best practices
Just like your favorite dish tastes better with the right ingredients, web applications thrive when they stick to the best practices. Let’s keep it fresh, up-to-date, and always with a sprinkle of testability on top.
# Aiming for software testability
Ever felt like some web apps are just easier to test than others? That's not magic, that's testability in action!
Poor testability not only increases test maintenance efforts over time but also directly impacts the number of bugs that might slip through and affect your application's performance.
# Tips for improving your application testability
- Be descriptive: Use descriptive attributes in your elements (e.g., IDs, data-test attributes, or other meaningful identifiers in your HTML elements)
- Embrace semantic HTML: Although it's a markup language and has some flexibility, structured markup not only improves the readability and maintainability of your HTML but also facilitates more reliable testing.
- Rethink iframes: Consider minimizing the use of iframes or opting for alternative solutions when possible. Although iframes can be useful, they can introduce complexity that affects both testability and the application itself.
- CSS is your friend: Prefer CSS classes over inline styles. One of the reasons is that changing the styles of a class does not alter the class name or the HTML structure, which reduces complexity and enhances testability.
- It's okay to cheat: Add test attributes or classes just for testing. It's like giving your app a secret tattoo that only testers know about!
- Accessibility is cool: Make your app friendly for everyone. Use ARIA attributes, keyboard support, and meaningful labels. It's like holding the door open for someone. It just feels good.
- Expect the unexpected: Dynamic content is prevalent in modern web applications. Ensure that different states and loading states are considered during testing to address scenarios involving asynchronous data that may impact the HTML structure.