Is there similar way to pagefactory (iOSXcuitFindBy/AndroidFindby) to handle dynamic element identifiers?

I have a hybrid app, which have the same features on iOS and Android. I am using one repository to automate both platform. There I have an alert banner, which needs to be identified in a dynamic way. I tried to pass the concatenated xpath to the @iOSXCUITFindBy annotation, but it dosent work, since the value is not present at the instantiation. How can I solve this?

   public class LoginPage extends BasePage {

    String alertmsg;

    @AndroidFindBy(xpath = "dynamic identifier....")
    @iOSXCUITFindBy**(xpath= "//XCUIElementTypeStaticText[`name == \""+alertmsg+"\"`]")**
    public WebElement alertBanner;

    @AndroidFindBy(xpath = "//android.widget.EditText[@resource-id=\"username\"]")
    @iOSXCUITFindBy(xpath = "//XCUIElementTypeTextField")
    public WebElement usernameInputField;

    @AndroidFindBy(xpath = "//android.widget.EditText[@resource-id=\"password\"]")
    @iOSXCUITFindBy(xpath = "//XCUIElementTypeSecureTextField")
    public WebElement passwordInputField;

    @AndroidFindBy(xpath = "//android.widget.Button[@text=\"Log in\"]")
    @iOSXCUITFindBy(xpath = "//XCUIElementTypeButton[@name='Log in' or @name='Anmelden']")
    public WebElement loginBtn;

    public void peformLogin(String username, String password){
        usernameInputField.click();
        usernameInputField.sendKeys(username);
        passwordInputField.click();
        passwordInputField.sendKeys(password);
        loginBtn.click();
    }

    public void setAlertBannerTxt(String  bannerTxt){
        alertmsg = bannerTxt;
    }


}
  1. Can you add examples of text or ids that you have?
// maybe something like ->
@iOSXCUITFindBy(iOSNsPredicate = "type == 'XCUIElementTypeButton' AND name CONTAINS[cd] 'accountSwitchButton_'")
  1. Using xpath you slow down your element search (specially for iOS. Android less affected)
// better use instead
    @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"username\")")
    @iOSXCUITFindBy(className = "XCUIElementTypeTextField")
    public WebElement usernameInputField;
    
    @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"password\")")
    @iOSXCUITFindBy(className = "XCUIElementTypeSecureTextField")
    public WebElement passwordInputField;
1 Like
  1. I will try this out thanks. Is there also something similar for android?

  2. Thanks a lot for the tip.

eee you need get text from alert?

    @AndroidFindBy(xpath = "TODO") // TODO
    @iOSXCUITFindBys(value = { // means first find element with id = alert, then inside element with classname = XCUIElementTypeStaticText
            @iOSXCUITBy(id = "alert"),
            @iOSXCUITBy(className = "XCUIElementTypeStaticText")
    })
    public WebElement alertBannerText;
1 Like
  1. Yes, exactly I need to compare the text from alert. You put “alert” as an id, but I dont have an ID there. Can you explain why?
  1. with iOS you have ID with wrap alert element. It is enough to find alert. We just added look for another element inside to get it text. Try!
    PS also name attribute is ID with iOS! value attribute is text.
  2. can you share page source of Android screen?
  1. I see, thanks a lot. But I have still a issue. The alert banner is always visible on the login page, but does not holds any value (text). So when I perform a login, it takes some time to render and show the alert with a message. But my test case immeadiatly takes the empty banner element to verify the text. The wait plugin does not wait, since the empty banner is visible. So I need to make sure, that the banner with given message is present or the banner value is not empty.

  2. Unfortunately, I cant share the whole page source. Do you need to see the whole tree?

  1. Show me how not visible banner looks like. It visibility attribute should we false. So you can add check el.isDispalyed() before pass any futher check. We even can add this into element search. But faster will be logic to check if it dispalyed.

  2. At least some part above and later. You can remove any private info from it

  1. Looks like this:
    image

  2. Cannot attach the xml file here.

  1. Yes. See visible false. Make logic like. Find element. Not found stop. Found then check isDispalyed(). False - stop. True - get it text and return.

  2. Write as code xml. Enter three ` chars then blank and three same chars again. Blank replace with xml.

Example

// here  xml or code
1 Like
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy index="0" class="hierarchy" rotation="0" width="1080" height="2115">
  <android.widget.FrameLayout index="0" package="xxx" class="android.widget.FrameLayout" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,0][1080,2115]" displayed="true">
    <android.widget.LinearLayout index="0" package="xxx" class="android.widget.LinearLayout" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,0][1080,2115]" displayed="true">
      <android.widget.FrameLayout index="0" package="xxx" class="android.widget.FrameLayout" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true">
        <android.widget.LinearLayout index="0" package="xxx" class="android.widget.LinearLayout" text="" resource-id="xxx:id/action_bar_root" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true">
          <android.widget.FrameLayout index="0" package="xxx" class="android.widget.FrameLayout" text="" resource-id="android:id/content" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true">
            <android.view.ViewGroup index="0" package="xxx" class="android.view.ViewGroup" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true">
              <android.view.ViewGroup index="0" package="xxx" class="android.view.ViewGroup" text="" resource-id="xxx:id/xxxletFragment" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true">
                <android.webkit.WebView index="0" package="xxx" class="android.webkit.WebView" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true">
                  <android.webkit.WebView index="0" package="xxx" class="android.webkit.WebView" text="xxx xxx" checkable="false" checked="false" clickable="false" enabled="true" focusable="true" focused="true" long-clickable="false" password="false" scrollable="true" selected="false" bounds="[0,81][1080,2115]" displayed="true" hint="">
                    <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true" hint="">
                      <android.view.View index="0" package="xxx" class="android.view.View" text="" resource-id="xxx-root" checkable="false" checked="false" clickable="true" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true" hint="">
                        <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true" hint="">
                          <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true" hint="">
                            <android.view.View index="0" package="xxx" class="android.view.View" text="" resource-id="view-stack-login" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true" hint="">
                              <android.view.View index="1" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true" hint="">
                                <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true" hint="">
                                  <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,309]" displayed="true" hint="">
                                    <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[48,129][1032,261]" displayed="true" hint="">
                                      <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[48,141][168,261]" displayed="true" hint="">
                                        <android.widget.Button index="0" package="xxx" class="android.widget.Button" text="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[48,141][168,261]" displayed="true" hint="" />
                                      </android.view.View>
                                      <android.widget.TextView index="1" package="xxx" class="android.widget.TextView" text="Login to xxx" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[168,162][912,231]" displayed="true" hint="" />
                                    </android.view.View>
                                  </android.view.View>
                                  <android.view.View index="1" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="true" selected="false" bounds="[0,309][1080,2115]" displayed="true" hint="">
                                    <android.widget.TextView index="0" package="xxx" class="android.widget.TextView" text="Please enter your user ID and your password" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[48,417][996,465]" displayed="true" hint="" />
                                    <android.view.View index="1" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[48,525][1032,1641]" displayed="true" hint="">
                                      <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,624][981,1590]" displayed="true" hint="">
                                        <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="true" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,624][981,846]" displayed="true" hint="">
                                          <android.widget.TextView index="0" package="xxx" class="android.widget.TextView" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[150,666][234,750]" displayed="true" hint="" />
                                          <android.widget.TextView index="1" package="xxx" class="android.widget.TextView" text="The entered access data are incorrect." checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[258,663][930,807]" displayed="true" hint="" />
                                        </android.view.View>
                                        <android.view.View index="1" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,894][981,1062]" displayed="true" hint="">
                                          <android.view.View index="0" package="xxx" class="android.view.View" text="User ID" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[147,912][933,972]" displayed="true" hint="" />
                                          <android.widget.EditText index="1" package="xxx" class="android.widget.EditText" text="ergt" resource-id="username" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[147,972][933,1044]" displayed="true" hint="User ID" />
                                        </android.view.View>
                                        <android.view.View index="4" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,1110][981,1278]" displayed="true" hint="">
                                          <android.view.View index="0" package="xxx" class="android.view.View" text="Password" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[147,1128][933,1188]" displayed="true" hint="" />
                                          <android.view.View index="1" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[147,1188][933,1260]" displayed="true" hint="">
                                            <android.widget.EditText index="0" package="xxx" class="android.widget.EditText" text="•••" resource-id="password" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" long-clickable="false" password="true" scrollable="false" selected="false" bounds="[147,1188][876,1260]" displayed="true" hint="Password" />
                                            <android.widget.Button index="1" package="xxx" class="android.widget.Button" text="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[873,1188][933,1260]" displayed="true" hint="" />
                                          </android.view.View>
                                        </android.view.View>
                                        <android.view.View index="7" package="xxx" class="android.view.View" text="Remember access data" resource-id="ui-id-12" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,1326][579,1398]" displayed="true" hint="" />
                                        <android.widget.CheckBox index="8" package="xxx" class="android.widget.CheckBox" text="Remember access data" checkable="true" checked="false" clickable="true" enabled="true" focusable="true" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[855,1326][981,1386]" displayed="true" hint="" />
                                        <android.widget.Button index="10" package="xxx" class="android.widget.Button" text="Log in" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,1470][981,1590]" displayed="true" hint="" />
                                      </android.view.View>
                                    </android.view.View>
                                    <android.widget.TextView index="2" package="xxx" class="android.widget.TextView" text="Other actions" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[48,1701][342,1749]" displayed="true" hint="" />
                                    <android.view.View index="3" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[48,1809][1032,2115]" displayed="true" hint="">
                                      <android.widget.Button index="0" package="xxx" class="android.widget.Button" text="Start mosaic scanner" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,1860][981,1980]" displayed="true" hint="" />
                                      <android.widget.Button index="1" package="xxx" class="android.widget.Button" text="Register more user IDs" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,2028][981,2115]" displayed="true" hint="" />
                                    </android.view.View>
                                  </android.view.View>
                                </android.view.View>
                              </android.view.View>
                            </android.view.View>
                          </android.view.View>
                        </android.view.View>
                        <android.widget.TextView index="2" package="xxx" class="android.widget.TextView" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[540,177][1080,2115]" displayed="true" hint="" />
                      </android.view.View>
                    </android.view.View>
                  </android.webkit.WebView>
                </android.webkit.WebView>
                <android.widget.FrameLayout index="1" package="xxx" class="android.widget.FrameLayout" text="" resource-id="xxx:id/customViewContainer" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,81][1080,2115]" displayed="true" />
              </android.view.ViewGroup>
            </android.view.ViewGroup>
          </android.widget.FrameLayout>
        </android.widget.LinearLayout>
      </android.widget.FrameLayout>
    </android.widget.LinearLayout>
  </android.widget.FrameLayout>
</hierarchy>

ok. so you have

                                 <android.view.View index="1" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="true" selected="false" bounds="[0,309][1080,2115]" displayed="true" hint="">
                                    <android.widget.TextView index="0" package="xxx" class="android.widget.TextView" text="Please enter your user ID and your password" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[48,417][996,465]" displayed="true" hint="" />
                                    <android.view.View index="1" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[48,525][1032,1641]" displayed="true" hint="">
                                      <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,624][981,1590]" displayed="true" hint="">
                                        <android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="true" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,624][981,846]" displayed="true" hint="">
                                          <android.widget.TextView index="0" package="xxx" class="android.widget.TextView" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[150,666][234,750]" displayed="true" hint="" />
                                          <android.widget.TextView index="1" package="xxx" class="android.widget.TextView" text="The entered access data are incorrect." checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[258,663][930,807]" displayed="true" hint="" />
                                        </android.view.View>

where we have ZERO ids. Better ask to add ids to some wrapper element:
<android.view.View index="0" package="xxx" class="android.view.View" text="" checkable="false" checked="false" clickable="true" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[99,624][981,846]" displayed="true" hint="">
OR! maybe directly to banner text element?

Thanks a lot for the help :pray:, I will ask them. Can you maybe send me a link/blog/tutorial/page/video, where I can learn more about locator strategies with factory method?

I do not have any. All learned while making similar tasks…