Generator Rules

All generator rules are optional and can be set at the project level, next to component HTML files, or in both places. Generation parameters from the component-level rules override the rules defined at the project level.

Any runner or JSON generation parameter can be overridden at the source code level. See Override generated content.

Runner parameters are explained in Generation runner parameters.

The next sections explain the configuration parameters responsible for how the generator interprets HTML and affects the generated JSON.

NOTE: The supported parameters were updated in version 2.1.0. Please do not use earlier versions.

Each string value set in the configuration for elements can be a regular string (for a full match) or a regular expression for a partial match.

Public basic, container, and frame elements

A public basic element, container or frame is generated from an HTML tag if it has a certain tag name, a class attribute with a given value, a type attribute with a given value, or if an element has one of the listed attributes.

To ignore something that is configured to be included, use the optional excludeElements parameter.

Configuration parameter: publicElements

publicElements: {
        withTags: {
            input: ['actionable', 'clickable', 'editable'],
            button: ['actionable', 'clickable'],
            a: ['actionable', 'clickable'],
            area: ['actionable', 'clickable'],
            li: ['actionable', 'clickable'],
            option: ['actionable', 'clickable'],
            menuItem: ['actionable', 'clickable'],
            textarea: ['actionable', 'clickable', 'editable'],
            select: ['actionable', 'clickable', 'editable'],
            iframe: 'frame',
            td: [],
            th: [],
            title: [],
            slot: 'container',
        },
        withType: {
            button: ['actionable', 'clickable'],
            checkbox: ['actionable', 'clickable'],
            radio: ['actionable', 'clickable'],
            reset: ['actionable', 'clickable'],
            submit: ['actionable', 'clickable'],
        },
        withAttributes: {
            onclick: ['actionable', 'clickable'],
            ondblclick: ['actionable', 'clickable', 'editable'],
            onkeydown: ['actionable', 'editable'],
            onkeypress: ['actionable', 'editable'],
            onchange: ['actionable'],
        },
    }
``

For example for the following HTML:

```html
<div>
    <button type="button">Click Me!</button>
    <div onchange={handle}>test events</div>
</div>

The generator produces the following public elements:

"elements" : [
    {
        "public": true,
        "name": "button",
        "type": ["actionable", "clickable"],
        "selector": { "css": "button" }
    },
    {
        "public": true,
        "name": "div",
        "type": ["actionable"],
        "selector": { "css": "div" }
    },
]

Container element

According to default rules for public elements, a public container element is generated from a <slot> HTML element.

<template if:false="{computedShowOutputField}">
    <span class="test-id__field-value slds-size_1-of-1">
        <slot name="inputField"></slot>
        <slot></slot>
    </span>
</template>

The generator produces the following public container element:

{
    "public": true,
    "name": "inputField",
    "type": "container",
    "selector": { "css": "[slot='inputField']" }
}

Configuration parameter: containerSelector

If the slot doesn't have a name attribute, it's the default slot, so the generated selector depends on the containerSelector parameter. The content of the slot isn't processed further because a container can't have child elements.

{
    "public": true,
    "name": "slot",
    "type": "container",
    "selector": { "css": ":scope > *:first-child" }
}

Frame element

According to default rules for public elements, a frame element is generated from an <iframe> HTML element.

<div class="frame">
    <iframe src="https://www.w3schools.com" title="W3Schools Free Online Web Tutorials"></iframe>
</div>

The generator produces the following public frame element:

{
    "public": true,
    "name": "iframe",
    "type": "frame",
    "selector": { "css": "iframe" }
}

Custom elements

A custom element is generated from a custom HTML element. An HTML element is considered custom if its tag isn't part of the HTML5 standard and contains "-"; for example, <custom-element>.

<template>
    <my-custom-element>Custom content</my-custom-element>
</template>

The generator produces the following public custom element:

{
    "public": true,
    "name": "customElement",
    "type": "my-namespace/pageObjects/customElement",
    "selector": { "css": "my-custom-element" }
}

A generated element requires a type, so the generator splits a custom HTML tag by "-" and considers the first part a namespace (in our example, it's my). Then, the generator tries to find a proper type prefix for the given namespace looking at the following configuration parameters.

namespaces

First, the generator tries to find a match in the namespaces map. Let's say our config looks like this:

{
    "namespaces": {
        "my": "my-namespace/pageObjects",
        "lightning": "salesforce/lightning/pageObjects"
    }
}

Since the generator can find a map entry for our namespace prefix my, it picks the type and adds the name, so the type is my-namespace/pageObjects/component2.

defaultNamespace

If the namespaces map is empty or doesn't have a proper entry, the generator uses the preconfigured default namespace.

If there isn't a matching namespace, a custom element's type is utam/pageObjects/customElement.

Traverse content of a custom element

generateCustomContent

When the generator processes a custom HTML tag such as my-custom-tag, it can be treated as a separate HTML source to generate another UTAM page object. If this parameter is set to true and myComponent1.html contains a custom HTML tag such as <my-component2>some HTML here</my-component2>, the generator treats the content of the custom tag as another HTML source for generation and creates a component2.utam.json file.

<inputRootDir>
├── utam-generator.config.json
├── src/
   └── modules/
      ├── component1/
         └── component1.html
         └── __utam__/
            └── component1.utam.json
            └── component2.utam.json

ignoreCustomTags

For custom HTML elements with tags such as my-custom-tag, the default behavior is to generate a custom element and not traverse the body of the custom HTML element because it's considered a different component and a different page object.

Use ignoreCustomTags if you prefer to avoid a custom element and generate JSON for the body of a custom HTML element instead.

<div>
    <my-generated-component>
        <button id="number"></button>
    </my-generated-component>
</div>

By default, the generated JSON would be:

{
    "elements": [
        {
            "public": true,
            "name": "generatedComponent",
            "selector": {
                "css": "my-generated-component"
            },
            "type": "utam/pageObjects/generatedComponent"
        }
    ]
}

But if a configuration file includes "ignoreCustomTags" : ["my-generated-component"], the HTML content in the body of my-generated-component is traversed and an element is generated for the button. Here's the output:

{
    "elements": [
        {
            "public": true,
            "name": "generatedComponent",
            "selector": {
                "css": "my-generated-component"
            },
            "shadow": {
                "elements": [
                    {
                        "public": true,
                        "name": "customTest",
                        "type": "clickable",
                        "selector": {
                            "css": "button#number"
                        }
                    }
                ]
            }
        }
    ]
}

Scope elements

Some HTML elements are used to scope other elements. When an element is configured to be a scope element, the generator creates a private element.

scopeElements

scopeElements: {
        withTags: ['footer', 'form', 'header', 'label', 'section', 'table', 'tr', 'ul', 'ol', 'menu'],
        withClasses: [],
        withAttributes: ['id', 'name', 'slot'],
    },

Consider this HTML:

<table>
    <tr>
        <td>table cell</td>
    </tr>
</table>

The generator produces the following JSON, creating private scope elements for table and tr as configured. td isn't defined as a scope element so the generator creates a public tableCell element.

{
    "elements": [
        {
            "name": "table",
            "type": [],
            "selector": {
                "css": "table"
            },
            "elements": [
                {
                    "name": "tableRow",
                    "type": [],
                    "selector": {
                        "css": "tr"
                    },
                    "elements": [
                        {
                            "public": true,
                            "name": "tableCell",
                            "type": [],
                            "selector": {
                                "css": "td"
                            }
                        }
                    ]
                }
            ]
        }
    ]
}

Exclude elements from generated JSON

The excludeElements parameter is responsible for ignoring HTML elements for any type, basic, custom, or scope. It overrides publicElements and scopeElements.

excludeElements

excludeElements: {
        withTags: ['meta', 'script', 'head', 'tbody'],
        withClasses: [],
        withAttributes: []
    }

List elements

loopElements

loopElements: {
        withAttributes: ['for:each', 'for:item', 'for:iterator'],
        withTags: [],
        withClasses: []
    }

If an HTML element is located inside a loop element, it's marked as a list (the generated selector has returnAll: true).

<template for:each="{filteredTodos}" for:item="todo">
    <todo-item onupdate="{handleTodoUpdate}"></todo-item>
</template>

The generator produces the following public list element with "returnAll": true:

{
    "public": true,
    "name": "items",
    "type": "utam/pageObjects/item",
    "selector": { "css": "todo-item", "returnAll": true }
}

In addition, the generator adds a public element with an indexed selector:

{
    "public": true,
    "name": "itemByIndex",
    "type": "utam/pageObjects/item",
    "selector": {
        "css": "todo-item:nth-of-type(%d)",
        "args": [{ "name": "index", "type": "number" }]
    }
}

Some HTML elements with siblings that have the same HTML tag are also considered a list.

For example, repeated options in a select tag.

<select>
    <option>one</option>
    <option>two</option>
</select>

The generator produces the following output:

{
    "public": true,
    "name": "select",
    "type": ["actionable", "clickable", "editable"],
    "selector": { "css": "select" },
    "elements": [
        {
            "public": true,
            "name": "options",
            "type": ["actionable", "clickable"],
            "selector": { "css": "option", "returnAll": true }
        }
    ]
}

The content of the list elements isn't processed further because a list can't have child elements.

Marking page object as a root

rootSelectors

The generated JSON can be marked as a root if the root HTML element is one of: <html>, <form>, <body>. The root selector is the same as the root HTML element:

<html>
    <body>
        <!-- content -->
    </body>
</html>

Generated JSON:

{
    "root": true,
    "selector": {
        "css": "html"
    },
    "elements": []
}

Traverse frame content

traverseFrameContent

If parameter is set to true, generator will traverse content of the frame and create a separate JSON file for the content. For example for the given HTML file 'my-html' with two nested frames:

<div class="content iframe-parent">
    <iframe id="outer">
        <html>
            <body>
                <custom-tag>something</custom-tag>
                <form>
                    <input>Outer frame</input>
                </form>
                <iframe id="inner">
                    <html>
                        <body>
                            <input>Inner frame</input>
                        </body>
                    </html>
                </iframe>
            </body>
        </html>
    </iframe>
</div>

If configuration parameter is set to true, the generator produces three files:

├── __utam__/
   └── my-html.utam.json
   └── my-html_irame.utam.json // for iframe with id="outer"
   └── my-html_irame_iframe.utam.json // for iframe with id="inner"