I want to pass my WebDriver to another class instead of passing it to the individual methods within that class. That would mean passing it to the constructor of the class when I create an instance of it. Here is my code, and my issue further below -
public class StepDefinitions{
public static WebDriver driver = null;
CustomWaits waits;
#Before("#setup")
public void setUp() {
driver = utilities.DriverFactory.createDriver(browserType);
System.out.println("# StepDefinitions.setUp(), driver = " + driver);
waits = new CustomWaits(driver);
}
}
public class CustomWaits {
WebDriver driver;
public CustomWaits(WebDriver driver){
this.driver = driver;
}
public boolean explicitWaitMethod(String id) {
boolean status = false;
try {
WebDriverWait wait = new WebDriverWait(driver, 30);
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(id)));
status = element.isDisplayed();
} catch (NullPointerException e){
e.printStackTrace();
}
return status;
}
}
The error I am getting in is NullPointerException when a method of that class is called within an #Given, #When, etc. This is a scope issue I cannot resolve.
Feature File:
#test
Feature: Test
#setup
Scenario: Navigate to Webpage and Assert Page Title
Given I am on the "google" Website
Then page title is "google"
Here is the step definition:
#Given("^element with id \"([^\"]*)\" is displayed$")
public void element_is_displayed(String link) throws Throwable {
if (waits.explicitWaitMethod(link)) {
// This is where waits becomes null when I put a breakpoint
driver.findElement(By.id(link)).isDisplayed();
} else {
System.out.println("Timed out waiting for element to display");
}
}
I would do something like this.
public class StepDefinitions{
public StepDefinitions() {
driver = utilities.DriverFactory.createDriver(browserType);
System.out.println("# StepDefinitions.setUp(), driver = " + driver);
waits = new CustomWaits(driver);
}
public static WebDriver driver = null;
public static CustomWaits waits;
#Given("^element with id \"([^\"]*)\" is displayed$")
public void element_is_displayed(String link) throws Throwable {
if (waits.explicitWaitMethod(link)) {
// This is where waits becomes null when I put a breakpoint
driver.findElement(By.id(link)).isDisplayed();
} else {
System.out.println("Timed out waiting for element to display");
}
}
}
public class CustomWaits {
private static WebDriver driver;
public CustomWaits(WebDriver driver){
this.driver = driver;
}
public boolean explicitWaitMethod(String id) {
boolean status = false;
try {
WebDriverWait wait = new WebDriverWait(driver, 30);
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(id)));
status = element.isDisplayed();
} catch (NullPointerException e){
e.printStackTrace();
}
return status;
}
}
Related
I'm writing search engine. For control http errors I use Selenium WebDriver. Created a class HTTPExceptoinController for this:
public final class HTTPExceptoinController {
private static HTTPExceptoinController instance;
private static String originalWindow;
private static ChromeOptions options;
private static WebDriver driver;
private HTTPExceptoinController() {}
public static synchronized HTTPExceptoinController getInstance() {
if (instance==null) {
instance = new HTTPExceptoinController();
setController();
}
return instance;
}
In the same class I made a method "openNewTab". How can I make each thread close its own tab without synchronization?
public synchronized String openNewTab(String link) {
driver.switchTo().newWindow(WindowType.TAB);
String page=null;
try {
driver.get(link);
String currentwindow = driver.getWindowHandle();
driver.switchTo().window(currentwindow);
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(3));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.tagName("a")));
page = driver.getPageSource();
driver.manage().deleteAllCookies();
driver.close();
} catch (Exception e) {
driver.close();
driver.switchTo().window(originalWindow);
}
driver.switchTo().window(originalWindow);
return page;
}
I tried to do this with ConcurrentHashMap; put in "mapWindow" WindowHandle and System.currentTimeMillis(), but it didn't work.
private static void stopwatch() {
Thread worker = new Thread(new Runnable() {
public void run() {
while(Status.isRun()) {
Set<String> allWindows = driver.getWindowHandles();
Iterator<String> i = allWindows.iterator();
while(i.hasNext()){
String childwindow = i.next();
if(mapWindow.containsKey(childwindow)){
long timeout = mapWindow.get(childwindow);
timeout=timeout+5000;
if(System.currentTimeMillis()>=timeout) {
mapWindow.remove(childwindow);
driver.switchTo().window(childwindow);
((JavascriptExecutor) driver).executeScript("window.stop;");
driver.close();
}
}
try { Thread.sleep(1000);}
catch (InterruptedException e) {}
}
}}});
worker.start();
}
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
java.lang.NullPointerException is appearing
(1 answer)
Getting error exception in thread "main" java.lang.NullPointerException
(2 answers)
java.lang.NullPointerException using static WebDriver instance
(1 answer)
Closed 2 years ago.
I am new to the selenium automation world, Getting java.lang.NullPointerException error which trying to call the function to take a screenshot in a test method on Selenium. I am pretty sure I have missed initialize or return the driver somewhere. below is the code.
baseTest class where I am initializing the webdriver
public class baseTest {
public Properties objProp = new Properties();
FileInputStream readProp;
{
try {
readProp = new FileInputStream("C:\\Users\\kiran\\IdeaProjects\\SelProject2\\src\\test\\java\\appDetails.properties");
objProp.load(readProp);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
protected WebDriver driver;
#BeforeClass
public void beforeSuite()
{
System.setProperty(objProp.getProperty("browser"),objProp.getProperty("driverPath"));
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.get(objProp.getProperty("URL"));
}
#AfterSuite
public void afterSuite()
{
if(null != driver)
{
driver.close();
driver.quit();
}
}
public WebDriver getDriver()
{
return driver;
}
}
All my tests extends the baseTest function link shown below
public class loginFunction extends baseTest {
guruLoginPage objLogin;
guruHomePage objHome;
takeScreenshot objSS = new takeScreenshot(driver);
//readExcel readObj;
#Test
public void performLogin() throws IOException, InterruptedException {
objLogin = new guruLoginPage(driver);
String loginPageTitle = objLogin.getTitle();
Assert.assertTrue(loginPageTitle.contains("Demo Site"));
objSS.screenshot();
objLogin.loginAction(objProp.getProperty("appUsername"), objProp.getProperty("appPassword"));
Thread.sleep(2000);
objHome = new guruHomePage(driver);
Assert.assertTrue(objHome.validateLogin().contains("Manger Id : mngr242657"));
Thread.sleep(2000);
objSS.screenshot();
}
}
when i write this piece of code in the loginfunction, it works fine, but when i am trying to optimize and put it in a separate class and call the method I am getting java.lang.NullPointerException error
public class takeScreenshot{
WebDriver driver;
public takeScreenshot(WebDriver driver)
{
this.driver=driver;
PageFactory.initElements(driver,this);
}
public void screenshot() throws IOException {
String fileSuffix = DateTime.now().toString("yyyy-dd-MM-HH-mm-ss");
TakesScreenshot ss = ((TakesScreenshot)this.driver);
File srcFile = ss.getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(srcFile,new File("C:\\Users\\kiran\\IdeaProjects\\SelProject2\\src\\test\\java\\Screenshots\\"+fileSuffix+".jpg"));
}
}
I have a page that contains a bunch of tables. I loop through the tables in the outer loop and then loop through each row in the table in the inner loop. It all works fine. But some of the pages have a Next button. When I add code to click that after completing the page, then I start getting StaleElementReferenceException while looping through the rows of a table.
Here is the code:
WebDriverWait wait1 = new WebDriverWait(driver, 10000);
WebElement maxPage = null;
WebElement auctionsWaitingDiv = driver.findElement(By.cssSelector("div[class='Head_W']"));
if (auctionsWaitingDiv.isDisplayed() == false) return properties;
try {
maxPage = wait1.until(ExpectedConditions.visibilityOfElementLocated(By.id("maxWA")));
} catch (TimeoutException ex) {
return properties;
}
Integer maxPageNo = 1;
if (!maxPage.getText().isEmpty())
maxPageNo = Integer.parseInt(maxPage.getText());
for (int i = 1; i <= maxPageNo; i++) {
driver.findElement(By.cssSelector("div[id='Area_W']")); //only look at Auctions Waiting section
WebDriverWait wait2 = new WebDriverWait(driver, 10000);
List<WebElement> tables = null;
try {
tables = wait2.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.cssSelector("table[class='ad_tab']")));
} catch (TimeoutException ex) {
System.out.println("table not found in allotted time");
return properties;
} catch (StaleElementReferenceException ex) {
System.out.println("returning due to StaleElementReferenceException");
return properties;
}
for (WebElement table: tables) {
List<String> propAttributes = new ArrayList<>();
// StaleElementReferenceException: The element reference of
// <table class="ad_tab"> is stale; either the element is no
// longer attached to the DOM, it is not in the current
// frame context, or the document has been refreshed
List<WebElement> rows = table.findElements(By.cssSelector("tr"));
String parcelLink = "";
for (WebElement row : rows) {
WebElement key = row.findElement(By.cssSelector("th"));
WebElement val = row.findElement(By.cssSelector("td"));
String keyVal = key.getText() + val.getText();
propAttributes.add(keyVal);
if (key.getText().equals("Parcel ID:")) {
WebElement a = val.findElement(By.cssSelector("a"));
parcelLink = a.getAttribute("href");
}
}
}
driver.findElement(By.xpath(".//*[#class='PageRight']")).click(); //click the "Next" button
}
What I don't understand is why the stale element is happening at all? The page is not changing during the loop and I've waited until all elements have been fetched. How to avoid the StaleElementReferenceException?
Edit: The last stack trace shows it is happening in this line:
List<WebElement> rows = table.findElements(By.cssSelector("tr"));
and the error message above it shows:
SEVERE: null
org.openqa.selenium.StaleElementReferenceException: The element reference of <table class="ad_tab"> is stale; either the element is no longer attached to the DOM, it is not in the current frame context, or the document has been refreshed
The StaleElementReferenceException is thrown whenever you want to access an element reference which is not available anymore. This happens when the element is no longer attached to the DOM or if the page was updated.
The solution for this is just searching for the element again whenever this happens.
You could adapt all your tests or page objects. Or you write your own RobustWebDriver and RobustWebElement which refreshes the element if a SERE is thrown.
RobustWebDriver:
public class RobustWebDriver implements WebDriver {
private WebDriver originalWebDriver;
public RobustWebDriver(WebDriver webDriver) {
this.originalWebDriver = webDriver;
}
#Override
public void get(String url) {
this.originalWebDriver.get(url);
}
#Override
public String getCurrentUrl() {
return this.originalWebDriver.getCurrentUrl();
}
#Override
public String getTitle() {
return this.originalWebDriver.getTitle();
}
#Override
public List<WebElement> findElements(By by) {
List<WebElement> elements = new ArrayList<>();
for (WebElement element : this.originalWebDriver.findElements(by)) {
elements.add(new RobustWebElement(element, by, this));
}
return elements;
}
#Override
public WebElement findElement(By by) {
return new RobustWebElement(this.originalWebDriver.findElement(by), by, this);
}
#Override
public String getPageSource() {
return this.originalWebDriver.getPageSource();
}
#Override
public void close() {
this.originalWebDriver.close();
}
#Override
public void quit() {
this.originalWebDriver.quit();
}
#Override
public Set<String> getWindowHandles() {
return this.originalWebDriver.getWindowHandles();
}
#Override
public String getWindowHandle() {
return this.originalWebDriver.getWindowHandle();
}
#Override
public TargetLocator switchTo() {
return this.originalWebDriver.switchTo();
}
#Override
public Navigation navigate() {
return this.originalWebDriver.navigate();
}
#Override
public Options manage() {
return this.originalWebDriver.manage();
}
}
RobustWebElement:
public class RobustWebElement implements WebElement {
private WebElement originalElement;
private RobustWebDriver driver;
private By by;
private static final int MAX_RETRIES = 10;
public RobustWebElement(WebElement element, By by, RobustWebDriver driver) {
this.originalElement = element;
this.by = by;
this.driver = driver;
}
#Override
public void click() {
int retries = 0;
while (retries < MAX_RETRIES) {
try {
this.originalElement.click();
return;
} catch (StaleElementReferenceException ex) {
refreshElement();
}
retries++;
}
throw new StaleElementReferenceException(
String.format("Element is still stale after %s retries.", MAX_RETRIES));
}
#Override
public void sendKeys(CharSequence... keysToSend) {
int retries = 0;
while (retries < MAX_RETRIES) {
try {
this.originalElement.sendKeys(keysToSend);
return;
} catch (StaleElementReferenceException ex) {
refreshElement();
}
retries++;
}
throw new StaleElementReferenceException(
String.format("Element is still stale after %s retries.", MAX_RETRIES));
}
// TODO add other unimplemented methods with similar logic.
private void refreshElement() {
this.originalElement = driver.findElement(by);
}
And then you just need to wrap your WebDriver into the RobustWebDriver and you are ready to go:
WebDriver driver = new RobustWebDriver(new ChromeDriver());
EDIT:
Of course you need to take care of scrolling up and down by yourself.
Well after tearing my hair out for a day, I finally realized what was happening. It should have been obvious to me. When the "Next" button is clicked, it takes some time for the new page to load. By simply adding a delay, the new DOM is loaded and processing begins on it, not again on the previous one!
driver.findElement(By.xpath(".//*[#class='PageRight']")).click();
try {
Thread.sleep(4000); //provide some time for the page to load before processing it
} catch (InterruptedException ex) {
Logger.getLogger(RealAuction.class.getName()).log(Level.SEVERE, null, ex);
}
Now it runs to completion with no StaleElementReferenceException.
I created a custom ExpectedCondition to be used as input in my wait.until() method, however when my code reaches my custom ExpectedCondition argument, a NullPointerException is thrown, and I cannot figure out why. I've tried everything, and the same result is always received. Below, you will find my code
CustomWait:
public static ExpectedCondition<Boolean> visibilityOfElement(final
WebElement element) {
return new ExpectedCondition<Boolean>() {
#Override
public Boolean apply(WebDriver input) {
try {
return element.isDisplayed();
}catch(NoSuchElementException e) {
return false;
}catch(StaleElementReferenceException e1) {
return false;
}
}
};
}
}
LoginPage (this page contains the code that calls the CustomWait class method):
public class LoginPage {
WebDriver driver;
WebDriverWait wait;
#FindBy(how=How.ID, using="email") WebElement email;
#FindBy(how=How.ID, using="password") WebElement password;
#FindBy(how=How.ID, using="submit-button") WebElement loginSubmitButton;
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public void login(String email, String password) {
wait.until(CustomWait.visibilityOfElement(this.email));
this.email.sendKeys(email);
this.password.sendKeys(password);
loginSubmitButton.click();
}
}
When the program reaches the code "wait.until(CustomWait.visibilityOfElement(this.email))", that is when the NullPointerException is thrown, and I believe that the "WebDriver input" part of my parameter for the visibilityOfElement method of the Custom Wait class is where the problem lies, but I cannot figure out why.
Main(this is where my test is found):
public class Main {
WebDriver driver;
public Main() {
driver = BrowserFactory.startBrowser("chrome",
"http://123help123.com/");
}
#Test
public void smokeTest() {
HomePage homePage = PageFactory.initElements(driver,
HomePage.class);
homePage.clickLogin();
LoginPage loginPage = PageFactory.initElements(driver,
LoginPage.class);
loginPage.login("haha", "123");
}
}
BrowserFactory (this is how my driver is created):
public class BrowserFactory {
static WebDriver driver;
public static WebDriver startBrowser(String browser, String url) {
if(browser.equalsIgnoreCase("firefox")) {
driver = new FirefoxDriver();
}
else if(browser.equalsIgnoreCase("chrome")) {
if(SystemUtils.IS_OS_MAC_OSX) {
System.setProperty("webdriver.chrome.driver",
"src/chromedriver");
}
else if(SystemUtils.IS_OS_WINDOWS) {
System.setProperty("webdriver.chrome.driver",
"src/chromedriver.exe");
}
driver = new ChromeDriver();
}
else if(browser.equalsIgnoreCase("ie")) {
driver = new InternetExplorerDriver();
}
driver.manage().window().maximize();
driver.get(url);
return driver;
}
}
Any help is greatly appreciated, and if you need more information, then please let me know.
You got NullPointerException because you haven't initialized wait in your LoginPage class. So there is not driver to pass to your custom ExpectedCondition.
public LoginPage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver,5);
}
If the button doesn't exist then the test would hang for much longer than 5 seconds.
The method findElement() in DefaultElementLocator was invoked ~63 times!
The deeper the nesting of the blocks, the longer the waiting time.
Is it possible to use blocks this way in htmlElements?
What am I doing wrong?
#Test
public void myTestFunc() {
WebElement element = myPage.getMyForm()
.getSubForm()
.getButton()
.getWrappedElement();
try {
(new WebDriverWait(driver, 5))
.until(ExpectedConditions.visibilityOf(element));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public class MyPage {
#FindBy(className = "...")
private MyForm myForm;
public MyPage(WebDriver driver){
PageFactory.initElements(new HtmlElementDecorator(driver), this);
}
public MyForm getMyForm() {
return myForm;
}
}
public class MyForm extends HtmlElement {
#FindBy(className = "...")
private MySubForm mySubForm;
public MySubForm getMySubForm() {
return mySubForm;
}
}
public class MySubForm extends HtmlElement {
#FindBy(className = "...")
private MyButtonWrap button;
public MyButtonWrap getButton() {
return button;
}
}
public class MyButtonWrap extends Button {
public MyButtonWrap(WebElement wrappedElement) {
super(wrappedElement);
}
// ...
}
I think the problem is related to the implicit wait which is set to a default of 5 seconds. See this issue for more details.
What I think is happening is that when you try to get the wrappedElement:
myPage.getMyForm().getSubForm().getButton().getWrappedElement();
it is implicitly waiting for 5s for each #FindBy.
Try putting print statements and seeing where the time is being spent, e.g.:
public void myTestFunc() {
System.out.println("start");
element = myPage.getMyForm().getSubForm().getButton().getWrappedElement();
System.out.println("got element");
try {
(new WebDriverWait(driver, 5)).until(visibilityOf(element));
System.out.println("Finished waiting successfully");
} catch (Exception ex) {
ex.printStackTrace();
}
}