Help needed ExpectedConditions.invisibilityOf()

Environment
Automation Name : UIAutomator2
Appium: 1.22.3
Appium Java-client : 8.2.0

Scenario:
Registration screen, once all the data provided and submit -> loader displayed.
If everything is proper, then next screen displayed else stays on registration screen.
Registration.java

    public void enterRegistrationCardDetails(String strCreditCardNumber, String strExpDate, String strDateOfBirth) {
     enterCreditCardNumberToRegister(strCreditCardNumber);
     selectRegistrationCardExpiryDate(strExpDate);
            selectRegistrationDateOfBirth(strDateOfBirth);
            clickOnBtnRegisterForRegistration();
            new Loader().waitForLoaderToComplete();  
           //Case 1: When an incorrect data provided, then screen remain on registration screen, This case is working perfectly.
    // Case 2: When an  correct data provided, navigated to next screen, but here keeps waiting for 60 seconds for invisibile
    } 

Loader.java

    public boolean isLoaderDisplayed() {
	 try {
		_mobileElementsProgressBar.isDisplayed();
		return true;
	 } catch (NoSuchElementException | StaleElementReferenceException e) {
		return false;
	}

    public void waitForLoaderToComplete() {
    	if (isLoaderDisplayed()) {
    		System.out.println("LOADING DISPLAYED");
    		boolean isLoaderBarDisappear = new WebDriverWait(getBaseMobileDriver(), Duration.ofSeconds(60)).until(ExpectedConditions.invisibilityOf(_mobileElementsProgressBar));
    		System.out.println("LOADING COMPLETED " + isLoaderBarDisappear);
    	}
    }

how you search for above element?

// this way will not WORK
_mobileElementsProgressBar = driver.findElement(some_locator);

// you should use instead
new WebDriverWait(getBaseMobileDriver(), Duration.ofSeconds(60)).until(ExpectedConditions. invisibilityOfElementLocated(ProgressBar_element_locator));
// or use pageObject model with element finding via annotation with limiting search time
    @WithTimeout(time = 1, unit = TimeUnit.SECONDS)
    @iOSXCUITFindBy(id = "some_ID") // you do not have iOS app -> delete this line
    @AndroidFindBy(id = "some_ID")
    private WebElement _mobileElementsProgressBar;

PS i did a bit brush up your message to looke better

I am using Page object model with factory to locate element
@AndroidFindBy(uiAutomator = “new UiSelector().className(“android.widget.ProgressBar”)”)
private WebElement _mobileElementsProgressBar;

first try to change it like:

@WithTimeout(time = 1, unit = TimeUnit.SECONDS)
@AndroidFindBy(className = "android.widget.ProgressBar")
private WebElement _mobileElementsProgressBar;

second try something like:

        long startTime = System.currentTimeMillis();
        do {
           try {
             // this action takes MAX 1 sec per WithTimeout
             // check element found and hidden
             if (!_mobileElementsProgressBar.isDisplayed())
               return true;
           } catch (NoSuchElementException | StaleElementReferenceException e) {
               // element NOT found or stale
               return true;
           }
            sleep(1000);
            System.out.println("Waiting progressBar to hide ...");
        } while (System.currentTimeMillis() < startTime + 60 * 1000); // 60 sec
        return false;

Tried the given solution, But still not working

@WithTimeout(time = 1, chronoUnit = ChronoUnit.SECONDS)
    @AndroidFindBy(className = "android.widget.ProgressBar")
    private WebElement _mobileElementsProgressBar;
    
    @SneakyThrows
    public boolean waitForLoaderToComplete() {
        long startTime = System.currentTimeMillis();
        do {
            try {
                if (!_mobileElementsProgressBar.isDisplayed()) {
                    System.out.println("not displayed ");
                    return true;
                }
            } catch (NoSuchElementException | StaleElementReferenceException e) {
                System.out.println(" progress bar displayed ");
                return true;
            }
            Thread.sleep(1000);
            System.out.println("Waiting progressBar to hide ...");
        } while (System.currentTimeMillis() < startTime + 60 * 1000); // 60 sec
        return false;
    }

I am attaching ref image for same.

Progress_Bar

Consider what “not present” means for your particular case.

An element could be not present in the XML tree, in this case it does not EXIST.

It could also be present there but would not be VISIBLE.

There could also be a situation when the element still exists and is reported as displayed to the accessibility framework, but it is not ACTUALLY visible. In this situation I would try to confirm its state by the fact that another page/element, which is supposed to replace the previous one, exists on the current view.

1 Like

Show page source after fail so we can check where is this bar hidden.

1 Like

Thanks @mykola-mokhnach @Aleksei for assistance
Now the issue resolved. Actually, on next screen also there was progress bar.

Progress Bar on Screen 1:
<android.widget.ProgressBar index="0" package="com..." class="android.widget.ProgressBar" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[480,1060][600,1180]" displayed="true" />

Progress Bar on Screen 2:
This progress bar displayed left to the timer displayed on next screen.
<android.widget.ProgressBar index="7" package="com..." class="android.widget.ProgressBar" text="5.0" resource-id="com../pb_timer" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[253,1040][303,1090]" displayed="true" />

So now after changing the loactor to
@WithTimeout(time = 1, chronoUnit = ChronoUnit.SECONDS)
@AndroidFindBy(xpath = “//android.widget.ProgressBar[@text=”"]")

public void waitForLoaderToComplete() {
	if (isElementPresent(_mobileElementsProgressBar)) {
		try {
			boolean isLoaderBarDisappear = new WebDriverWait(getBaseMobileDriver(), Duration.ofSeconds(60)).until(ExpectedConditions.invisibilityOf(_mobileElementsProgressBar));
			System.out.println("LOADING COMPLETED " + isLoaderBarDisappear);
		} catch (Exception exception) {
			System.out.println("LOADING NOT COMPLETED false ");
		}
	}
}

Once again thanks.

better use something like:

@AndroidFindBys({ // this is chain search means find first then inside second and so on if we add more
        @AndroidBy (id = "page_id"),
        @AndroidBy (className = "android.widget.ProgressBar")
})

where page_id is id of screen (some parent element of screen).

1 Like

Thank you so much @Aleksei