I would like to implement my Own By Class in order to have custom selectors. What are the pros and cons of using Custom Selectors
DEV code
<button class="btn js-AddNuts" type="button" testid="addbutton">
Here Selector is testid
Reason: We are planning to have test specific selectors and design Names for all the elements in DEV Code so that if they change anything in DEV it doesn't impact test.
There is no need to create a custom locator in this case (or any case that I have run across or can think of). You can simply use the following code that uses a CSS Selector.
By.cssSelector("button[testid='addbutton']")
I would suggest that you spend some time reading and learning CSS Selectors. They are very powerful.
CSS Selector Reference
CSS Selector Tips
As you know there is no problem with creating your own locator, Selenium provides the functionality to create custom locator. Basically selenium internally uses to locate element using xPath when you are going to find element By.id, By.name etc. So you could create simply your own locator by extending By class.
If you want to create a custom locator which locate element using testid, you should create as below (Assuming you are using java) :-
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;
class ByTestId extends By {
private final String testId;
public ByTestId(String testId) {
this.testId = testId;
}
#Override
public List<WebElement> findElements(SearchContext context) {
return context.findElements(By
.xpath(".//*[#testid = '"+testId+"']"));
}
#Override
public WebElement findElement(SearchContext context) {
return context.findElement(By
.xpath("//*[#testid = '"+testId+"']"));
}
}
Now you can easily locate element by using testid as below :-
//for single webelement
WebElement element = driver.findElement(new ByTestId("addbutton"));
//for list of webelements
List<WebElement> elements = driver.findElements(new ByTestId("addbutton"));
Note : You can also locate the element in your custom By class using By.cssSelector as : By.cssSelector("*[testid = '"+testId+"']")
Hope it helps..:)
Related
I have followed a tutorial on how to close random opened popups before doing any action :
https://www.vinsguru.com/selenium-webdriver-how-to-handle-annoying-random-popup-alerts/
The idea is to ccreate an ElementProxy class which implements the interface InvocationHandler. so, the proxy’s invoke method would be called first before the actual method is invoked.
So we call checkForPopupAndKill before invoking any action on a WebElement.
Then we wrap our regular WebElement with this proxy object. We basically need a class which has a method to accept a WebElement and returns the WebElement with some wrapper.
In the tutorial they init elements of the page object with wrapper elements, like this:
//first init elements
PageFactory.initElements(driver, pageobject);
//then access all the WebElements and create a wrapper
for(Field f:pageobject.getClass().getDeclaredFields()){
if(f.getType().equals(WebElement.class)){
boolean accessible = f.isAccessible();
f.setAccessible(true);
//reset the webelement with proxy object
f.set(pageobject, ElementGuard.guard((WebElement) f.get(pageobject)));
f.setAccessible(accessible);
}
}
That's because they have declared fileds with the #FindBy annotation in the page object, but in my framework I'm declaring elements as following :
WebElement elt = getSmartElement(By.cssSelector("#my_id"));
My question is, how can I init my element with with the wrapper element ?
Thanks in advance.
You can directly use the ElementGuard as shown here which wraps the actual WebElement with proxy.
WebElement elt = ElementGuard.guard(getSmartElement(By.cssSelector("#my_id")));
Suppose I have stored WebElment in LoginPage.java class as shown below.
#FindBy(xpath="//input[#name='user_name']") WebElement username;
Now from my test file say VerifyLogin.java I want to access xpath of username which is stored in above LoginPage.java file something like this
LoginPage loginPage = PageFactory.initElements(driver, LoginPage.class);
loginPage.username;//then it should return the xpath //input[#name='user_name']
I know the above approach is wrong, but is there any way to get only the xpath that was stored in LoginPage file that I can use in VerifyLogin file?
If you need it outside of the page object then your design is not that good .
Anyhow if you really want it the following should do the job
#FindBy(xpath="//input[#name='user_name']")
public WebElement username;
new YourPageObject().username;
Not sure why you need to see the xpath after it's done its job but you can do something like this:
public final String xUserName = "//input[#name='user_name']";
#FindBy(xpath = xUserName)
public WebElement username;
The final attribute is required.
This way you store the path as a string, and use the string in your POM, but it is still readable as a string. I start the String variable with x so I know that the String is in xpath format. Start with c for CSS Selector notation.
I've done similar for a couple reasons:
The actual xpath is gigantic so I break it into smaller, reusable String chunks. But for that I mark the String as private.
I need the string to pass to other Selenium methods such as a pause class I created.
But to answer the direct question, no, you can't get the xpath from a POM variable because that information is not stored. The variable is a WebElement and doesn't care by what method you assigned it. It's like asking how a String was created.
you can use this small trick , I used it and it worked for me.
toString method lets you print webelement which has its xpath in it
then using substring method get ur xpath
// method to fetch xpath from PageFacotry webElement
public String getXpathFromWebelement(WebElement e) {
String xpath = e.toString();
xpath = xpath.substring(xpath.indexOf("/"), xpath.lastIndexOf("]"));
return xpath;
}
public static < E > void clickOnElement( E element ) {
}
I am trying to make one generic function which can take element type as WebElement when using selenium and MobileElement when using appium ,so that i can peform click event. Anyone having idea how to implement?
Since MobileElementimplements the WebElement interface, you can simply use WebElement object itself for your click() method.
Considering, you still want to make a generic method anyways, you can achieve it without using Generics, simply use Objectclass:
public static void clickOnElement(Object element ) {
String className = element.getClass().getName();
if(className.contains('WebElement'))
{
//do your WebElement stuff
}
else if(className.contains('MobileElement'))
{
//do your MobileElement stuff
}
}
#FindBy(className = "shellTileBase")
private WebElement tile;
#FindBy(className = "FilterDefault FilterIcon UiIcon IconMirrorInRTL")
private WebElement form;
I am working with selenium and testng but am trying to add arquilliian to my testing. can arquillian handle
#FindBy(className ="")
With multiple class names as per my above example. When I run this I am getting a:
InvalidSelectorError: Compound class names not permitted
Is there a way around this?
Compound class names (class names with a spaces) cannot be used as selector in search by className. You can solve it using XPath as below:
#FindBy(xpath="//*[#class='FilterDefault FilterIcon UiIcon IconMirrorInRTL']")
or CSS:
#FindBy(css=".FilterDefault.FilterIcon.UiIcon.IconMirrorInRTL")
I am building a test automation project using Perl and Selenium. I am using the Page Object Model. I am somewhat unsure about where the Selenium driver would fit into the implementation of the page object model.
Should each page object 'have' a driver ? The way I am thinking is that each page object represents a set of services that the page offers to a user. With this concept in mind , a page object does not have a 'has-a' relationship with a driver. A page object interacts with a driver. However, I am still looking for suggestions. Should I have the driver as part of each page object in the web-application?
Thanks!
This answer won't be any much different from #zodvik, and your thought process, but is another optional approach. Instead of passing the driver around, you can create an abstract class that each page object can then inherit from. From the abstract class, can also contain some common functional methods that you will find yourself often using.
This is at least how I do it in Java language.
I always include driver as part of every Page Object. The way I thought about driver was that it represents the state of the current page. It gives access to the URL, Page Source, etc.
Now, each page has a a current URL, a page source code, a page title which are all accessible through the driver.
In the way, I implemented the framework. I used the driver in the commonFactory.class that contains commonly used elements. Each page was implemented as a child class of the commonFactory.class. so you don't have to implement the driver in each and every class. Since the driver is independent of the test scenarios it's better to have it in a separate way.
As I understand, there are No set rules/standards for implementing POM.
However, the general thumb rule is to create a BaseTest and BasePage class in your Framework where each respective webpage (like Login) will represent by its PageClass(LoginPage).
Similarly, all your Page classes will extend your BasePage and all tests will extend your BaseTest class.
Below is a rough idea of its implementation ->
public class BaseTest{
#BeforeSuite()
setupMethod(){
initialize your WebDriver here
}
}
------------------------------------------
public class BasePage {
//create constructor
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(this.driver, Duration.ofSeconds(TIMEOUT));
PageFactory.initElements(new AjaxElementLocatorFactory(this.driver, TIMEOUT), this);
}
//other common methods which can be utilized in your respective child Page classes
}
----------------------------------------------------
public class LoginPage extends BasePage {
//Your Locators and Weblelements
private static final LOGIN_ID = "login";
//Constructor to supply webdriver
public LoginPage(WebDriver driver) {
super(driver);
}
//your action methods
public void loginToApp(){
driver.findbyelement(By.ID(LOGIN_ID)).click
}
}
----------------------------------------------------
public class LoginTest extends BaseTest{
public LoginPage login;
BeforeAll()
{
login = new LoginPage(driver);
}
#Test
public void verifyLogin(){
login.loginToApp();
}
}