0

I wrote the below method in my Page.class for reusing implicit wait.

public WebDriver waitForElementToLoad(WebElement element)
{
        WebDriverWait wait = new WebDriverWait(driver, 60);
        wait.until(ExpectedConditions.presenceOfElementLocated((By) element));
        return (driver);
}

In my test.class I am using page factory elements, for example:

//Save button
@FindBy(xpath = "//*[@*='Save']")
private WebElement saveButton;

Now I am trying to call: waitForElementToLoad(saveButton); from test.Class but I am getting below error.

"java.lang.ClassCastException: class com.sun.proxy.$Proxy12 cannot be cast to class org.openqa.selenium.By (com.sun.proxy.$Proxy12 and org.openqa.selenium.By are in unnamed module of loader 'app')"

I also tried

WebElement saveButton = driver.findElement(By.xpath("//*[@*='Save']"));
waitForElementToLoad(saveButton);

but no luck.

How can I make this work?

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
Afsal
  • 404
  • 3
  • 7
  • 24

3 Answers3

1

WebDriverWait is explicit wait, not implicit. And you can't cast WebElement to By.

If saveButton is not null than it was already found by the page factory, waiting for it presence is meaningless, that's why you don't have an overload with WebElement. Wait for visibility instead

wait.until(ExpectedConditions.visibilityOf(element));
Guy
  • 46,488
  • 10
  • 44
  • 88
  • This works, Thanks. Now if I have a dynamic xpath like: driver.findElement(By.xpath("(//a[@class='profile-link-label' and text()='"+userName+"'])[1]")); and I want to pass it as parameter to the same method. How can I do that? – Afsal Feb 18 '20 at 10:17
  • @user194258 You need to send the `WebElement` returned, or build a new method that receive `By` instead of web element and use `ExpectedConditions.visibilityOfElementLocated(By.xpath("(//a[@class='profile-link-label' and text()='"+userName+"'])[1]"))` – Guy Feb 18 '20 at 10:25
  • For the new method that receive By, what will be the datatype if it's not WebElement? Also, when I send the parameter, should I be sending driver.findElement(By.xpath("(//a[@class='profile-link-label' and text()='"+userName+"'])[1]")); or just (//a[@class='profile-link-label' and text()='"+userName+"'])[1]") ? – Afsal Feb 18 '20 at 11:10
  • @user194258 You send `By`, `By.xpath("...")` – Guy Feb 18 '20 at 11:12
0

If you need to use ExpectedConditions with some your "Page" class that contains asynchronously loaded elements I would recommend to use the following approach:

  1. Extend AjaxElementLocator where override protected boolean isElementUsable(WebElement element) so that it uses ExpectedConditions to decide if the element is ready for use
  2. Extend AjaxElementLocatorFactory where override public ElementLocator createLocator(Field field) method so that it now returns the instance of your extended class
  3. Where you initialize elements use PageFactory.initElements(new MyAjaxElementLocatorFactory(driver, 10), this); where MyAjaxElementLocatorFactory is the class that you created on step 2
Alexey R.
  • 8,057
  • 2
  • 11
  • 27
0

This error message...

java.lang.ClassCastException: class com.sun.proxy.$Proxy12 cannot be cast to class org.openqa.selenium.By (com.sun.proxy.$Proxy12 and org.openqa.selenium.By are in unnamed module of loader 'app')

...implies that the ClassCastException occured while trying to cast a proxy object.

You need to consider a few things as follows:

  • The wait you introduced within waitForElementToLoad() isn't implicit wait as such but an ExplicitWait. See: What is the internal working difference between Implicit Wait and Explicit Wait
  • presenceOfElementLocated() doesn't garuntees the visibility or interactablity of any element. So for visibility or interactablity you need to use the matching ExpectedConditions either visibilityOf(WebElement element) or elementToBeClickable(By locator). For details, see: Selenium: How selenium identifies elements visible or not? Is is possible that it is loaded in DOM but not rendered on UI?
  • If your usecase involves inducing WebDriverWait you pass the WebElement saveButton in the form of a proxy object within waitForElementToLoad() as follows:

    package com.pol.zoho.PageObjects;
    import org.openqa.selenium.By;
    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.ExpectedConditions;
    import org.openqa.selenium.support.ui.WebDriverWait;
    
    public class ZohoLoginPage {
    
        WebDriver driver;
        public ZohoLoginPage(WebDriver driver)
        {
        PageFactory.initElements(driver, this);
        }
    
        //Save button
        @FindBy(xpath = "//*[@*='Save']")
        private WebElement saveButton;
    
        public void waitForElementToLoad()
        {
        WebElement element = new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(ZohoLoginPage.getWebElement()));
        saveButton.click();
        }
    
        public WebElement getWebElement()
        {
        return saveButton;
        }
    
    }
    

Reference

You can find a couple of relevant discussions in:

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