@WithTimeout PageFactory annotation and Implicit or Explicit waiting

Hi,
I would like to understand the waiting strategy Appium PageFactory uses before locating and interacting the element.
I am working on fixing a legacy appium automation project (Appium Java client 7.3) where WebElement field is annotated with @WithTimeout annotation like:

@WithTimeout(time = 10, chronoUnit = ChronoUnit.SECONDS)
@AndroidFindBy(id = "android:id/button2")
@iOSXCUITFindBy(accessibility = "Cancel")
public MobileElement CancelButton;

and the page object is being initiazed as:

public AbstractPage(AppiumDriver driver) {
        PageFactory.initElements(new AppiumFieldDecorator(driver) , this);
}

According to the Appium Java client documentation
If time of the waiting for elements differs from usual (longer, or shorter when element is needed only for quick checkings/assertions) then we use the above annotation.

Question 01:

I would like to understand if this waiting time in @WithTimeout is set implicitly (Appium will ALWAYS wait for 10 seconds for this element to appear/present) or this timeout is meant for explicit waiting (Appium will wait AT-MAX for 10 seconds)? Please help me understand how this waiting works.

Question 02:

Also since i am not passing the Duration or Timeout in the constructor in AppiumFieldDecorator , so will the timeout set in @WithTimeout(time = 10, chronoUnit = ChronoUnit.SECONDS) take effect in this case? or for it to work, we have to pass the Timeout as Duration inside AppiumFieldDecorator?

Question 03:

As far as i understood, passing timeout duratuion in the AppiumFieldConstructor is for setting implicit wait for web elements on the page. If this is the case, I would not want to use it and looking for ways to set explicit waiting on the web elements before I interact with them. Is this possible in PageFactory at all?
Usually I work with By locators and set explicit waiting strategies to find and interact with elements but in this case I am using PageFactory for cross platform application, so i need to find a way to use explicit waits with page factory so Appium should wait for at max of set explicit waiting duration before throwing element not found exception if element is not present or visible on the page.

I will be working on updating it to latest appium java client so I am. making a POC to apply best waiting strategies in PageFactory. I would really appreciate any inputs and helpful responses. Thank you.

References:

https://appium.github.io/java-client/io/appium/java_client/pagefactory/AppiumFieldDecorator.html#AppiumFieldDecorator-org.openqa.selenium.SearchContext-java.time.Duration-
https://appium.github.io/java-client/io/appium/java_client/pagefactory/WithTimeout.html

To make it a bit more clear. Lets make it step by step.

Base SIMPLE example

@AndroidFindBy(id = "Android_ID")
@iOSXCUITFindBy(id = "iOS_ID")
public WebElement cancelButton;

public MyPage(AppiumDriver driver) {
  // we limit search by 5 sec
  PageFactory.initElements(new AppiumFieldDecorator(driver, Duration.ofSeconds(5)), this);
}

public MySecondPage tapCancelButton() {
  Assert.assertTrue(tap(cancelButton), createAssertionLog("FAILED"));
  return new MySecondPage(driver);
}

With above SIMPLE example EVERY call to element cancelButton init first search for element with max time of 5 sec. This means if element will found faster then 5 sec, search will stop and element will be returned.
In your example you just do not set this time = Appium uses default it has.

You need control search time in many cases instead of using set in AppiumFieldDecorator. There are several way.

First way you mentioned above is add WithTimeout decorator which has higher priority. This is constant and you cant change it on fly during test.

Second way. I use code below with reinit AppiumFieldDecorator.

// example where we limit search by 3 sec
public String getNameText() {
  setLookTiming(3);
  String result = getText(nameText);
  setDefaultTiming();
  return result;
}

// or even as parameter
@Step("Check 'AccountPage' loaded {sec}")
public boolean isLoaded(int sec) {
  Logger.log("sec: '" + sec + "'");
  setLookTiming(sec);
  return super.isLoaded(mainContainer);
}

// where setLookTiming as you already guessed is
public void setLookTiming(int sec) {
  PageFactory.initElements(new AppiumFieldDecorator(driver, Duration.ofSeconds(sec)), this);
}
// while setDefaultTiming you can now write already yourself

Inside of AppiumFieldDecorator just simple commands of driver.findElement which are executed in loop with small delay with MAX time specified in AppiumFieldDecorator or WithTimeout.

Thank you Aleksei for detailed answer. Now things are more clear.

  • The time uses by default by Appium is 1 second in AppiumFieldDecorator.
  • The time set here will be applied to all the elements on page PageFactory.initElements(new AppiumFieldDecorator(driver, Duration.ofSeconds(5)), this);
  • The time set via annotation @WithTimeout will be applied to specific element and will override the time set in AppiumFieldDecorator or Default time.

If I understood correctly, this time is equivalent to waiting for presence of element in XML, and it waits for maximum of this duration. But it does not wait for element to be visible or interactable.

Let say, for cases like click() or sendkeys() etc.I want to wait explicitly for the element to be visible or interactable and I apply explicit wait of 10 seconds in addition to the wait set in @WithTimeout:

@WithTimeout(time = 5, chronoUnit = ChronoUnit.SECONDS)
@AndroidFindBy(id = "android:id/button2")
@iOSXCUITFindBy(accessibility = "Cancel")
public MobileElement CancelButton;

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
String elementText = wait.until(ExpectedConditions.visibilityOf(CancelButton)).getText();

so my question is in this case, if element is not visible, then the exception will be thrown after 10 seconds or 15 seconds? I mean will it first wait for element to be present for a max of 05 seconds, and then wait again another 10 seconds for it to be visible/interactable which means total of 15 seconds? or it will only wait for max of 10 seconds i.e. time for explicit wait?

If i am correct, then it will do as follows:
first wait for max of 05 seconds for element to be present.
If element is not present throw element not found exception
if element is present, wait for max of another 10 seconds for it to be visible

please help me understand it correctly.

your times are very close to limit: 5 + 5 = 10. All depends where WebDriverWait if finish second loop. a bit after 10 sec = stop here, a bit before then search will stop later at 15 sec.

But all logic you wrote is correct.

If you really want to use ExpectedConditions.visibilityOf better make WithTimeout = 1 sec so WebDriverWait will always stop at 10 or max 11 sec.

Note we assume that element search itself is fast. But there when there are many elements on iOS often simple search can be 5 or 10 or even 40 sec. Valid especially when we have long list of elements.
Thus WithTimeout = 1 sec will stop after first element search BUT it can be after 5/10 or 40 sec.