Selenium 4 : frameToBeAvailableAndSwitchToIt doesn't seem to work - java

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

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);
}
};
}
}

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();

Related

Selenium: Don't understand how to write correct xpath for IMDB top movies

I am trying to click on the IMDB Top Rated Movies which is under "Movies, TV & Showtimes" but I don't understand how to write the correct and precise xpath for it. I am not able to click on the Top Rated Movies part.
Below is the code:
driver.get("http://www.imdb.com");
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
Actions action = new Actions(driver);
action.moveToElement(driver.findElement(By.id("navTitleMenu"))).build().perform();
driver.findElement(By.xpath("//li[#id='navTitleMenu']/div/div[2]/ul[1]/li[6]/a")).click();
Help me out. Thanks already.
That's what worked in my case:
driver.get("http://www.imdb.com");
new WebDriverWait(driver, 5000)
.until(ExpectedConditions.visibilityOfElementLocated(By.id("navTitleMenu")));
new Actions(driver)
.clickAndHold(driver.findElement(By.id("navTitleMenu")))
.moveToElement(driver.findElement(By.linkText("Top Rated Movies")))
.click()
.build().perform();
The problem is that when you move to navTitleMenu - you should continue performing your actions, as focus will be lost from dropdown (so it will be closed)
do you create the xpath yourself i recommend using chrome to create the xpath maybe the problem is that your xpath is incorrect try this:
//*[#id="navMenu1"]/div[2]/ul[1]/li[6]/a
or if not maybe you should wait a little to get the page load done try
try {
// thread to sleep for 5 seconds
Thread.sleep(5000);
} catch (Exception e) {
System.out.println(e);
}
then
driver
.findElement(By.xpath("//*#id="navMenu1"]/div[2]/ul[1]/li[6]/a")).click();
First of all,
Forget about the implicit wait, It is not needed since IMDB is a stable site
Add explicit wait after you have performed the mousehover. It will definitely work for you.
Try the exact code mentioned below, Working fine with my browser.
Please change the gecko driver path according to location in your directory
package com.imdb.top;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class Imdb
{
public static void main(String... args)
{
System.setProperty("webdriver.gecko.driver",
"C:\\Users\\thinksysuser\\Downloads\
\geckodriver-v0.18.0-win64\\geckodriver.exe");
WebDriver driver = new FirefoxDriver();
driver.get("http://www.imdb.com");
Actions action = new Actions(driver);
action.moveToElement(driver.findElement(By.id("navTitleMenu")))
.build().perform();
WebDriverWait wait = new WebDriverWait(driver, 60, 50);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(".//*
[#id='navMenu1']/div[2]/ul[1]/li[6]/a"))).click();
driver.findElement(By.xpath(".//*
[#id='navMenu1']/div[2]/ul[1]/li[6]/a")).click();
}
}

(Should be easy) Selenium chromedriver (java) no element found error

I tried this exact code on other websites and it seems to work fine. It's just on pizza hut that it can't even locate an element let alone click on it. Thread.sleep() doesn't make a difference. The problem is between the commented *, according to the compiler. Here's the code.
package training;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class PizzaHut {
WebDriver driver;
#Test
public void open() throws Exception {
//SET UP WEBDRIVER AND OPEN WEBSITE
System.setProperty("webdriver.chrome.driver","/Users/1mr4n/Downloads/chromedriver");
WebDriver driver = new ChromeDriver();
driver.get("https://www.pizzahut.com/#/home");
Thread.sleep(1000);
//CLICK PIZZA
Thread.sleep(5000);
test("before");
//**************PROBLEM*CODE**********************
driver.findElement(By.xpath("//a[#id='lg-nav-pizza']")).click();
//************************************************
test("clicked pizza");
//CLOSE BROWSER
Thread.sleep(15000);
driver.close();
}
public static void test(String x) {
System.out.println(x);
}
}
I just ran the code below and it worked just fine. No need for sleep or waits and I'm using Chrome also.
driver.get("https://www.pizzahut.com");
driver.findElement(By.id("lg-nav-pizza")).click();
Try this one ,definitely it help you.
package training;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class PizzaHut {
WebDriver driver;
#Test
public void open() throws Exception {
//SET UP WEBDRIVER AND OPEN WEBSITE
// System.setProperty("webdriver.chrome.driver","/Users/1mr4n/Downloads/chromedriver");
WebDriver driver = new ChromeDriver();
driver.get("https://www.pizzahut.com/#/home");
Thread.sleep(1000);
//CLICK PIZZA
Thread.sleep(5000);
test("before");
List<WebElement> webElements=driver.findElement(By.cssSelector("div.btn-group.btn-group-lg.btn-group-justified.ph-header-navigation"))
.findElements(By.cssSelector("div.btn.btn-link.ph-ghost-padding.ng-scope"));
for (WebElement element : webElements) {
if (element.getAttribute("title").equals("PIZZA")) {
element.click();
}
}
Thread.sleep(5000);
test("clicked pizza");
//CLOSE BROWSER
Thread.sleep(15000);
driver.close();
}
public static void test(String x) {
System.out.println(x);
}
}
You are not able to click it because it is an AngularJS tag, hence it takes some time to load: add some wait like below and it works (Here i'm just adding thread.sleep for the sake of simplicity,I've increased it to 15000ms):
//CLICK PIZZA
Thread.sleep(15000);
test("before");
//**************PROBLEM*CODE**********************
driver.findElement(By.xpath("//a[#id='lg-nav-pizza']")).click();

Unable to locate element radio botton in Selenium webdriver

I am a newbie trying to learn automation using the tool Selenium. I am trying to automate this website -
http://newtours.demoaut.com/
where I login and try to access this radio button (one way, round way )for flight finder.
But i am getting the error Unable to locate the element.
Tried the following.
Tried to locate the element using Xpath obtained from firebug.
Used the following Xpath composed from the html code to locate the radio button
//*[#type='radio']//*[#value='oneway']
//*[contains(#type,'radio')]
//*[contains(text(),'oneway']
//input[#type='radio' AND #value='oneway']
Also tried CSS selector to locate the element.
driver.findElement(By.cssSelector("input[type=radio][value=oneway]"))
Tried adding wait times using implicit wait and thread.sleep
The HTML script for the radio button as obtained from firebug is -
input type="radio" checked="" value="roundtrip" name="tripType"
Round Trip
input type="radio" value="oneway" name="tripType"
One Way
Given below is my code -
package gurutrial2;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class gurutrial2
{
public static WebDriver driver;
#BeforeTest
public final void preTest() {
System.setProperty("webdriver.firefox.marionette", "C:/Users/serajendran/Downloads/geckodriver-0.10.0 (1)");
DesiredCapabilities capabilities = DesiredCapabilities.firefox();
capabilities.setCapability("marionette", true);
driver = new FirefoxDriver(capabilities);
driver.get("http://newtours.demoaut.com/");
driver.manage().window().maximize();
System.out.println(driver.getTitle());
Assert.assertEquals("Welcome: Mercury Tours", driver.getTitle());
}
#Test
public final void login() {
driver.findElement(By.name("userName")).sendKeys("invalidUN");
driver.findElement(By.name("password")).sendKeys("invalidPW");
driver.findElement(By.name("login")).click();
System.out.println("login in progress");
}
#Test
public final void flightFinder() {
WebDriverWait wait = new WebDriverWait(driver, 30);
WebElement oneWayRadioButton = wait.until(ExpectedConditions.elementToBeClickable(By.linkText("oneway")));
oneWayRadioButton.click();
System.out.println("Clicked One Way");
}
}
Any help would be deeply appreciated.
//*[#type='radio']//*[#value='oneway'] - you're looking for an element of type radio and value oneway.. this xpath look for an element of type radio that has a child element with value oneway.
//*[contains(#type,'radio')] - you'll get multiple results for this
//*[contains(text(),'oneway'] - the text is not oneway, only the value attribute is oneway, the text contains 'One Way'
//input[#type='radio' AND #value='oneway'] - this should work if you change 'AND' to 'and'
Following solution worked for me on the newtour site -
driver.findElement(By.cssSelector("input[value='oneway']")).click();
Actually the problem is in your test Methods
In TestNG the execution of #Test methods is in alphabetic order by default. So in your code flightFinder() method executing before login() So even you are using right locator to click on radio button, It will show the exception.
Solution:
Maintains your method name in alphabetic order
Use priority under #Test annotation for the methods e.g. - #Test(priority = 1)
Create dependency test e.g. -
#Test()
public final void login()
{
//code
}
#Test(dependsOnMethods={"login"})
public final void flightFinder()
{
//code
}
Update your code as below and try -
#Test
public final void doLogin() {
driver.findElement(By.name("userName")).sendKeys("invalidUN");
driver.findElement(By.name("password")).sendKeys("invalidPW");
driver.findElement(By.name("login")).click();
System.out.println("login in progress");
}
#Test()
public final void flightFinder() {
driver.findElement(By.xpath("//input[#type='radio' and #value='oneway']")).click();
System.out.println("Clicked One Way");
}

Unable to switch between two browser windows using Selenium WebDriver

I am new to WebDriver, i am facing an issue on browser window switching.
I googled for my query resolution and the answer i found best is still not working for me.
Here is my code :
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.BeforeSuite;
public class FrameWorkBase {
public static WebDriver driver;
public static WebDriverWait wait;
public static String firstWindow,secondWindow;
#BeforeSuite
public void startDriver() throws Exception{
driver= new FirefoxDriver(); // this firefox window is to open survey
driver.manage().window().maximize();
wait=new WebDriverWait(driver, 40);
driver.get("http://www.cricinfo.com");
firstWindow=driver.getWindowHandle();
driver=new FirefoxDriver();
driver.manage().window().maximize();
driver.get("https://translate.google.co.in/");
secondWindow=driver.getWindowHandle();
System.out.println("First window handle :" + firstWindow);
System.out.println("\n Second window handle :" + secondWindow);
driver.switchTo().window(firstWindow);
System.out.println("hello");
}
}
I am getting an error on execution as Unable to find window 'xyz' where 'xyz' is the name of first window.
Even i am printing the window name and it is displaying the same window for which it is displaying error.
Please suggest me what i am doing wrong here.
Thanks
This is happening because you have reinitialized the driver instance.
driver=new FirefoxDriver();
driver.manage().window().maximize();
driver.get("https://translate.google.co.in/");
This line has reinitialised your driver instance so what ever u try to do you won't find the window handle. If you are trying to work on both websites simultaneously, try to create another object of driver like WebDriver driver2 = new FirefoxDriver();
#Vivek has aptly answered your question. But, if you still want to open a link in a new window, you can try the below code for that:
Actions act = new Actions();
WebElement link = driver.findElement(By.xpath("//xpath of the link"));
//Opening the link in new window (works in FF and Chrome)
act.contextClick(link).sendKeys(Keys.ARROW_DOWN).sendKeys(Keys.ARROW_DOWN).sendKeys(Keys.ENTER).build().perform();
And you can switch in between them accordingly, with the use of handles. Furthermore, this link will help you handle two windows simultaneously.

Maintain and re-use existing webdriver browser instance - java

Basically every time I run my java code from eclipse, webdriver launches a new ie browser and executes my tests successfully for the most part. However, I have a lot of tests to run, and it's a pain that webdriver starts up a new browser session every time. I need a way to re-use a previously opened browser; so webdriver would open ie the first time, then the second time, i run my eclipse program, I want it to simply pick up the previous browser instance and continue to run my tests on that same instance. That way, I am NOT starting up a new browser session every time I run my program.
Say you have 100 tests to run in eclipse, you hit that run button and they all run, then at about the 87th test you get an error. You then go back to eclipse, fix that error, but then you have to re-run all 100 test again from scratch.
It would be nice to fix the error on that 87th test and then resume the execution from that 87th test as opposed to re-executing all tests from scratch, i.e from test 0 all the way to 100.
Hopefully, I am clear enough to get some help from you guys, thanks btw.
Here's my attempt below at trying to maintain and re-use a webdriver internet explorer browser instance:
public class demo extends RemoteWebDriver {
public static WebDriver driver;
public Selenium selenium;
public WebDriverWait wait;
public String propertyFile;
String getSessionId;
public demo() { // constructor
DesiredCapabilities ieCapabilities = DesiredCapabilities
.internetExplorer();
ieCapabilities
.setCapability(
InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS,
true);
driver = new InternetExplorerDriver(ieCapabilities);
this.saveSessionIdToSomeStorage(getSessionId);
this.startSession(ieCapabilities);
driver.manage().window().maximize();
}
#Override
protected void startSession(Capabilities desiredCapabilities) {
String sid = getPreviousSessionIdFromSomeStorage();
if (sid != null) {
setSessionId(sid);
try {
getCurrentUrl();
} catch (WebDriverException e) {
// session is not valid
sid = null;
}
}
if (sid == null) {
super.startSession(desiredCapabilities);
saveSessionIdToSomeStorage(getSessionId().toString());
}
}
private void saveSessionIdToSomeStorage(String session) {
session=((RemoteWebDriver) driver).getSessionId().toString();
}
private String getPreviousSessionIdFromSomeStorage() {
return getSessionId;
}
}
My hope here was that by overriding the startSession() method from remoteWebdriver, it would somehow check that I already had an instance of webdriver browser opened in i.e and it would instead use that instance as opposed to re-creating a new instance everytime I hit that "run" button in eclipse.
I can also see that because I am creating a "new driver instance" from my constructor, since constructor always execute first, it creates that new driver instance automatically, so I might need to alter that somehow, but don't know how.
I am a newbie on both stackoverflow and with selenium webdriver and hope someone here can help.
Thanks!
To answer your question:
No. You can't use a browser that is currently running on your computer. You can use the same browser for the different tests, however, as long as it is on the same execution.
However, it sounds like your real problem is running 100 tests over and over again. I would recommend using a testing framework (like TestNG or JUnit). With these, you can specify which tests you want to run (TestNG will generate an XML file of all of the tests that fail, so when you run it, it will only execute the failed tests).
Actually you can re-use the same session again..
In node client you can use following code to attach to existing selenium session
var browser = wd.remote('http://localhost:4444/wd/hub');
browser.attach('df606fdd-f4b7-4651-aaba-fe37a39c86e3', function(err, capabilities) {
// The 'capabilities' object as returned by sessionCapabilities
if (err) { /* that session doesn't exist */ }
else {
browser.elementByCss("button.groovy-button", function(err, el) {
...
});
}
});
...
browser.detach();
To get selenium session id,
driver.getSessionId();
Note:
This is available in Node Client only..
To do the same thing in JAVA or C#, you have to override execute method of selenium to capture the sessionId and save it in local file and read it again to attach with existing selenium session
I have tried the below steps to use the same browser instance and it worked for me:
If you are having generic or Class 1 in different package the below code snippet will work -
package zgenerics;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.BeforeTest;
import org.openqa.selenium.WebDriver;
// Class 1 :
public class Generics {
public Generics(){}
protected WebDriver driver;
#BeforeTest
public void maxmen() throws InterruptedException, IOException{
driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
String appURL= "url";
driver.get(appURL);
String expectedTitle = "Title";
String actualTitle= driver.getTitle();
if(actualTitle.equals(expectedTitle)){
System.out.println("Verification passed");
}
else {
System.out.println("Verification failed");
} }
// Class 2 :
package automationScripts;
import org.openqa.selenium.By;
import org.testng.annotations.*;
import zgenerics.Generics;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
public class Login extends Generics {
#Test
public void Login() throws InterruptedException, Exception {
WebDriverWait wait = new WebDriverWait(driver,25);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("")));
driver.findElement(By.cssSelector("")).sendKeys("");
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("")));
driver.findElement(By.xpath("")).sendKeys("");
}
}
If your Generics class is in the same package you just need to make below change in your code:
public class Generics {
public Generics(){}
WebDriver driver; }
Just remove the protected word from Webdriver code line. Rest code of class 1 remain as it is.
Regards,
Mohit Baluja
I have tried it by extension of classes(Java Inheritance) and creating an xml file. I hope below examples will help:
Class 1 :
package zgenerics;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.BeforeTest;
import org.openqa.selenium.WebDriver;
public class SetUp {
public Generics(){}
protected WebDriver driver;
#BeforeTest
public void maxmen() throws InterruptedException, IOException{
driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
String appURL= "URL";
driver.get(appURL);
String expectedTitle = "Title";
String actualTitle= driver.getTitle();
if(actualTitle.equals(expectedTitle)){
System.out.println("Verification passed");
}
else {
System.out.println("Verification failed");
} }
Class 2 :
package automationScripts;
import org.openqa.selenium.By;
import org.testng.annotations.Test;
import zgenerics.SetUp
public class Conditions extends SetUp {
#Test
public void visible() throws InterruptedException{
Thread.sleep(5000);
boolean signINbutton=driver.findElement(By.xpath("xpath")).isEnabled();
System.out.println(signINbutton);
boolean SIGNTEXT=driver.findElement(By.xpath("xpath")).isDisplayed();
System.out.println(SIGNTEXT);
if (signINbutton==true && SIGNTEXT==true){
System.out.println("Text and button is present");
}
else{
System.out.println("Nothing is visible");
}
}
}
Class 3:
package automationScripts;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.testng.annotations.Test;
public class Footer extends Conditions {
#Test
public void footerNew () throws InterruptedException{
WebElement aboutUs = driver.findElement(By.cssSelector("CssSelector"));
aboutUs.click();
WebElement cancel = driver.findElement(By.xpath("xpath"));
cancel.click();
Thread.sleep(1000);
WebElement TermsNCond = driver.findElement(By.xpath("xpath"));
TermsNCond.click();
}
}
Now Create an xml file with below code for example and run the testng.xml as testng suite:
copy and paste below code and edit it accordingly.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="TestSuite" parallel="classes" thread-count="3">
<test name="PackTest">
<classes>
<class name="automationScripts.Footer"/>
</classes>
This will run above three classes. That means one browser and different tests.
We can set the execution sequence by setting the class names in alphabetical order as i have done in above classes.

Categories

Resources