5

Is there a way to wait for an element not present in Selenium using PageFactory annotations?

When using:

@FindBy(css= '#loading-content')
WebElement pleaseWait;

to locate the element, and then:

wait.until(ExpectedConditions.invisibilityOfElementLocated(pleaseWait));

I would get:

org.opeqa.selenium.WebElement cannot be converted to org.openqa.selenium.By

I am able to do what I need by using:

wait.until(ExpectedConditions.invisibilityOfElementLocated(By.cssSelector('loading-content')));

However, I would like to be able to use the PageFactory annotations in order to keep the framework consistent. Is there a way to do this?

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
Mate Mrše
  • 7,997
  • 10
  • 40
  • 77

4 Answers4

3

When using PageFactory in PageObjectModel if you expect the element to be invisible, you can use the Explicit Wait support with a normal locator factory and use either of the following solutions:


invisibilityOfElementLocated()

invisibilityOfElementLocated() is the implementation for an expectation for checking that an element is either invisible or not present on the DOM. It is defined as follows:

public static ExpectedCondition<java.lang.Boolean> invisibilityOfElementLocated(By locator)
An expectation for checking that an element is either invisible or not present on the DOM.

Parameters:
    locator - used to find the element

Returns:
    true if the element is not displayed or the element doesn't exist or stale element
  • Code Block:

    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import org.openqa.selenium.support.PageFactory;
    import org.openqa.selenium.support.ui.WebDriverWait;
    
    public class fooPage {
    
        WebDriver driver;
        public fooPage(WebDriver driver)
        {
            PageFactory.initElements(driver, this);
        }
    
        //you don't need this
        //@FindBy(css= '#loading-content')
        //WebElement pleaseWait;
    
        public void foo()
        {
            Boolean bool = new WebDriverWait(driver, 20).until(ExpectedConditions.invisibilityOfElementLocated(By.cssSelector('#loading-content')));
            //other lines of code
        }
    }
    

As an alternative you can also use the invisibilityOf() method as follows:

invisibilityOf()

invisibilityOf() is the implementation for an expectation for checking the element to be invisible. It is defined as follows:

public static ExpectedCondition<java.lang.Boolean> invisibilityOf(WebElement element)
An expectation for checking the element to be invisible

Parameters:
    element - used to check its invisibility

Returns:
    Boolean true when elements is not visible anymore
  • Code Block:

    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import org.openqa.selenium.support.PageFactory;
    import org.openqa.selenium.support.ui.WebDriverWait;
    
    public class fooPage {
    
        WebDriver driver;
        public fooPage(WebDriver driver)
        {
            PageFactory.initElements(driver, this);
        }
    
    
        @FindBy(css= '#loading-content')
        WebElement pleaseWait;
    
        public void foo()
        {
            Boolean bool = new WebDriverWait(driver, 20).until(ExpectedConditions.invisibilityOf(fooPage.getWebElement()));
            //other lines of code
        }
    
        public WebElement getWebElement()
        {
            return pleaseWait;
        }
    }
    

You can find a detailed discussion in How to use explicit waits with PageFactory fields and the PageObject pattern

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
2

invisibilityOfElementLocated is expecting a locator but you are sending a web-element and that is why it is throwing an error. You can perform the operation by checking the webelement list by using:

wait.until(ExpectedConditions.invisibilityOfAllElements(Arrays.asList(pleaseWait)));

Updated Answer:
If you want to check that the element is not present on the page then you can check its list size is equal to 0 or not, as its list size will be 0 when its not displayed on the UI.

You can get the list of the element by using:

@FindBy(css='#loading-content')
List<WebElement> pleaseWait;

And you can check the list size equals to 0 by using:

if(pleaseWait.size()==0){
     System.out.println("Element is not visible on the page");
     // Add the further code here
}

And this would not give NoSuchElement exception as well.

Sameer Arora
  • 4,439
  • 3
  • 10
  • 20
  • No, doesn't work. I'm getting `Expected condition failed: waiting for invisibility of all elements [Proxy element for: DefaultElementLocator 'By.cssSelector: #loading-content'] (tried for 5 second(s) with 500 miliseconds interval)`. However, it seems that the next step is being executed. – Mate Mrše Feb 13 '19 at 12:31
  • Yes, so i guess it gave the error because it was expecting the element to be invisible but it is visible. – Sameer Arora Feb 13 '19 at 12:33
  • Because the method worked and the next line of code also got executed. – Sameer Arora Feb 13 '19 at 12:33
  • This is the continuation of the above message: `Caused by: org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"#loading-content"}`. I don't think it worked as expected. Why would it throw an exception than? – Mate Mrše Feb 13 '19 at 12:35
  • If you are getting `NoSuchElementException`, that means either the element is not visible on the page or your css is incorrect through which you are finding the element. Please tell me what is the scenario that you want to verify here then i would be able to help you better. – Sameer Arora Feb 13 '19 at 12:37
  • There is a "please wait while loading" element present while the page is loading. I'm waiting for page load before clicking an element. The locator is correct, but I suspect it is somehow too fast for selenium to notice. – Mate Mrše Feb 13 '19 at 12:40
  • I have updated by answer, please check that, i have posted alternative approach to solving your problem. – Sameer Arora Feb 13 '19 at 12:55
  • Please try that and let me know if that helps :) – Sameer Arora Feb 13 '19 at 13:11
1

You can use the correct expected condition, also:

wait.until(ExpectedConditions.invisibilityOf(pleaseWait));

Reference.

Hope it helps you!

Ratmir Asanov
  • 6,237
  • 5
  • 26
  • 40
  • I'm waiting for the element *not* to be visible. – Mate Mrše Feb 13 '19 at 12:18
  • @MateMrše, yes, I see. I have updated my answer. Check it, please, again. Thanks. – Ratmir Asanov Feb 13 '19 at 12:18
  • I'm getting `Expected condition failed: waiting for invisibility of Proxy element for: DefaultElementLocator 'By.cssSelector: #loading-content' (tried for 5 second(s) with 500 miliseconds interval)` error message. It seems that element is gone too quickly. – Mate Mrše Feb 13 '19 at 12:25
0

By default, invisibilityOf doesn't return true if the element is not present on the page. (NoSuchElementException)

public static ExpectedCondition<Boolean> invisibilityOf(final WebElement element) {
return new ExpectedCondition<Boolean>() {
    public Boolean apply(WebDriver webDriver) {
        return ExpectedConditions.isInvisible(element);
    }

    public String toString() {
        return "invisibility of " + element;
    }
};
}

you can create a method in your WebDriverUtils class that you can use instead:

public static ExpectedCondition<Boolean> invisibilityOf(final WebElement element) {
return new ExpectedCondition<>() {
    public Boolean apply(WebDriver webDriver) {
        try {
            return !element.isDisplayed();
        } catch (NoSuchElementException | StaleElementReferenceException e) {
            return true;
        }
    }

    public String toString() {
        return "invisibility of " + element;
    }
};
}

similar to invisibilityOfElementLocated(final By locator)

Bijin Abraham
  • 1,709
  • 2
  • 11
  • 25
validng
  • 1
  • 1