Selenium WebDriver page object - selenium-webdriver

Quick question about page objects in selenium webdriver. our site is very dynamic with lots of ajax and various authentication states. It is tough to figure out how to define each page object BUT lets say I have figured that out and defined several page objects that represent our site.
How do you handle crossing from page to page. So I get a page object for my home page and one for my account page and one for my results page. Then I need to write a test that traverses all my pages to simulate a user performing multiple actions.
How do you say give me a HomePage object to create a new use -> then get a account page object to go perform some user actions - then get a result page object to verify those actions all from a single script.
How are people doing this?
thanks

When you're simulating having the user enter a new URL into the URL bar of the browser, then it's the responsibility of the test class to create the page object it needs.
On the other hand, when you're doing some operation on the page that would cause the browser to point to another page -- for example, clicking a link or submitting a form -- then it's the responsibility of that page object to return the next page object.
Since I don't know enough about the relationships between your home page, account page, and result page to tell you exactly how it'd play out in your site, I'll use an online store app as an example instead.
Let's say you've got a SearchPage. When you submit the form on the SearchPage, it returns a ResultsPage. And when you click on a result, you get a ProductPage. So the classes would look something like this (abbreviated to just the relevant methods):
public class SearchPage {
public void open() {
return driver.get(url);
}
public ResultsPage search(String term) {
// Code to enter the term into the search box goes here
// Code to click the submit button goes here
return new ResultsPage();
}
}
public class ResultsPage {
public ProductPage openResult(int resultNumber) {
// Code to locate the relevant result link and click on it
return new ProductPage();
}
}
The test method to execute this story would look something like this:
#Test
public void testSearch() {
// Here we want to simulate the user going to the search page
// as if opening a browser and entering the URL in the address bar.
// So we instantiate it here in the test code.
SearchPage searchPage = new SearchPage();
searchPage.open(); // calls driver.get() on the correct URL
// Now search for "video games"
ResultsPage videoGameResultsPage = searchPage.search("video games");
// Now open the first result
ProductPage firstProductPage = videoGameResultsPage.openResult(0);
// Some assertion would probably go here
}
So as you can see, there's this "chaining" of Page Objects where each one returns the next one.
The result is that you end up with lots of different page objects instantiating other page objects. So if you've got a site of any considerable size, you could consider using a dependency injection framework for creating those page objects.

Well, I created my own Java classes which represent the pages:
Say, the below is code to represent home page. Here user can login:
public class HomePage{
private WebDriver driver;
private WebElement loginInput;
private WebElement passwordInput;
private WebElement loginSubmit;
public WebDriver getDriver(){
return driver;
}
public HomePage(){
driver = new FirefoxDriver();
}
public CustomerPage login(String username, String password){
driver.get("http://the-test-page.com");
loginInput = driver.findElement(By.id("username"));
loginInput.sendKeys(username);
passwordInput = driver.findElement(By.id("password"));
passwordInput.sendKeys(password);
loginSubmit = driver.findElement(By.id("login"));
loginSubmit.click();
return new CustomerPage(this);
}
}
And the page for Customer can look like this. Here I am demonstrating, how to get, say, logged in user:
public class CustomerPage{
private HomePage homePage;
private WebElement loggedInUserSpan;
public CustomerPage(HomePage hp){
this.homePage = hp;
}
public String getLoggedInUser(){
loggedInUserSpan = homePage.getDriver().findElement(By.id("usrLongName"));
return loggedInUserSpan.getText();
}
}
And the test can go like this:
#Test
public void testLogin(){
HomePage home = new HomePage();
CustomerPage customer = home.login("janipav", "extrasecretpassword");
Assert.assertEquals(customer.getLoggedInUser(), "Pavel Janicek");
}

You generally want to model what a user actually does when using your site. This ends up taking the form of a Domain Specific Language (DSL) when using page objects. It gets confusing with reusable page components though.
Now that Java 8 is out with default methods, reusable page components can be treated as mixins using default methods. I have a blog post with some code samples found here that explains this in more detail: http://blog.jsdevel.me/2015/04/pageobjects-done-right-in-java-8.html

I suggest you use a framework that provides support for these patterns. Geb is one of the best one out there. Below is an example taken from their manual
Browser.drive {
to LoginPage
assert at(LoginPage)
loginForm.with {
username = "admin"
password = "password"
}
loginButton.click()
assert at(AdminPage)
}
class LoginPage extends Page {
static url = "http://myapp.com/login"
static at = { heading.text() == "Please Login" }
static content = {
heading { $("h1") }
loginForm { $("form.login") }
loginButton(to: AdminPage) { loginForm.login() }
}
}
class AdminPage extends Page {
static at = { heading.text() == "Admin Section" }
static content = {
heading { $("h1") }
}
}

I enjoy writing Selenium Webdriver tests using the Page Object pattern. But was personally annoyed at the verbosity and repetition of having to always explicitly instantiate and return the next page or page component. So with the benefit of Python's metaclasses I wrote a library, called Keteparaha, that automatically figures out what should be returned from a selenium page object's method calls.

Related

How to inject a Drone instance without managing its lifecycle?

I have a Graphene Page Object.
#Location("/page")
public class MyPage {
#Drone
private WebDriver driver;
// page methods using the driver
}
And a Test Class that uses the page object.
#RunWith(Arquillian.class)
public class MyTest {
#Test
public void test(#InitialPage MyPage page) {
// use page & assert stuff
}
#Test
public void anotherTest(#InitialPage MyPage page) {
// use page & assert stuff even harder
}
}
Now, I've decided that MyTest should use method scoped Drone instances. So I add...
public class MyTest {
#Drone
#MethodLifecycle
private WebDriver driver;
Now when I run the test I get two browsers and all tests end with errors. Apparently this lifecycle management is treated as a qualifier too.
Yes, adding #MethodLifecycle in MyPage too helps. But this is not a solution - a page shouldn't care about this and should work in any WebDriver regardless of its scope. Only tests have the knowledge to manage the drone lifecycles. A page should just use whatever context it was invoked in. How can I achieve that?
This may be the answer:
public class MyPage {
#ArquillianResource
private WebDriver driver;
But I'm afraid that this skips some Drone-specific enriching. Also not sure if it will correctly resolve when there are multiple Drone instances.

I am getting null pointer exception for webelement defined under #FindBy annotation in page factory model

I'm new in Selenium learning. I'm getting null pointer exception when I try to use web element - Milestone_Tile_Text.click; in my code but it works fine when I use
LoginTestScript.fd.findElement(By.linkText("Milestone")).click();
Please see below code I have used PageFactory.initElements as well but not sure how to solve this error.
public class MilestoneTileModel
{
GenerateTestData objtestdata = new GenerateTestData() ;
public MilestoneTileModel() //constructor
{
PageFactory.initElements(LoginTestScript.fd, this);
}
#FindBy(xpath="//a[text()='Milestone']")
WebElement Milestone_Tile_Text;
public void Milestone_Tile_Click()
{
Milestone_Tile_Text.click();
LoginTestScript.fd.findElement(By.linkText("Milestone")).click();
LoginTestScript.fd.findElement(By.xpath("//*#id='CPH_btnAddNewMilestoneTop']")).click();
}
}
Timing issues might occur more often when you use an init method.
The timing issue is when you init an element the driver immediately try to find the elements, on failure you will get no warning but the elements will refer null.
The above can occur for example because the page was not fully rendered or the driver see an older version of the page.
A fix can be to define the elements as a property and on the get of the property use the driver to get the element from the page
Please note that selenium does not promise the driver sees the latest version of the page so even this might break and on some situations a retry will work.
First problem what I see: You didn't set LoginTestScript
Following documentation at first you need to set PageObject variable:
GoogleSearchPage page = PageFactory.initElements(driver, GoogleSearchPage.class);
The best way to rich that point is separate Page Object Model and scenario scipt
You fist file POM should contain:
LoginTestPOM
public class LoginTestPOM {
#FindBy(xpath="//a[text()='Milestone']")
WebElement MilestoneTileText;
public void clickMilestoneTitleText(){
MilestoneTitleText.click();
}
}
TestScript
import LoginTestPOM
public class TestLogin {
public static void main(String[] args) {
// Create a new instance of a driver
WebDriver driver = new HtmlUnitDriver();
// Navigate to the right place
driver.get("http://www.loginPage.com/");
// Create a new instance of the login page class
// and initialise any WebElement fields in it.
LoginTestPOM page = PageFactory.initElements(driver, LoginTestPOM.class);
// And now do the page action.
page.clickMilestoneTitleText();
}
}
This is basis of Page Object Pattern.
NOTE: I'm writing that code only in browser so it could contain some mistakes.
LINK: https://github.com/SeleniumHQ/selenium/wiki/PageFactory
The "ugly" solution without page object pattern is:
UglyTestScript
public class UglyTestLogin {
public static void main(String[] args) {
// Create a new instance of a driver
WebDriver driver = new HtmlUnitDriver();
// Navigate to the right place
driver.get("http://www.loginPage.com/");
// DON'T create a new instance of the login page class
// and DON'T initialise any WebElement fields in it.
// And do the page action.
driver.findElement(By.xpath("//a[text()='Milestone']").click()
}
}

Webdriver: what is the right way to write a user defined function

I am writing a login function, I want this function to exit gracefully in case of an error/unexpected condition.example: when I enter the password, if the text field has changed/not present I want the function to exit with the message saying that password field is not present. the function status should be "Fail" and the exact failure message. same way it should handle any other exception that might occur. What is the right way to right this function, so that I can follow this practice in the future functions that I write that deals with web objects?
public boolean Login(String userName, String password, String url)
{
_driver= new FirefoxDriver();
_driver.manage().window().maximize();
_driver.manage().timeouts().implicitlyWait(20,TimeUnit.SECONDS);
_driver.get(url);
_driver.findElement(By.Xpath(".//*[id='usename']")).sendKeys(userName);
_driver.findElement(By.Xpath(".//*[id='pword']")).sendKeys(password);
_driver.findElement(By.Xpath(".//*[id='btn']")).click();
String title = _driver.getTitle();
boolean pass;
if (title.toLowerCase().contains("homepage"))
{
pass=true;
}
else
{
Assert.fail("Manager home page not opened after login");
pass=false;
}
return pass;
}
You would want to remove the driver setup and navigating to the initial URL from your function. Your function should only log in... to match the name, Login(). If you want to use the page object model, you would do something like
The main script would look like
_driver = new FirefoxDriver();
_driver.manage().window().maximize();
_driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
_driver.get(url);
LoginPage loginPage = new LoginPage(_driver);
loginPage.Login(userName, password);
Assert.assertTrue(_driver.getTitle().contains("homepage"), "Log in successful");
While the LoginPage class would look like
public class LoginPage
{
WebDriver _driver;
public LoginPage(WebDriver driver)
{
_driver = driver;
}
public void Login(String userName, String password)
{
_driver.findElement(By.id("usename")).sendKeys(userName);
_driver.findElement(By.id("pword]")).sendKeys(password);
_driver.findElement(By.id("btn")).click();
}
}
BTW, I changed your locators to use By.id() instead of XPath. You should avoid using XPath unless it's absolutely necessary... finding an element by containing text, etc. Start with By.id() then use By.cssSelector()... if those two won't work, then fall back to XPath. XPath is slower, more likely to be brittle, and has inconsistent support in browsers.

How to share data resources between widgets in GWT

I am using GWT and AppEngine for a project. I would like to know how can I share data (ArrayList objects)between widgets, so I could centralize the logic and reduce the number of RPC calls to the server.
I have thought of two ways, but I don't know which is better:
1) When I instantiate the widget, I pass the ArrayList object as a parameter, although I don't know how to do that because the widget gets instantiated with :
ThisAppShell shell = GWT.create(ThisAppShell.class);
2) By using a mechanism like eventBus
http://www.dev-articles.com/article/Gwt-EventBus-(HandlerManager)-the-easy-way-396001
When the user loads the application,after the login process is complete, I would like to download a list of employees which should be available for all widgets. This should all be done in the onModuleLoad() method. I would like to download them all at startup because I would like to implement some sort of caching mechanism. For example, I want to have 2 ArrayList instances:
- emplListOnStart which is populated when the application is loading
- emplListChanges, an array on which the user will make modifications from inside widgets.
After the user has finished making the changes (he presses the "Save" button), the two arrays will be compared, the differences will be saved in appengine (via RPC) and also updated in emplListOnStart.
This is the code for the EntryPoint class:
public class ThisApp implements EntryPoint {
ThisAppShell shell = GWT.create(ThisAppShell.class);
LoginServiceAsync loginService = GWT.create(LoginService.class);
private ArrayList<Employee> emplListOnStart;
private ArrayList<Employee> emplListChanges;
public void onModuleLoad() {
RootLayoutPanel.get().clear();
RootLayoutPanel.get().add(shell);
loginService.isAuthenticated(new AsyncCallback<UserDto>() {
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
public void onSuccess(UserDto result) {
//Here I should load the emplListOnStart list;
}
});
shell.getLogoutLink().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
loginService.logout(new AsyncCallback() {
public void onFailure(Throwable caught) {
}
public void onSuccess(Object result) {
//Here the user will get logged out
}
});
Window.Location.assign("");
}
});
}
}
And here is the code for the widget:
public class ThisAppShell extends Composite {
private static ThisAppShellUiBinder uiBinder = GWT
.create(ThisAppShellUiBinder.class);
interface ThisAppShellUiBinder extends UiBinder<Widget, ThisAppShell> {
}
#UiField
Anchor logout_link;
#UiField
StackLayoutPanel stackLPanel;
#UiField
TabLayoutPanel tabLPanel;
public ThisAppShell() {
initWidget(uiBinder.createAndBindUi(this));
initializeWidget();
}
public void initializeWidget() {
stackLPanel.add(new HTML("Manage empl."), new HTML("Employees"), 30);
stackLPanel.add(new HTML("Manage Dept."), new HTML("Departments"), 30);
// Add a home tab
HTML homeText = new HTML("This is the home tab");
tabLPanel.add(homeText, "Home");
// Add a tab
HTML moreInfo = new HTML("This is the more info tab");
tabLPanel.add(moreInfo, "More info");
// Return the content
tabLPanel.selectTab(0);
}
public Anchor getLogoutLink() {
return logout_link;
}
}
Is this possible, or how could this be done better?
Thank you.
I think there are two ways to do it:
Create a setter on your widget to set your ArrayList instances (setData()). You can then call this function in the onSuccess method of your loginService.
Inject the singleton instance of a global EventBus into your widget (using i.e. gin/guice) and fire an event containing your data. In the widget you have to attach an EventHandler for the specific event (i.e. LoadEmplListEvent).
I think both solutions are fine to use.
Solution one creates a tighter coupling to your widget but is easier to implement and I think you should take this route if you only have a small number of widgets where you work
with the data.
Solution is a cleaner approach because it de-couples your widgets from the rest. You fire the event the data in your onSuccess method once and you don't care about the widgets.
The widgets that are interested in the data will make sure that they handle the event appropriately (by handling the event). I guess if you have a lot of widgets that have to deal with the data the second approach is the one to go for.

how to pass parameter to a page?

I have a page that is loaded into a frame. in the code behind, i have a string variable called mode. What i want to do is when a hyperlink is clicked, open the page and set the mode
I was hoping to do it declaratively. I tried doing
NavigatUri="myPage?mode=edit"
and then adding the following to the code behind after the initialize component call
mode = this.NavigationContext.QueryString["mode"];
But I was getting a page not found error. I have a feeling I'm on another planet. I'm new to silverlight. How do i navigate to a page in this fashion and pass that argument?
Take a look at the UriMapper in you main page. The final UriMapping would normally be the catch all that looks like this:-
<uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>
You will note the "/" at the beginning of the Uri and the corresponding page belongs in the "/Views" folder.
Use the attribute:-
NavigatUri="/myPage?mode=edit"
make sure your page in the Views folder.
However I'm pretty sure you already have that. Your real problem is your attempt to access the NavigationContext in the execution the page constructor. Its not available at that point in the pages lifecycle. You should not attempt to use it until OnNavigatedTo is executed.
public partial class MyPage : Page
{
public MyPage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string mode = null;
if (NavigationContext.QueryString.ContainsKey("mode"))
{
mode = NavigationContext.QueryString["mode"];
}
// Do stuff with mode.
}
}
The reason you are seeing "page not found error" is thats because the Nav apps ErrorWindow just assumes any failure to load a page was because it wasn't found. Which assuming you've coded your pages correctly is probably a reasonable assumption.

Resources