I use Page Object Pattern in Java Selenium and I've got problem with updating element after click action.
There is a list of X elements on page and button that enables to scroll them (after clicking on button, new elements are displayed on the list, old ones disappear)
There are two classes:
public abstract class WebPage {
protected WebDriver driver;
WebPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(new AppiumFieldDecorator(driver, Duration.ofSeconds(30)), this);
}
and
public class ChildClass extends WebPage{
#FindBy(selector)
List<WebElement> elements;
By nextButton = By.id(xxx);
ChildClass(WebDriver driver) {
super(driver);
}
public void iterateAllElements(){
for (WebElement element: elements){
//some action
}
if(buttonIsEnabled())
clickOnButton();
iterateAllElements();
}
}
Please tell me why object "elements" is not initialized with new values after invoking clickOnButton(); function? I cannot get elements that are displayed on 2nd, 3rd page ect
There are still elements from 1st view on the list. It works fine in debug mode, but problem occurs when I want to run it.
Related
I am trying to find the element of the field First Name on the page https://whitelabel.sandbox.array.io/signup?platform=v3. I tried searching by id, classname, name, cssSelector, etc. but none works. I even added waiter to ensure it is loaded well before I try to find the element. Same issue happens for all fields in the page. So, the issue is not unique to this field.
Tried this in Chrome and Firefox on Mac. The same code works well to find the username field in gmail.com page.
driver.get("https://whitelabel.sandbox.array.io/signup?platform=v3");
driver.manage().window().maximize();
WebDriverWait wait = new WebDriverWait(driver, java.time.Duration.ofSeconds(10));
WebElement selectFirstName = driver.findElement(By.name("firstName"));
//assertNotNull(driver.findElement(By.name("firstName")));
//assertNotNull(driver.findElement(By.xpath("//input[#name='firstName']")));
//driver.findElement(By.cssSelector("input[name='firstName']")).sendKeys("Thomas");
//driver.findElement(By.name("firstName")).sendKeys("Thomas");
//driver.findElement(By.xpath("//input[#name='firstName']")).sendKeys("Thomas");
//page.locator("[name='firstName']").type("Thomas");
//driver.findElement(By.cssSelector("input[name='firstName']")).sendKeys("Thomas");
//driver.locator("[name='firstName']").type("Thomas");
Error that I get is:
Exception in thread "main" org.openqa.selenium.NoSuchElementException:
no such element: Unable to locate element: {"method":"css
selector","selector":"*[name='firstName']"} (Session info:
chrome=103.0.5060.134)
Does anyone know what I need to do differently to be able to get the webElement?
The First Name field within the website https://whitelabel.sandbox.array.io/signup?platform=v3 is within a #shadow-root (open)
ShadowRoot in selenium4
As per the test implementation in ShadowRoot.java:
import static org.openqa.selenium.remote.Dialect.W3C;
import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENTS_FROM_SHADOW_ROOT;
import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENT_FROM_SHADOW_ROOT;
// Note: we want people to code against the SearchContext API, so we keep this class package private
class ShadowRoot implements SearchContext, WrapsDriver {
private final RemoteWebDriver parent;
private final String id;
ShadowRoot(RemoteWebDriver parent, String id) {
this.parent = Require.nonNull("Owning remote webdriver", parent);
this.id = Require.nonNull("Shadow root ID", id);
}
#Override
public List<WebElement> findElements(By by) {
return parent.findElements(
this,
(using, value) -> FIND_ELEMENTS_FROM_SHADOW_ROOT(id, using, String.valueOf(value)),
by);
}
#Override
public WebElement findElement(By by) {
return parent.findElement(
this,
(using, value) -> FIND_ELEMENT_FROM_SHADOW_ROOT(id, using, String.valueOf(value)),
by);
}
#Override
public WebDriver getWrappedDriver() {
return parent;
}
public String getId() {
return this.id;
}
private Map<String, Object> toJson() {
return singletonMap(W3C.getShadowRootElementKey(), id);
}
}
#titusfortner explains the same in their comment as:
The actual state is that the return value of that JavaScript changed in v96 of ChromeDriver in order to be w3c compliant. Selenium 3.141.59 can not parse this new return value. You can use getShadowRoot() in Selenium 4, or you'll be able to get a ShadowRoot instance returned from the JS in Selenium 4.1.
And I stand corrected, you need to cast to SearchContext interface.
Solution
To send a character sequence within the First Name you can use the following Locator Strategy:
Code Block:
driver.get("https://whitelabel.sandbox.array.io/signup?platform=v3");
WebElement element = new WebDriverWait(driver, Duration.ofSeconds(10), Duration.ofSeconds(10)).until(ExpectedConditions.visibilityOfElementLocated(By.tagName("array-account-enroll")));
SearchContext context = element.getShadowRoot();
WebElement firstName = context.findElement(By.cssSelector("input[name='firstName'][placeholder='Enter first name']"));
firstName.sendKeys("Supramanian");
Browser snapshot:
I am using Page Factory set up for my project where in I have the following:
#FindBy(how=How.XPATH,using="//*[contains(#text,'Monthly Pass')]")
private WebElement monthlyPass;
#FindBy(how=How.XPATH,using="//*[contains(#text,'Ten Ride Ticket')]")
private WebElement tenRideTicket;
#FindBy(how=How.XPATH,using=""./parent::android.view.View/following-sibling::android.view.View[1]/android.view.View[2]")
private WebElement AmountForThePass;
public void getAmountValue(){
if(monthlyPass.isDisplayed){
// Here I want to findElement 'AmountForThePass' from the element 'monthlyPass'
// The actual element is //*[contains(#text,'Monthly Pass')]/parent::android.view.View/following-
// sibling::android.view.View[1]/android.view.View[2]
// which I have broken into 2 parts.The second part 'AmountForThePass' is same for other
// 'tenRideTicket'
}
}
How can I combine the WebElements (monthlyPass with AmountForThePass) OR (tenRideTicket with AmountForThePass)
I have a button on my Webpage which I want to click once the required piece of information is entered. I am currently using By to establish all the elements of the page but want to use WebElements for this button and then use Actions to click it later.
How should I do that in my Page Object class.
I tried with below approach :
WebElement addressinput = driver.findElement(By.xpath("//input[#id='pac-input']"));
By addressinput = By.xpath("//input[#id='pac-input']");//this works fine
But on running the Test class as TestNG it shows null pointer exception on WebElement line. Tried to do it with By as well but the button just won't recieve the click. It works pefectly fine with WebElements and action which I have tried before without using POM below is the reference code for that :
WebElement button = driver.findElement(By.xpath("//button[#id='btn_gtservice']"));
Actions action = new Actions(driver);
action.moveToElement((WebElement) CheckAvailability).click().perform();
driver.switchTo().defaultContent();
You've got
action.moveToElement((WebElement)CheckAvailability)
That should be
action.moveToElement((button)CheckAvailability)
As it is, you'll be getting a null pointer as you have no variable named WebElement defined
When using PageFactory in PageObjectModel if you expect the element to be loaded after some information is entered, through some JavaScript and it might not be immediately present on the page already you can use the Actions once the element is returned through WebDriverWait support with a normal locator factory as follows:
Code Block:
package com.pol.zoho.PageObjects;
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;
import org.openqa.selenium.interactions.Actions;
public class ZohoLoginPage {
WebDriver driver;
public ZohoLoginPage(WebDriver driver)
{
PageFactory.initElements(driver, this);
}
#FindBy(xpath="//button[#id='btn_gtservice']")
public WebElement myButton;
public void doLogin(String username,String userpassword)
{
WebElement button = new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(ZohoLoginPage.getWebElement()));
new Actions(driver).moveToElement(button).click().perform();
}
public WebElement getWebElement()
{
return myButton;
}
}
You can find a detailed discussion in How to use explicit waits with PageFactory fields and the PageObject pattern
I am trying to write code to validate a webpage (Test Form with 3 required fields firstname, lastname, phone and 2 buttons submit and clear form) using POM with Selenium WebDriver with Java.
This is the code which I have written so far. I want to confirm whether I am going in the right way.
public class TestForm {
WebDriver driver;
By firstName=By.id("fname");
By lastName=By.id("lname");
By phoneno=By.id("phone");
By submit=By.id("submit");
By clearForm=By.xpath("//tagname[#type='button']");
public TestForm(WebDriver driver)
{
this.driver=driver;
}
public void typeFirstName(String fname)
{
driver.findElement(firstName).sendKeys(fname);
}
public void typeLastName(String lname)
{
driver.findElement(lastName).sendKeys(lname);
}
public void typePhone(String phone)
{
driver.findElement(phoneno).sendKeys(phone);
}
public void clickSubmit()
{
driver.findElement(submit).click();
}
public void clickClearForm()
{
driver.findElement(clearForm).click();
}
}
public class VerifyTestForm {
#Test
public void verifyValidTestForm()
{
WebDriver driver=new FirefoxDriver();
driver.manage().window().maximize();
driver.get("url of the application");
TestForm form=new TestForm(driver);
form.typeFirstName("John");
form.typeLastName("Adams");
form.typePhone("1234567890");
form.clickSubmit();
form.clickClearForm();
driver.quit();
}
}
Most code look good, except following items:
1) By clearForm=By.xpath("//tagname[#type='button']");
tagname should be a correct tag, like button or 'input'
2) After click Submit, the page still stay the form page, If so call clickClearForm should work.
form.clickSubmit();
form.clickClearForm();
3) There is no any check point/validation in your code, all are operateion on page.
// Assume an new page will open after click Submit button
// You need to check the new page opened by check page title if it'll change
// or check an element belongs to the new page is displayed
Assert(driver.getTitle()).toEqual('xxx')
Assert(driver.findElement(xxx).isDisplay()).toBe(true)
// above code may not exact correct, dependent on you choose Junit, TestNG
// or third-party Assert library.
// After click `Clear/Reset` button, you should check all fields reset to default value
Assert(form.readFirstName()).toEqual("")
4) For test class name VerifyTestForm, it's better start or end with Test, like Testxxx or xxxTest
As your code is correct but it is not the way to implementing Page object Model.
You have to use concept of DataProvider to implement framework.
Make a excel sheet and extract the data by using DataProvider.
Make a new class file from where you can read your excel data.
Make a function which return 2d data of the file.
So By using this, The way to implement the framework.
Page object Model generally says that we should have to make the separate page of each module which we are using and return the reference of the last page.
Screenshot of the element I want to click:
I automating my website(new to automation). once i login i get to another page where selenium web driver is not able to find any of the elements(I tried all possibilities even sso related).
Only solution i could find was using tabs and enter.
So when i enter that page i need to click 9 time "TAB" key from the keyboard and then enter so that my login is verified. since i don't have any element using which i can perform the tab and enter actions. is there a way where once i get to that page the web driver starts pressing "TAB" key 9 times and then "Enter" on 10 time.
Please help I have been working on this over a week now and not getting
anywhere.
optimist_creeper-main class:
package Modules;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.Test;
import Modules.HomePage;
public class MainClass {
String appUrl = "als-stg-1.mtvn.ad.viacom.com/webqa/";
#Test public void MainTest() {
System.setProperty("webdriver.gecko.driver", "C:\\Shayni Coding\\Automation\\Gecko\\geckodriver.exe");
WebDriver driver = new FirefoxDriver();
driver.get(appUrl);
HomePage home = new HomePage();
home.HomePageTest(driver);
}
}
Home Page class:
public class HomePage {
#BeforeClass public void beforeClass() {
System.out.println("before class");
}
public void HomePageTest(WebDriver driver) {
driver.manage().window().maximize();
WebElement email = driver.findElement(By.id("cred_userid_inputtext"));
email.sendKeys("shayni#outlook.com");
WebElement pass = driiver.findElement(By.id("cred_password_inputtext"));
pass.sendKeys(Keys.ENTER);
pass.click();
String expectedTitle = "VMS Web";
String actualTitle = driver.getTitle();
Assert.assertEquals(expectedTitle,actualTitle);
}
}
Thanks.
Just get a random object like the body tag and use that to send your key presses.
e.g.
WebElement dummyElement = driver.findElement(By.xpath("/html/body"));
for (int i = 0; i < 9; ++i) {
dummyElement.sendKeys(keys.TAB);
}
dummyElement.sendKeys(keys.ENTER);
The above code finds the body take and sets it as an element. It then presses the tab key 9 times and then presses the enter key. Which is what you asked for. Hope that helps.