Interfaces

UTAM provides the ability to declare a page object as an interface to abstract page object APIs (declared public methods) from the implementation.

One of the most common use cases is for mobile: a page or component in a mobile application can be WebView or pure native. Creating a page object for a WebView page is the same as creating a page for desktop. However, for a native page, you can create an interface that's shared between the iOS and Android platforms, and have implementations of the interface for both platforms.

Interface Declaration

Declare an interface in a separate JSON file.

An interface can contain these properties:

Here's an interface with four methods.

{
    "interface": true,
    "methods": [
        {
            "name": "openMenuSection"
        },
        {
            "name": "clickTabBarItem",
            "args": [
                {
                    "name": "item",
                    "type": "string"
                }
            ]
        },
        {
            "name": "hasTabBarItem",
            "returnType": "boolean",
            "args": [
                {
                    "name": "item",
                    "type": "string"
                }
            ]
        },
        {
            "name": "getTabBarItems",
            "returnType": ["clickable"],
            "returnAll": true
        }
    ]
}

For every declared interface, the UTAM Java compiler generates a Java interface with method declarations:

public interface NavTabBar extends RootPageObject {

  void openMenuSection();

  void clickTabBarItem(String item);

  Boolean hasTabBarItem(String item);

  List<GetTabBarItemsElement> getTabBarItems();

  interface GetTabBarItemsElement extends Clickable {}
}

Interface Implementation

A page object declares that it implements a declarative interface by using the implements property.

{
    "implements": "utam-lightning/pageObjects/global/navigationMenu",
    "elements": [],
    "methods": []
}

The page object must:

Let's look at an example of a page object that has different implementations for different mobile platforms. To support multiple implementing JSON page objects for a single UTAM interface, use the profile property. This property enables UTAM to choose the appropriate implementation depending on the test context. Profile is an array of objects. Set a platform property in profile to define the supported platforms. The supported values for platform are:

For example, addConnAndroidImpl.utam.json defines a shared implementation for Android phone and tablet.

{
   "implements":"utam-salesforceapp/pageObjects/authentication/addConn",
   "profile":[
      {
         "platform": ["android_phone", "android_tablet"]
      }
   ],
   ....
}

addConnAndroidPhoneImpl.utam.json is for Android phone only.

{
   "implements":"utam-salesforceapp/pageObjects/authentication/addConn",
   "profile":[
      {
         "platform": "android_phone"
      }
   ],
   ....
}

addConniOSImpl.utam.json is for iOS phone and tablet.

{
   "implements":"utam-salesforceapp/pageObjects/authentication/addConn",
   "profile":[
      {
         "platform": ["ios_phone", "ios_tablet"]
      }
   ],
   ...
}

addConniOSTabletImpl.utam.json is for iPad only.

{
   "implements":"utam-salesforceapp/pageObjects/authentication/addConn",
   "profile":[
      {
         "platform": "ios_tablet"
      }
   ],
   ...
}

In the compiler configuration file, you must configure all possible profile values for your module. Otherwise, the compiler throws an error, such as:

utam.core.framework.consumer.UtamError: profile 'platform' does not support value 'android_tablet'

Here's an example of a Java compiler configuration file in compiler.config.json with a profiles property. The configuration file name can be anything. It must just match the name passed as a runtime parameter to the compiler.

{
  ....
  "profiles": [
    {
      "name": "platform",
      "values": [
        "ios_phone",
        "ios_tablet",
        "android_phone",
        "android_tablet"
      ]
    }
  ]
}

The compiler configuration file defines the supported platform values.

Interface Implementation Resolution at Run Time

When an instance of the page object is created in a test at runtime, UtamLoader decides which implementating class should be instantiated. UtamLoader references a dependency injections config file, which is a JSON file that declares pairs of an interface and an implementation for each profile.

The dependency injections config is created during page object generation and looks like this:

{
    "platform": {
        "ios_tablet": [
            {
                "interface": "utam.pageObjects.auth.AddConn",
                "implementation": "utam.pageObjects.auth.AddConnIOSTabletImpl"
            }
        ],
        "android_phone": [
            {
                "interface": "utam.pageObjects.auth.AddConn",
                "implementation": "utam.pageObjects.auth.AddConnAndroidPhoneImpl"
            }
        ]
    }
}

This particular config tells UtamLoader that if the active profile is "ios_tablet", and UtamLoader.load(AddConn.class) is invoked, use the AddConnIOSTabletImpl implementation class.

For more details about setting active profiles and using depenency injections configs, please check out the Java guide and the JavaScript guide.

Default implementation

If an implementing page object doesn't have a profile property, it's considered a default implementation and is picked up by default.

{
    "default": {
        "impl": [
            {
                "interface": "utam.pageObjects.auth.AddConn",
                "implementation": "utam.pageObjects.auth.AddConnCrossPlatformImpl"
            }
        ]
    }
}