Page Object Elements
Elements can have different types.
Element types
If a component has an HTML element that you need in the page object, declare an element as an object in an elements
array.
You can put an element object in an elements
array at the root, inside a shadow, or nested inside a basic element.
These are the element types:
- Basic element represents standard HTML element like anchor or div and should be added only if it’s needed for user interactions or to scope other elements.
- Custom element represents a nested/child component, and therefore has a
type
property that references another page object that matches this nested/child component. - Container element represents
slot
orlwr-dynamic
that can act as a container to inject other components whose types are unknown to the component developer. - Frame element represents a frame or an iframe.
An element has properties.
- Each element type has
name
,type
, andselector
properties. - Each element type can be marked as
public
. - If it takes time for the element to load, use an explicit wait using the
wait
property. - If the page object needs to wait on the element to load, use the
load
property. This element cannot have arguments.
There are limitations for different elment types:
- Frame cannot have nested elements.
- Only basic or custom elements can be marked as nullable.
- Only basic or custom elements can have a filter.
Basic Elements
A basic element can have these properties:
-
name
(Required) String. The element name, which UTAM uses in the getter method name. The value must be unique within the JSON file. -
type
(Optional) A String for a single type or an array of strings. A list of the types of user interaction that this basic element supports. If omitted, it defaults to a base element type, which supports the methods listed in the Base Element Actions table in actions.To allow more interaction, add one or more of these values to the type array:
actionable
Exposes the methods listed in the Actionable Type Actions table.clickable
Exposes the methods listed in the Clickable Type Actions table.editable
Exposes the methods listed in the Editable Type Actions table.draggable
Exposes the methods listed in the Draggable Type Actions table.touchable
Exposes the methods for mobile device interactions listed in the Touchable Type Actions table.
-
selector
(Required) Object. Locates the element inside its immediate parent. See Selector properties. -
filter
(Optional) Object. Picks an element from a list or filters a list at run time. See Element Filters. -
nullable
(Optional, default isfalse
). Boolean. If set totrue
and the element can't be found inside its parent, the getter method returnsnull
. If set tofalse
, an error is thrown if the element isn't found. For examples, see Guide: Nullable Versus isPresent. -
public
(Optional, default isfalse
) Boolean. If set totrue
, UTAM generates a public method that returns an instance of the element with the given type. The name of the getter is generated automatically from thename
property asget<Name>
(the value of thename
property is capitalized). -
wait
(Optional, default isfalse
) Boolean. If set totrue
, UTAM generates a public method that waits for the element. -
load
(Optional, default isfalse
) Boolean. If set totrue
, UTAM generates a private method that waits for the element and automatically adds it to the beforeLoad method of the page object. -
shadow
(Optional) Object. A shadow boundary. Contains only anelements
property, which is a nested tree of objects. -
elements
(Optional) Array. Contains a nested tree of element objects that are located inside this basic element.
{
"elements": [
{
"name": "myElement",
"type": ["clickable", "editable"],
"public": true,
"selector": {
"css": ".element"
},
"elements": []
}
]
}
If an element has one basic type only, it can be defined as a string instead an array of strings. For example, this root element has the actionable
type only:
{
"exposeRootElement": true,
"type": "actionable"
}
UTAM generates a public method that returns an instance of the element to interact with.
Java:
public MyElementElement getMyElement() {
// return element
}
JavaScript:
//declaration
getMyElement(): Promise<_BaseUtamElement>;
// implementation
async getMyElement() {
const driver = this.driver;
const root = await this.getRootElement();
let element = await _utam_get_myElement(driver, root, );
return new _ActionableUtamElement(driver, element);
}
Custom Elements
A custom element has the same properties as a basic element, except that the type
property is required and must reference another page object.
-
type
(Required) String. A reference to a UTAM page object. The format is:<package name>/pageObjects/<component namespace>/<page object name>
For JavaScript, the
<package name>
and the<page object name>
are required and can include only alphanumeric characters and dashes. The path between these segments is optional. Each path segment can contain only alphanumeric characters (no dashes).For Java, the
<package name>
must start withutam-
and the/pageObjects/
segment is required. The<page object name>
must be a valid class name (it can't include dashes). It can start with a lowercase character, because the compiler transforms it to uppercase.For Java, the UTAM compiler transforms the
type
value to match Java syntax rules.-
<package name>
: transform-
into.
-
Path: transform
/
into.
; transform uppercase characters into lowercase. -
<page object name>
: transform lowercase first letter to uppercase, because Java class names always start with an uppercase character.// JSON "type": "utam-navex/pageObjects/one/navigationBar" // UTAM compiler transforms to: utam.navex.pageobjects.one.NavigationBar
-
This example declares a custom element called todo-item
, which lives in the utam-tutorial
package.
{
"root": true,
"selector": { "css": "body" },
"elements": [
{
"name": "todoApp",
"type": "utam-tutorial/todoApp",
"selector": { "css": "example-todo-app" },
"public": true,
"shadow": {
"elements": [
{
"name": "todoItem",
"selector": { "css": "example-todo-item" },
"public": true,
"type": "utam-tutorial/todoItem"
}
]
}
}
]
}
Elements should only be nested inside custom element if it mimics component code. In most cases custom element only references another page object type and everything is encapsulated inside the page object.
The generated getTodoItem()
method returns an object scoped inside its parent element.
JavaScript:
getTodoApp(): Promise<_ActionableUtamElement>;
getTodoItem(): Promise<_todoItem>;
Container Elements
There are certain requirements to declare a container element for a slot. For more information, see the guidelines in the slots guide.
Let's look at an example where we would need to declare a container element.
For example, here is a component that has a placeholder for content in an output field. In component source code, a placeholder is a slot
.
<template>
<div class="slds-form-element__control">
<span
class={computedOutputFieldContainerClass}
oninlineeditbehavioroverride={handleInlineEditBehaviorOverride}>
<!-- slot for a field -->
<slot name="outputField"></slot>
</span>
</div>
</template>
In this case, declare an element with "type": "container"
. The compiler generates a method with a parameter for the type of the component being loaded.
A container element has these properties:
name
(Required) String. An element name that's unique to this JSON file.type
(Required) String. The value must becontainer
.public
(Optional, default isfalse
) Boolean. Must be set totrue
so that UTAM generates a public method.selector
(Optional, default is"css": ":scope > *:first-child"
). A selector injected as a root for the container content.wait
(Optional, default isfalse
) Boolean. If set totrue
, UTAM generates a public method that waits for the element.shadow
(Optional) Object. A shadow boundary. Contains only anelements
property, which is a nested tree of objects.elements
(Optional) Array. Contains a nested tree of element objects that are located inside this basic element.
- As with any other element, if there's a
#shadow-root
between the basic element and the nested container element, enclose the container element in ashadow
object. Note, that slots are usualy not inside shadow root.- Most containers would not have a hardcoded selector because content is unknown, except for named slots. If a selector is omitted, a default CSS selector of
:scope > *:first-child
will be used in the generated code.
For our example, the container element is:
{
"elements": [
{
"name": "outputFieldContent",
"type": "container",
"public": true,
"selector": {
"css": "[slot='outputField']"
}
}
]
}
UTAM generates this JavaScript code.
async function _utam_get_outputFieldContent(driver, root) {
let _element = root;
const _locator = core.By.css("[slot='outputField']");
return _element.findElement(_locator);
}
async getOutputFieldContent(ContainerCtor) {
const driver = this.driver;
const root = await this.getRootElement();
let element = await _utam_get_outputFieldContent(driver, root, );
element = new ContainerCtor(driver, element);
return element;
}
From the test, we can load any page object inside our container.
const textFieldPageObject = await myPageObject.getOutputFieldContent(TextField);
If the selector inside the container is marked with "returnAll": true
, the generated container method returns an array (in JavaScript) or a list (in Java) of objects.
Elements nested inside container
Container can have nested elements if it mimics component code, for example when source component provides default content of the slot. In most cases container does not have nested elements.
For example if component has following code that puts default content inside slot:
<template if:false={showThankYou}>
<slot>
<div class="form-icon">
<lightning-icon icon-name="utility:form"></lightning-icon>
</div>
</slot>
</template>
then UTAM Page Object can have following nested element:
{
"name": "content",
"type": "container",
"public": true,
"elements": [
{
"name": "utilityIcon",
"selector": { "css": "lightning-icon" },
"public": true,
"type": "utam-lightning/pageObjects/icon"
}
]
}
Frame Elements
To load a frame or an iframe, use a frame element with the following properties:
name
(Required) String. The element name, which UTAM uses in the getter method name. The value must be unique within the JSON file.type
(Required) String. Set toframe
and extends Basic element type.public
(Optional, default isfalse
) Boolean. If set totrue
, UTAM generates a public method that returns an instance of the element with the given type. The name of the getter is generated automatically from thename
property asget<Name>
(the value of thename
property is capitalized).selector
(Required) Object. Locates the element inside its immediate parent. See Selector properties. Note thatreturnAll
inside a selector isn't allowed for a frame.wait
(Optional, default isfalse
) Boolean. If set totrue
, UTAM generates a public method that waits for the element.load
(Optional, default isfalse
) Boolean. If set totrue
, UTAM generates a private method that waits for the element and automatically adds it to the beforeLoad method of the page object.
Here's an example of a frame element:
{
"name": "myPublicFrame",
"public": true,
"type": "frame",
"selector": {
"css": "iframe"
}
}
The generated code returns an object of the special FrameElement
type that can be used as a parameter in methods to switch between frames.
Wait for an element
If an element has wait
set to true
, UTAM generates a public method that waits for the element with the name waitFor<ElementName>
.
The method uses predicate syntax and wraps the invocation of the element getter into a fluent wait.
For example for the following element:
{
"name": "mySlowElement",
"selector": {
"css": ".slow"
},
"wait": true
}
The generated JavaScript code is:
async waitForMySlowElement() {
const _result0 = await this.waitFor(async () => {
const _result0 = await this.__getMySlowElement();
return _result0;
});
return _result0;
}
If marked as public
, the element will have both public getter and waitFor
methods.
Load an element
If an element has load
set to true
, UTAM generates a private method that waits for the element with the name waitFor<ElementName>
.
The method uses predicate syntax and wraps the invocation of the element getter into a fluent wait.
This wait method is then automatically invoked in the page object's beforeLoad method.
For example for the following element:
{
"name": "mySlowElement",
"selector": {
"css": ".slow"
},
"load": true
}
The generated JavaScript code is:
async __waitForMySlowElement() {
const _result0 = await this.waitFor(async () => {
const _result0 = await this.__getMySlowElement();
return _result0;
});
return _result0;
}
async __beforeLoad__() {
await this.__waitForMySlowElement();
}
Note:
- If the
wait
property for this element is also set to true, thewaitFor<ElementName>
method will also be public. load
cannot be set to true for an element with arguments or a container element.