I'm writing automated scripts using page factory and I want to use visibilityOfElementLocated with page factory instead of visibilityOf
I tried to use visibilityOf but some times it not work with my element
the problem here that visibilityOfElementLocated take By parameter, and I have WebElment
#FindBy(id = "test")
WebElement locator;
You can't use it directly with #FindBy, but you can call it from a method that will run before PageFactory.initElements
public abstract class BasePage {
protected WebDriverWait wait;
public BasePage(WebDriver driver) {
wait = new WebDriverWait(driver, 10);
assertInPage();
PageFactory.initElements(driver, this);
}
public abstract void assertInPage();
}
public class DerivedPage extends BasePage {
#FindBy(id = "test")
WebElement locator;
public DerivedPage(WebDriver driver) {
super(driver);
}
#Override
public void assertInPage() {
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("test")));
}
}
assertInPage() in DerivedPage will be executed right before PageFactory.initElements.
visibilityOfElementLocated(By locator)
visibilityOfElementLocated(By locator) takes a __By locator_ as an argument and is the expectation for checking that an element is present on the DOM of a page and visible.
visibilityOf(WebElement element)
visibilityOf(WebElement element) takes an WebElement as an argument and is the expectation for checking that an element, known to be present on the DOM of a page, is visible.
When using PageFactory in PageObjectModel if you expect the element to be dynamic and loaded through some JavaScript and might not be present on the page already you can use the Explicit Wait support with a normal locator factory as follows:
Code Block:
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 TestPage {
WebDriver driver;
//constructor
public TestPage(WebDriver driver)
{
PageFactory.initElements(driver, this);
}
//Locator Strategy
#FindBy(id = "test")
WebElement locator;
//Test Method
public void testMethod()
{
WebElement element = new WebDriverWait(driver, 20).until(ExpectedConditions.visibilityOf(TestPage.getWebElement()));
//perform your desired action here e.g element.click()
}
public WebElement getWebElement()
{
return locator;
}
}
You can find a detailed discussion in How to add explicit wait in PageFactory in PageObjectModel?
Related
everyone, I'm struggling with the following situation.
Returned driver is not recognized.
I want to make a method to used it for rest of the methods for a test suite but
package acceptanceTesting
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.BeforeSuite;
public class LoginTestingStep {
#BeforeSuite
public static WebDriver driver() {
System.setProperty(WebDriverPage.webDriverAdress, WebDriverPage.webDriverPath);
WebDriver driver = new ChromeDriver();
driver.get("https://evernote.com/");
return driver;
}
public static void WaitForElementVisible(String option) {
WebDriverWait wait = new WebDriverWait(driver(), 5);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(option)));
}
#Test
public static void UnauthotisedLogin() {
driver.manage().window().maximize();
driver.findElement(By.cssSelector(LoginTestingPage.loginButton)).click();
driver.findElement(By.cssSelector(LoginTestingPage.emailAdreesField)).sendKeys("spacesiatat#yahoo.com");
WaitForElementVisible(LoginTestingPage.continueButton);
driver.findElement(By.cssSelector(LoginTestingPage.continueButton)).click();
// TimeUnit.SECONDS.sleep(5);
// driver.close();
}
public static void AuthotisedLogin() {
}
}
Simple advice first: Do not make return anything when using TestNG annotations (like #BeforeSuite and etc.) Because everytime you want to call this method as parameter it will open new Chrome Browser.
Since you are creating driver in the inside of driver() method
No any other methods can see that there is driver already defined. Insted use it like global variable, not inside method.
public class LoginTestingStep {
WebDriver driver; //declare as global
#BeforeSuite
public static void driver() {
System.setProperty(WebDriverPage.webDriverAdress,
WebDriverPage.webDriverPath);
driver = new ChromeDriver(); //then create instance
wait = new WebDriverWait(driver, Duration.ofSeconds(5));
driver.get("https://evernote.com/");
}
public static void WaitForElementVisible(String option) {
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(option)));
}
#Test
public static void UnauthotisedLogin() {
driver.manage().window().maximize();
driver.findElement(By.cssSelector(LoginTestingPage.loginButton)).click();
driver.findElement(By.cssSelector(LoginTestingPage.emailAdreesField)).sendKeys("spacesiatat#yahoo.com");
WaitForElementVisible(LoginTestingPage.continueButton);
driver.findElement(By.cssSelector(LoginTestingPage.continueButton)).click();
}
}
**What I changed in your code? **
Made driver() method to be return type void instead of WebDriver return type
Deleted line that include: return driver;
Made WebDriverWait global to enable using it in other methods as well and avoiding repeating creating instance of it every time
Added 'DurationOfSeconds' to 'WebDriverWait' constructor parameter since raw usage of second is deprecated
I'm trying to use only PageFactory in my project, without using fields with type By. And I'm looking to implement something like this:
#FindBy(className = "loading-container")
private WebElement loadingElement;
public LoadingPage(WebDriver driver) {
PageFactory.initElements(driver, this);
this.waitDriver = new WebDriverWait(this.driver, 20);
}
public void waitLoadingToFinish() {
this.waitDriver.until(ExpectedConditions.elementNotExists(loadingElement));
}
Is there a way to implemet custom Expected Condition for this? or any other way to implement this? (without using By fields, only using page factory).
As far as I understand you have some elements on the page that you consider ready for usage only when there is no certain element on the page (like waiting wheel).
There is a special locator class in Selenium called AjaxElementLocator. What you need to do is to extend that type by changing isElementUsable method when you initialize your page so that all the controls you use on that page would first check the condition. Here is the example:
package click.webelement.pagefactory.conditions;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.AjaxElementLocator;
public class LoadablePage {
#FindBy(id = "cntrl-id")
WebElement control;
public LoadablePage(WebDriver driver){
PageFactory.initElements(field -> new AjaxElementLocator(driver, field, 10){
#Override
protected boolean isElementUsable(WebElement element) {
return driver.findElements(By.xpath("//WHEEL_XPATH")).size() == 0;
}
}, this);
}
}
Here you can find more information on such approach.
Selenium has this method
ExpectedConditions.InvisibilityOfElementLocated
An expectation for checking that an element is either invisible or not
present on the DOM.
For your code
public void waitLoadingToFinish() {
this.waitDriver.until(ExpectedConditions.invisibilityOfElementLocated(loadingElement));
}
also, you might try to add a javascript executor to wait until the page is loaded
public static void waitForScriptsToLoad(WebDriver driver) {
WebDriverWait(driver, 30).until((ExpectedCondition<Boolean>) wd ->
((JavascriptExecutor) wd).executeScript("return document.readyState").equals("complete"));
}
and then your page constructor becomes this
public LoadingPage(WebDriver driver) {
PageFactory.initElements(driver, this);
this.waitDriver = new WebDriverWait(this.driver, 20);
waitForScriptsToLoad(driver);
}
If you create wait method in your program that is easy and customize in selenium framework
private static WebElement waitForElement(By locator, int timeout)
{
WebElement element=new WebDriverWait(driver,timeout).until(ExpectedConditions.presenceOfElementLocated(locator));
return element;
}
//If you want wait for id following code should use
waitForElement(By.id(""),20);
Here 20 is miliseconds
and you can use any web elements to wait
To simulate an ExpectedConditions like elementNotExists you can use either among invisibilityOfElementLocated() or invisibilityOf().
invisibilityOfElementLocated()
invisibilityOfElementLocated() is the implementation for an expectation for checking that an element is either invisible or not present on the DOM. It is defined as follows:
public static ExpectedCondition<java.lang.Boolean> invisibilityOfElementLocated(By locator)
An expectation for checking that an element is either invisible or not present on the DOM.
Parameters:
locator - used to find the element
Returns:
true if the element is not displayed or the element doesn't exist or stale element
Code Block:
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.WebDriverWait;
public class fooPage {
WebDriver driver;
public fooPage(WebDriver driver)
{
PageFactory.initElements(driver, this);
}
//you don't need this
//#FindBy(className = "loading-container")
//private WebElement loadingElement;
public void foo()
{
Boolean bool = new WebDriverWait(driver, 20).until(ExpectedConditions.invisibilityOfElementLocated(By.className('loading-container')));
//other lines of code
}
}
As an alternative you can also use the invisibilityOf() method as follows:
invisibilityOf()
invisibilityOf() is the implementation for an expectation for checking the element to be invisible. It is defined as follows:
public static ExpectedCondition<java.lang.Boolean> invisibilityOf(WebElement element)
An expectation for checking the element to be invisible
Parameters:
element - used to check its invisibility
Returns:
Boolean true when elements is not visible anymore
Code Block:
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.WebDriverWait;
public class fooPage {
WebDriver driver;
public fooPage(WebDriver driver)
{
PageFactory.initElements(driver, this);
}
#FindBy(className= 'loading-container')
WebElement loadingElement;
public void foo()
{
Boolean bool = new WebDriverWait(driver, 20).until(ExpectedConditions.invisibilityOf(fooPage.getWebElement()));
//other lines of code
}
public WebElement getWebElement()
{
return loadingElement;
}
}
You can find a detailed discussion in How to use explicit waits with PageFactory fields and the PageObject pattern
I want to create a class where I set all the common actions of the WebDrivers such as: waitExplicit, findElement, click. But if I create a method then I have to create the WebDriver and WebDriverWait over and over on each method of the class, I already tried having a class for the Drivers, but when I call the methods, they just create instances over and over, so multiple windows open, I tried this way, but still cannot get to it:
public class AutomationActions{
static LoadProperties prop = new LoadProperties(); //This class has the System.setProperty for the driver
prop.getSysProp(); //***This is the issue, how can I solve this?****
WebDriver driver = new ChromeDriver(); //this will not work without the one above working
WebDriverWait wait = new WebDriverWait(driver, 30);//this will not work without the one above working
public void waitForPageToLoad() throws Exception {
ExpectedCondition<Boolean> pageLoadCondition = new
ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
}
};
// WebDriverWait wait = new WebDriverWait(driver, 30); // I want to avoid having to set this in every method
wait.until(pageLoadCondition); //this is supposed to replace the line of code above
}
I don't really work on Java much any more, I've written our framework in C# but I put together some quick classes in Java to show you how I set things up. I use page object model and I recommend you do too so I've written this example using page object model. I've written a simple test that uses Dave Haeffner's (one of the Selenium contributors) demo site, http://the-internet.herokuapp.com.
The basic concepts are:
There is a class BaseTest that holds things that correspond to tests, e.g. setting up the driver at the start of the test, quitting the driver at the end of the test, etc. All of your tests will inherit from BaseTest
There is a class BasePage that holds things that correspond to generic methods for finding elements, clicking on elements, etc. Each of your tests inherit from BasePage. (This is what I think the main part of your question is asking about).
To follow the page object model, each page (or part of a page) is its own class and holds all locators and actions done on that page. For example, a simple login page would have the locators for username, password, and the login button. It would also hold a method Login() that takes a String username and a String password, enters those in the appropriate fields and clicks the Login button.
The final class of this example is a sample test aptly named SampleTest.
You shouldn't have any FindElements() or related calls in your tests, all those should be in the appropriate page object.
This is using TestNG as the unit test library. Use it or JUnit, your preference but if you use JUnit, you will need to change the asserts and the annotations.
Under 'src', I create a folder for page objects, 'PageObjects', and a folder for tests, 'Tests'. Here's what the files look like on disk.
\src
\PageObjects
BasePage.java
DropdownListPage.java
\Tests
BaseTest.java
SampleTest.java
BasePage.java
package PageObjects;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class BasePage
{
private WebDriver driver;
private final int shortWait = 10;
public BasePage(WebDriver _driver)
{
driver = _driver;
}
public void ClickElement(By locator)
{
new WebDriverWait(driver, shortWait).until(ExpectedConditions.elementToBeClickable(locator)).click();
}
public WebElement FindElement(By locator)
{
return new WebDriverWait(driver, shortWait).until(ExpectedConditions.presenceOfElementLocated(locator));
}
// add more methods
}
DropdownListPage.java
package PageObjects;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.Select;
public class DropdownListPage extends BasePage
{
private final By dropdownListLocator = By.id("dropdown");
public DropdownListPage(WebDriver _driver)
{
super(_driver);
}
public String GetSelectedOption()
{
return new Select(FindElement(dropdownListLocator)).getFirstSelectedOption().getText();
}
public void SelectOptionByIndex(int index)
{
new Select(FindElement(dropdownListLocator)).selectByIndex(index);
}
}
BaseTest.java
package Tests;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
public class BaseTest
{
public WebDriver driver;
public WebDriver GetChromeDriver()
{
System.setProperty("webdriver.chrome.driver", "C:\\Path\\To\\Chrome\\Driver\\chromedriver.exe");
return new ChromeDriver();
}
#BeforeTest
public void Setup()
{
driver = GetChromeDriver();
driver.manage().window().maximize();
driver.get("http://the-internet.herokuapp.com/dropdown");
}
#AfterTest
public void Teardown()
{
driver.close();
}
}
SampleTest.java
package Tests;
import org.testng.Assert;
import org.testng.annotations.Test;
import PageObjects.DropdownListPage;
public class SampleTest extends BaseTest
{
#Test
public void SampleTestCase()
{
DropdownListPage dropdownListPage = new DropdownListPage(driver);
dropdownListPage.SelectOptionByIndex(1);
Assert.assertEquals(dropdownListPage.GetSelectedOption(), "Option 1", "Verify first option was selected");
}
}
You will need to create a project that contains Selenium for Java and TestNG. Download them and put them on your build path. Create the folder structure as described above and create each of these classes and copy/paste the contents into them. Now all you need to do is run SampleTest as a TestNG Test and it should go.
The test creates a new Chromedriver instance, navigates to the sample page, selects the first option in the dropdown, asserts that the dropdown text is correct, and then quits the driver.
That should get you started. There's a lot of info crammed into the above wall of text, let me know if you have some questions.
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?
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));
If you need to use ExpectedConditions with some your "Page" class that contains asynchronously loaded elements I would recommend to use the following approach:
Extend AjaxElementLocator where override protected boolean isElementUsable(WebElement element) so that it uses ExpectedConditions to decide if the element is ready for use
Extend AjaxElementLocatorFactory where override public ElementLocator createLocator(Field field) method so that it now returns the instance of your extended class
Where you initialize elements use PageFactory.initElements(new MyAjaxElementLocatorFactory(driver, 10), this); where MyAjaxElementLocatorFactory is the class that you created on step 2
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:
How to add explicit wait in PageFactory in PageObjectModel?
How to wait for invisibility of an element through PageFactory using Selenium and Java
I'm performing Selenium automation in Java using POM. I need to highlight elements on the following web page. But it doesn't make any effect at all, though I don't get any error message, it simply doesn't highlight the element I've selected.
As I'm using POM pattern I have a separate class including all element functionality methods, such as click, write text, etc.
package pageObjects;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
public class BasePage {
public WebDriver driver;
public WebDriverWait wait;
// Constructor
public BasePage (WebDriver driver) {
this.driver = driver;
wait = new WebDriverWait(driver, 15);
}
// Click Method
public void click (By elementBy) {
waitVisibility(elementBy);
driver.findElement(elementBy).click();
}
I have highlighting element method as following.
// Element highlighter method
public static void highLightElement(WebDriver driver, By elementBy) {
JavascriptExecutor js = (JavascriptExecutor)driver;
js.executeScript("arguments[0].setAttribute('style', 'background: yellow; border: 2px solid red;');", elementBy);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
js.executeScript("arguments[0].setAttribute('style','border: solid 2px white');", elementBy);
}
I have changed my click method as following.
public void click_2 (By elementBy) {
waitVisibility(elementBy);
highLightElement(driver,elementBy);
driver.findElement(elementBy).click();
}
And use it in a separate class with all page methods.
package pageObjects;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.Select;
public class HomePage extends BasePage {
// Constructor
public HomePage (WebDriver driver) {
super(driver);
}
// Page Variables
String baseURL = "https://www.bookdepository.com/";
// Web Elements
// --- Sign In UI ---
By signInJoin_button = By.xpath("//div[#class='page-slide']//ul[#class='right-nav mobile-nav-content']//li[3]//a[1]");
By signOut_button = By.xpath("/html/body/div[4]/div[1]/div/ul[2]/li[5]/a");
// Page Methods ---
public HomePage goToBookDepositoryHomePage (){
driver.get(baseURL);
pause();
return this;
}
public LoginPage goToLoginPage (){
click_2(signInJoin_button);
pause();
return new LoginPage(driver);
}
What am I doing wrong here?
You should use WebElement and not By to change the style. Try following code:
public static void highLightElement(WebDriver driver, By elementBy) {
WebElement webElement = driver.findElement(elementBy);
String originalStyle = webElement.getAttribute("style");
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("arguments[0].setAttribute(arguments[1], arguments[2]", webElement, "style", originalStyle + "border: 2px solid red;");
//Do something e.g. make a screenshot
//Reset style
js.executeScript("arguments[0].setAttribute(arguments[1], arguments[2])", webElement, "style", originalStyle);
}
I think it makes sense to keep the original style and just add the border. After doing something with the highlighted element e.g. creating a screenshot, you should reset the style. When you return the original style you could remove the reset part in a separate method.
Try the following code. This will highlight all the web elements of a test page.
I am using the EventFiringWebDriver to highlight the webelements in the below code.
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.events.AbstractWebDriverEventListener;
import org.openqa.selenium.support.events.EventFiringWebDriver;
public class HighLighterEventListener extends AbstractWebDriverEventListener {
public static void main(String[] args) {
System.setProperty("webdriver.chrome.driver", "./libs/chromedriver 4");
WebDriver webdriver=new ChromeDriver();
EventFiringWebDriver driver = new EventFiringWebDriver(webdriver);
webdriver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("https://yosuva.com");
driver.register(new HighLighterEventListener());
driver.findElement(By.xpath("//span[text()='Home']"));
driver.findElement(By.xpath("//span[text()='About']"));
driver.findElement(By.xpath("//span[text()='Tools']"));
driver.findElement(By.xpath("//span[text()='News']"));
driver.findElement(By.xpath("//span[text()='Events']"));
driver.findElement(By.xpath("//span[text()='Contact']"));
driver.quit();
}
#Override
public void afterFindBy(By by, WebElement element, WebDriver driver) {
((JavascriptExecutor)driver).executeScript(
"arguments[0].style.border='3px solid green'",element
);
}
}