DID THIS ARTICLE SOLVE YOUR ISSUE?
Let us know so we can improve!
Let us know so we can improve!
Elements can have different 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:
type property that references another page object that matches this nested/child component.slot or lwr-dynamic that can act as a container to inject other components whose types are unknown to the component developer.An element has properties.
name, type, and selector properties.public.wait property.load property. This element cannot have arguments.There are limitations for different elment types:
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 is false). Boolean. If set to true and the element can’t be found inside its parent, the getter method returns null. If set to false, an error is thrown if the element isn’t found. For examples, see Guide: Nullable Versus isPresent.
public (Optional, default is false) Boolean. If set to true, 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 the name property as get<Name> (the value of the name property is capitalized).
wait (Optional, default is false) Boolean. If set to true, UTAM generates a public method that waits for the element.
load (Optional, default is false) Boolean. If set to true, 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 an elements 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.
1{
2 "elements": [
3 {
4 "name": "myElement",
5 "type": ["clickable", "editable"],
6 "public": true,
7 "selector": {
8 "css": ".element"
9 },
10 "elements": []
11 }
12 ]
13}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:
1{
2 "exposeRootElement": true,
3 "type": "actionable"
4}UTAM generates a public method that returns an instance of the element to interact with.
Java:
1public MyElementElement getMyElement() {
2 // return element
3}JavaScript:
1//declaration
2getMyElement(): Promise<_BaseUtamElement>;
3
4// implementation
5async getMyElement() {
6 const driver = this.driver;
7 const root = await this.getRootElement();
8 let element = await _utam_get_myElement(driver, root, );
9 return new _ActionableUtamElement(driver, element);
10}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:
1<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 with utam- 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.
1// JSON
2"type": "utam-navex/pageObjects/one/navigationBar"
3
4// UTAM compiler transforms to:
5utam.navex.pageobjects.one.NavigationBarThis example declares a custom element called todo-item, which lives in the utam-tutorial package.
1{
2 "root": true,
3 "selector": { "css": "body" },
4 "elements": [
5 {
6 "name": "todoApp",
7 "type": "utam-tutorial/todoApp",
8 "selector": { "css": "example-todo-app" },
9 "public": true,
10 "shadow": {
11 "elements": [
12 {
13 "name": "todoItem",
14 "selector": { "css": "example-todo-item" },
15 "public": true,
16 "type": "utam-tutorial/todoItem"
17 }
18 ]
19 }
20 }
21 ]
22}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.
Important
The generated getTodoItem() method returns an object scoped inside its parent element.
JavaScript:
1getTodoApp(): Promise<_ActionableUtamElement>;
2getTodoItem(): Promise<_todoItem>;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.
1<template>
2 <div class="slds-form-element__control">
3 <span
4 class={computedOutputFieldContainerClass}
5 oninlineeditbehavioroverride={handleInlineEditBehaviorOverride}>
6 <!-- slot for a field -->
7 <slot name="outputField"></slot>
8 </span>
9 </div>
10</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 be container.public (Optional, default is false) Boolean. Must be set to true 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.nullable (Optional, default is false). Boolean. If set to true and the element can’t be found inside its parent, the getter method returns null. If set to false, an error is thrown if the element isn’t found. For examples, see Guide: Nullable Versus isPresent.wait (Optional, default is false) Boolean. If set to true, UTAM generates a public method that waits for the element.shadow (Optional) Object. A shadow boundary. Contains only an elements 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 a shadow 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:
1{
2 "elements": [
3 {
4 "name": "outputFieldContent",
5 "type": "container",
6 "public": true,
7 "selector": {
8 "css": "[slot='outputField']"
9 }
10 }
11 ]
12}UTAM generates this JavaScript code.
1async function _utam_get_outputFieldContent(driver, root) {
2 let _element = root;
3 const _locator = core.By.css("[slot='outputField']");
4 return _element.findElement(_locator);
5}
6
7async getOutputFieldContent(ContainerCtor) {
8 const driver = this.driver;
9 const root = await this.getRootElement();
10 let element = await _utam_get_outputFieldContent(driver, root, );
11 element = new ContainerCtor(driver, element);
12 return element;
13}From the test, we can load any page object inside our container.
1const 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.
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:
1<template if:false="{showThankYou}">
2 <slot>
3 <div class="form-icon">
4 <lightning-icon icon-name="utility:form"></lightning-icon>
5 </div>
6 </slot>
7</template>then UTAM Page Object can have following nested element:
1{
2 "name": "content",
3 "type": "container",
4 "public": true,
5 "elements": [
6 {
7 "name": "utilityIcon",
8 "selector": { "css": "lightning-icon" },
9 "public": true,
10 "type": "utam-lightning/pageObjects/icon"
11 }
12 ]
13}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 to frame and extends Basic element type.public (Optional, default is false) Boolean. If set to true, 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 the name property as get<Name> (the value of the name property is capitalized).selector (Required) Object. Locates the element inside its immediate parent. See Selector properties. Note that returnAll inside a selector isn’t allowed for a frame.nullable (Optional, default is false). Boolean. If set to true and the element can’t be found inside its parent, the getter method returns null. If set to false, an error is thrown if the element isn’t found. For examples, see Guide: Nullable Versus isPresent.wait (Optional, default is false) Boolean. If set to true, UTAM generates a public method that waits for the element.load (Optional, default is false) Boolean. If set to true, 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:
1{
2 "name": "myPublicFrame",
3 "public": true,
4 "type": "frame",
5 "selector": {
6 "css": "iframe"
7 }
8}The generated code returns an object of the special FrameElement type that can be used as a parameter in methods to switch between frames.
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:
1{
2 "name": "mySlowElement",
3 "selector": {
4 "css": ".slow"
5 },
6 "wait": true
7}The generated JavaScript code is:
1async waitForMySlowElement() {
2 const _result0 = await this.waitFor(async () => {
3 const _result0 = await this.__getMySlowElement();
4 return _result0;
5 });
6 return _result0;
7}If marked as public, the element will have both public getter and waitFor methods.
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:
1{
2 "name": "mySlowElement",
3 "selector": {
4 "css": ".slow"
5 },
6 "load": true
7}The generated JavaScript code is:
1async __waitForMySlowElement() {
2 const _result0 = await this.waitFor(async () => {
3 const _result0 = await this.__getMySlowElement();
4 return _result0;
5 });
6 return _result0;
7}
8
9async __beforeLoad__() {
10 await this.__waitForMySlowElement();
11}If the wait property for this element is also set to true, the waitFor<ElementName> method will also be public.
Additionally, load can’t be set to true for an element with arguments or a container element.
Note