Getting StaleElementReferenceException trying one valid and one invalid login - selenium

I am trying to write a test for valid and invalid login in same test class but I am getting StaleElementReferenceException. Below is the code
public class LoginTest {
static{
System.setProperty("webdriver.chrome.driver", "./driver/chromedriver.exe");
}
public static void main(String[] args) throws InterruptedException {
WebDriver driver = new ChromeDriver();
driver.get("http://app.meltwaterbuzz.com/");
driver.manage().window().maximize();
LoginPage login = new LoginPage(driver);
System.out.println(driver.getTitle());
login.setUsername("username");
login.setPassword("password");
login.clickSignIn();
login.setUsername("username");
login.setPassword("password");
login.clickSignIn();
System.out.println(driver.getTitle());
//driver.close();
}
}
Any idea how to handle this?

Generally we will be getting the Stale Exception if the element attributes or something is changed after initiating the webelement. For example, in some cases if user tries to click on the same element on the same page but after page refresh, gets staleelement exception.
To overcome this, we can create the fresh webelement in case if the page is changed or refreshed. Below code can give you some idea.
Example:
webElement element = driver.findElement(by.xpath("//*[#id='StackOverflow']"));
element.click();
//page is refreshed
element.click();//This will obviously throw stale exception
To overcome this, we can store the xpath in some string and use it to create a fresh webelement as we go.
String xpath = "//*[#id='StackOverflow']";
driver.findElement(by.xpath(xpath)).click();
//page has been refreshed. Now create a new element and work on it
driver.fineElement(by.xpath(xpath)).click(); //This works
Hope this helps you. Thanks.

Here is the Answer to your Question:
In your code block you are creating an object login of LoginPage class, filling up username, password and you are clicking on SignIn button. Once you click on SignIn button you are logged in as a registered user and redirected to a new page. Here the page/url have changed and the HTML DOM have also changed.
Next you are again trying to pass username through login.setUsername("username") again and Selenium is unable to find the same locator (id/name/css/xpath) on this new page. Hence Selenium complains of StaleElementReferenceException which essentially means the locator you are looking for is no more present on the HTML DOM or have become stale.
The solution to your issue would be to Logout and come back again to the initial url http://app.meltwaterbuzz.com/.
Let me know if this Answers your question.

I found the way to overcome StaleElementReferenceException, If we use page object model then we can overcome it. Here is the example.
POM Class
public class LoginPage {
//declaration
#FindBy(id="username")
private WebElement usernameTxt;
#FindBy(name = "pwd")
private WebElement passwordTxt;
#FindBy(css="#loginButton")
private WebElement loginBTN;
//Initialization
public LoginPage(WebDriver driver) {
PageFactory.initElements(driver, this);
}
//Utilization
public void setUserName(String un) {
usernameTxt.sendKeys(un);
}
public void setPassword(String pw) {
passwordTxt.sendKeys(pw);
}
public void clickLogin() {
loginBTN.click();
}
}
Test Class
public class LoginTest {
public static void main(String[] args) throws InterruptedException {
System.setProperty("webdriver.chrome.driver", "./driver/chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.get("http://localhost/login.do");
LoginPage login = new LoginPage(driver);
login.setUserName("zahid");
login.setPassword("manager");
login.clickLogin();
Thread.sleep(2000);
login.setUserName("admin");
login.setPassword("manager");
login.clickLogin();
}
}

Related

Getting as "Cannot invoke "org.openqa.selenium.WebDriver.findElement(org.openqa.selenium.By)" because "this.driver" is null."

I am trying to run a page object methods by cucumber. I am getting error out with driver null NPE errpr. Could you please help. I created Page Object Model file. A feature file describing scenario outline. And then in Test, I am trying to call the method by creating a object of Page Model java class
POM class:
package pages;
import javax.imageio.IIOException;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class Pom {
static WebDriver driver;
By username=By.xpath("//*[#id='user-name']");
By password=By.xpath("//*[#id='password']");
By loginbutton=By.xpath("//*[#id='login-button']");
By logout=By.xpath("//*[contains(text(),'Logout')]");
By open_menu=By.xpath("//*[contains(text(),'Open Menu')]");
public Pom(WebDriver driver)
{
this.driver=driver;
//if (!driver.getTitle().equals("Swag Labs")) {
//throw new IIOException("This is not the login page. The current url is " +driver.getCurrentUrl());
}
//}
public void enter_username(String username1)
{
driver.findElement(username).sendKeys(username1);
}
public void enter_password(String password1)
{
driver.findElement(password).sendKeys(password1);
}
public void click_login()
{
driver.findElement(loginbutton).click();
}
public void check_logout()
{
driver.findElement(logout).isDisplayed();
}
public void click_open_menu()
{
driver.findElement(open_menu).click();
}
}
StepDefination:
package StepDefinations;
import java.time.Duration;
import javax.imageio.IIOException;
import org.junit.BeforeClass;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import io.cucumber.java.en.*;
import io.github.bonigarcia.wdm.WebDriverManager;
import pages.Pom;
public class PomTest {
public WebDriver driver;
Pom pom=new Pom(driver);
#Given("User launches the browser")
public void user_launches_the_browser() {
// Write code here that turns the phrase above into concrete actions
WebDriverManager.chromedriver().setup();
driver=new ChromeDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(6));
driver.manage().window().maximize();
}
#Given("user navigate to the Login landing Page")
public void user_navigate_to_the_login_landing_page() throws InterruptedException {
// Write code here that turns the phrase above into concrete actions
driver.navigate().to("https://www.saucedemo.com/");
Thread.sleep(3000);
}
#When("^User enters the credentials as (.+) and (.+) $")
public void user_enters_the_credentials_as_and(String username, String password) throws Throwable {
// Write code here that turns the phrase above into concrete actions
// pom=new Pom(driver);
pom.enter_username(username);
Thread.sleep(3000);
pom.enter_password(password);
Thread.sleep(3000);
}
#And("User clicks on the login button after entering credentials")
public void user_clicks_on_the_login_button_after_entering_credentials() {
pom.click_login();
}
#Then("User lands on the user account home page and checks the logout button")
public void user_lands_on_the_user_account_home_page_and_checks_the_logout_button() {
// Write code here that turns the phrase above into concrete actions
pom.click_open_menu();
pom.check_logout();
}
#Then("the user closes the browser")
public void the_user_closes_the_browser() {
// Write code here that turns the phrase above into concrete actions
driver.close();
}
}
Faeture File:
Feature: Login functionality Test
Scenario Outline: Verify the login of the user on the particvular website
Given User launches the browser
And user navigate to the Login landing Page
When User enters the credentials as <username> and <password>
And User clicks on the login button after entering credentials
Then User lands on the user account home page and checks the logout button
And the user closes the browser
Examples:
|username|password|
|standard_user|secret_sauce|
|locked_out_user|secret_sauce|
|problem_user|secret_sauce|
|performance_glitch_user|secret_sauce|
I am expecting this code to run in a data driven way.
#user3857859 , in your step definition file, move the initialization of pom object to inside #Given("User launches the browser"), after you initialize the driver. so after changes your #Given should look like below:
public WebDriver driver;
Pom pom;
#Given("User launches the browser")
public void user_launches_the_browser() {
// Write code here that turns the phrase above into concrete actions
WebDriverManager.chromedriver().setup();
driver=new ChromeDriver();
pom=new Pom(driver);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(6));
driver.manage().window().maximize();
}
this way, you not sending the driver without initializing to your Pom constructor.

Testng how to get all the log steps in aftermethod for post issue on some bug tracking tools

I am working on a project where if test case is failed I have to post a bug on their bug tracking tools through the tools. But they want in a bug report all the steps should mentioned properly with error.
Like in the description they want like this
Open Url
List item
Click the submit button Dashboard title is not match properly
In My automation code, I have written log
public class logtest {
WebDriver driver;
#BeforeMethod
public void Before(){
driver=new ChromeDriver();
}
#Test
public void test1(){
Log.info("Open URl");
//Opened url
Log.info("Click on the submit button");
// Submit button Clicked
Log.info("Open Dashboard");
Log.info("Dashboard Title match");
}
#AfterMethod
public void AfterMethod(ITestResult result){
if (result.getStatus() == ITestResult.FAILURE) {
PostIssue_to_Somewhere();
}
}
}
Is there any want I can get all the steps that I print inside #Test in the after method
so I can post bugs to their board through API
I think I can save the result somewhere and print it the end of the test case but I don't think it's an ideal solution. If you have any suggestion or way that I can manage it using testing that will really helpful
You cannot get the result in after test method. you should use TestNG listener for get the results
Steps to get them :
First create your own class with extend listener class
public class Listener implements ITestListener
Then use the override method to get the results
#Override
public void onTestSuccess(ITestResult iTestResult) {
}
there are several method to get the exact data,
OnStart- OnStart method is called when any Test starts.
onTestSuccess- onTestSuccess method is called on the success of any test
onTestFailure- onTestFailure method is called on the failureof any Test.
onTestSkipped- onTestSkipped method is called on
skipped of any Test
onTestFailedButWithinSuccessPercentage- method
is called each time Test fails but is within success percentage.
onFinish- onFinish method is called after all Tests are executed.
Use Extent Report and then use its method like (log, pass, fail ) to log your steps in your report, based on your test result, if you still need help let me know :)
You can see the full project demo code at ( below is code for the extent report) https://github.com/0kakarot0/ExtentReportBasic.git
public class MyExtentReport {
private WebDriver driver;
ExtentSparkReporter spark;
ExtentReports extentReports;
ExtentTest extentTest;
public MyExtentReport(WebDriver driver) {
this.driver = driver;
}
//Created Extent Report Html File
public void extentReporter() {
Date date = new Date();
String rightNow = "Spark"+ date.getTime() + ".html" ;
String pathOfFile = "allReports/"+rightNow;
spark = new ExtentSparkReporter(pathOfFile);
extentReports = new ExtentReports();
extentReports.attachReporter(spark);
}
public void logTestNameAndDescription(String name, String description){
extentTest = extentReports.createTest(name, description);
}
public void logTestInfo(String testInfo){
extentTest.log(Status.INFO,testInfo);
}
public void logPassedTestSteps(String logPassMessage){
extentTest.pass(logPassMessage);
}
public void logFailTestSteps(String logFailMessage){
extentTest.fail(logFailMessage);
}
public void logFailTestScreenShot(String reasonOfFailure){
extentTest.fail(reasonOfFailure,MediaEntityBuilder.createScreenCaptureFromPath("utils/failedTestScreenShot/screenshot.png").build());
}
public void flushExtentReport(){
extentReports.flush();
}

Autocomplete section in Selenium

I have the list which has multiple links under each section. Each section has different links. I need to click a particular link under each section. I have written the below code but when it executes, I'm not able to click the second section of autocomplete field after clicking the first section autocomplete field.
Here is my code. By using for-each am not able to select the second autocomplete field.
public class Autocomplete {
public static WebDriver driver;
public static void main(String[] args) throws InterruptedException {
ChromeOptions option = new ChromeOptions();
option.addArguments("--disable-notifications");
System.setProperty("webdriver.chrome.driver",".//src//browser//chromedriver.exe");
driver = new ChromeDriver(option);
driver.manage().window().maximize();
System.out.println("Browser Launch chrome");
driver.get("https://www.redbus.in/");
driver.findElement(By.xpath("//*[#id=\"src\"]")).sendKeys("ta");
AutocompleteRedbus.RebBus(By.xpath("//*[#id=\"src\"]"), "Tambaram, Chennai");
Thread.sleep(5000);
driver.findElement(By.xpath("//*[#id=\"dest\"]")).sendKeys("pon");
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
AutocompleteRedbus.RebBus(By.xpath("//*[#id=\"dest\"]"), "Ponamaravathi");
}
}
Above mentioned AutocompleteRedbus calling method code here:
public class AutocompleteRedbus extends Autocomplete{
public static void RebBus(By xpath , String text) throws InterruptedException {
List<WebElement> listOfLinks = driver.findElements(By.xpath("xpath"));
listOfLinks.forEach(link -> {
if (link.getText().equalsIgnoreCase("text")) {
link.click();
}
});
}
}
When you are trying to select the value from the autocompleter, the xpath you are using is incorrect. Please change the xpath with the one i am providing and it would work fine.
AutocompleteRedbus.RebBus(By.xpath("//li[contains(#select-id,'results')]"), "Tambaram, Chennai");
And same for the destination method, like:
AutocompleteRedbus.RebBus(By.xpath("//li[contains(#select-id,'results')]"), "Ponamaravathi");

Selenium PageFactory placement in framework

Currently I have the following classes which use PageFactory to initialize the elements that I use.
Base Class:
public class BaseClass {
public static WebDriver driver;
public static boolean bResult;
public BaseClass(WebDriver driver){
BaseClass.driver = driver;
BaseClass.bResult = true;
}}
Login Page Classs that holds the elements:
public class LoginPage extends BaseClass{
public LoginPage(WebDriver driver)
{
super(driver);
}
#FindBy(how= How.XPATH, using="//input[#placeholder='Username']")
public static WebElement username;
Then I use a separate Login class for my actions:
public class Login {
//Login
public static void enterUsernamePassword(String username, String password) throws Exception{
LoginPage.username.sendKeys(username);
LoginPage.password.sendKeys(password);
}
Then my steps class:
#When("^I enter a valid username (.*) and password (.*)")
public void I_enter_a_valid_username_and_password(String username, String password) throws Throwable
{
PageFactory.initElements(driver, LoginPage.class);
Login.enterUsernamePassword(username, password);
}
As you can see I am using PageFactory within the steps class. I hate doing this and would like to put the PageFactory somewhere else, just not in the steps class.
Without adding or deleting any classes, where could I place the PageFactory class? Any help would be appreciated.
I found that the best to do it is by using an Inversion Of Control / Dependency Injection framework. I use Spring but there are other options as well, e.g. Guice, Weld, Picocontainer.
Using Spring you can annotate all pages with #Component and add a PageObject Bean postprocessor to initialize all page elements when the pages are being created.
This approach is also recommended by the Cucumber for Java Book by Aslak Hellesoy (Cucumber Ltd founder). Cucumber also provides

How to check whether auto save functionality working or not using selenium web driver?

I am working on a project which contains forms having auto save functionality.
I know how to send data for the fields but i dont how to check whether these fields are auto saved or not.
This is what I have done :
public class Ca1GeneralInformation {
private static WebElement element = null;
//Quick facts
public static WebElement ca1_companyName(WebDriver driver){
element = driver.findElement(By.id("ca1_GEN_coName"));
return element;
}
public static WebElement ca1_WebAdress(WebDriver driver){
element = driver.findElement(By.id("ca1_GEN_coURL"));
return element;
}
public static WebElement ca1_YearFounded(WebDriver driver){
element = driver.findElement(By.id("ca1_GEN_foundedDate"));
return element;
}
public static WebElement ca1_nameOfCeo(WebDriver driver){
element = driver.findElement(By.id("ca1_GEN_CEOname"));
return element;
}
public static WebElement ca1_titleOfCeo(WebDriver driver){
element = driver.findElement(By.id("ca1_GEN_CEOtitle"));
return element;
}
public static WebElement ca1_becomeCeo(WebDriver driver){
element = driver.findElement(By.id("ca1_GEN_CEOstart"));
return element;
}
public static WebElement ca1_CeoJoinYear(WebDriver driver){
element = driver.findElement(By.id("ca1_GEN_CEOjoin"));
return element;
}
public static WebElement ca1_CeoFirstPosition(WebDriver driver){
element = driver.findElement(By.id("ca1_GEN_CEOposition"));
return element;
}
}enter code here
//Send data for quick facts page
Ca1GeneralInformation.ca1_companyName(driver).clear();
Ca1GeneralInformation.ca1_companyName(driver).sendKeys("Test Company");
Ca1GeneralInformation.ca1_WebAdress(driver).clear();
Ca1GeneralInformation.ca1_WebAdress(driver).sendKeys("www.test.com");
Ca1GeneralInformation.ca1_YearFounded(driver).clear();
Ca1GeneralInformation.ca1_YearFounded(driver).sendKeys("2000");
Ca1GeneralInformation.ca1_nameOfCeo(driver).clear();
Ca1GeneralInformation.ca1_nameOfCeo(driver).sendKeys("mr.CEO");
Ca1GeneralInformation.ca1_titleOfCeo(driver).clear();
Ca1GeneralInformation.ca1_titleOfCeo(driver).sendKeys("CEO");
Ca1GeneralInformation.ca1_becomeCeo(driver).clear();
Ca1GeneralInformation.ca1_becomeCeo(driver).sendKeys("2002");
Ca1GeneralInformation.ca1_CeoJoinYear(driver).clear();
Ca1GeneralInformation.ca1_CeoJoinYear(driver).sendKeys("2001");
Ca1GeneralInformation.ca1_CeoFirstPosition(driver).clear();
Ca1GeneralInformation.ca1_CeoFirstPosition(driver).sendKeys("manager");
driver.close();
>
`
In a similar case in our application - when you enter some text for the input field and click outside, the data is saved on db and is also displayed on the input field. (We fire one Ajax event-on blur to update our db) So you can try these:
Connect to the DB and check for the update for the appropriate table
Enter your test data on the input field/form and do the event which is in your case (like on blur - click outside on our case) and wait for few secs. You can do gettext() on the same field to validate if the value is stored or not.