I am trying to work with a webpage in Selenium 4. The page has a few iframes and I am trying to wait for an iframe to load completely and then switch to it.
However, the code below doesn't seem to work:
driver = new ChromeDriver(options);
driver.get("https://www.stagecoachliquor.com/online-store-1/Whiskey-c20954043");
WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(30));
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.cssSelector("#TPASection_iw75naz9 > iframe")));
System.out.println(driver.getPageSource());
The system out just prints an empty HTML snippet below:
<html><head></head><body></body></html>
As a result, when I try to select any element after the switch, it fails. The iframe is loading alright in the chrome window which seems strange to me. I have tried implicit wait as well which did not work and had the same result.
After a few hours of debugging, I have not been able to identify the root cause. Any help is much appreciated.
Best, R
CodePudding user response:
I've reproduced the issue.
This behavior looks like a selenium bug, because, when it switches to frame, the frame has no any product elements (they are loaded a few seconds later). But then, when I was in debug and all the products loaded, and a I call driver.getPageSource()
, the result is <html><head></head><body></body></html>
, and when I call this again, it loads the correct page source, but still the driver cannot find any element inside the iframe.
So, I've added a custom expected condition, which switches to frame and check if some element present for workaround this.
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.Test;
import static java.time.Duration.ofSeconds;
public class ChromeIframeTest {
@Test
public void test() {
// I use https://github.com/bonigarcia/webdrivermanager lib for download chromedriver
WebDriverManager.chromedriver().setup();
ChromeOptions options = new ChromeOptions();
WebDriver driver = new ChromeDriver(options);
driver.get("https://www.stagecoachliquor.com/online-store-1/Whiskey-c20954043");
WebDriverWait wait = new WebDriverWait(driver, ofSeconds(30));
wait.until(
frameToBeAvailableAndSwitchToItAndElementToBeAvailable(
By.cssSelector("#TPASection_iw75naz9 > iframe"),
By.cssSelector(".grid-product__shadow") // product in iframe
)
);
System.out.println(driver.getPageSource());
driver.quit();
}
// Custom expected condition
public static ExpectedCondition<Boolean> frameToBeAvailableAndSwitchToItAndElementToBeAvailable(
By frame, By frameElement) {
return new ExpectedCondition<>() {
private boolean isLoaded = false;
@Override
public Boolean apply(WebDriver driver) {
if (ExpectedConditions.frameToBeAvailableAndSwitchToIt(frame).apply(driver) != null) {
isLoaded = ExpectedConditions.presenceOfAllElementsLocatedBy(frameElement).apply(driver) != null;
}
if (!isLoaded) {
driver.switchTo().defaultContent();
}
return isLoaded;
}
@Override
public String toString() {
return String.format("element \"%s\" should present in frame \"%s\", is present: \"%b\"", frameElement.toString(), frame.toString(), isLoaded);
}
};
}
}
CodePudding user response:
The root cause is your condition is always true, iframe is available after you get the HTML. You can simply add a Thread.sleep
to verify it.
For now: I can't find any condition that is suited for your situation.
WebDriver driver = new ChromeDriver();
driver.get("https://www.stagecoachliquor.com/online-store-1/Whiskey-c20954043");
Thread.sleep(10000);
driver.switchTo().frame(driver.findElement(By.cssSelector("#TPASection_iw75naz9 > iframe")));
System.out.println(driver.getPageSource());
driver.quit();