Introducing page-object gem

page-object is a simple ruby gem that assists in creating flexible page objects for testing browser based applications. To understand the inspiration for this gem please read this blog post.

This post will walk you through some of the core features of the gem. Most of the materials in this post are also on the project wiki. Please refer to the wiki for updated documentation.

Get me started right now!

For the impatient among you we will get directly to showing you how to use it.

Create your page

The first thing you must do is create your page objects. These are simple ruby classes that include the PageObject Module.

class RegistrationPage
  include PageObject
end

By including the PageObject Module you have added a lot of capabilities to your page. Let’s take a look at how we might use some of that right now.

Describe your page

After you create your page object class you need to describe the web page this class represents. The RegistrationPage example might look like this:

class RegistrationPage
  include PageObject

  text_field(:name, :id => 'name')
  text_field(:email, :id => 'email')
  button(:register, :value => 'Register')
end

By calling these methods, the PageObject Module will add several additional methods for you. To learn about what methods are available and what methods they generate please see this page.

Use your page

Now that we have a basic page object defined it is time to put it to use. You can use either watir-webdriver or selenium-webdriver as the driver gem. Just pass them into the constructor.

browser = Watir::Browser.new :firefox
registration_page = RegistrationPage.new(browser)

or

browser = Selenium::WebDriver.for :firefox
registration_page = RegistrationPage.new(browser)

Once created, you can interact with the page using the generated methods.

registration_page.name = 'Test User'
registration_page.email = 'test@example.com'
registration_page.register

That’s all there is to getting started with the gem. The good news is that there is a lot of additional functionality that was not covered in this brief introduction. Continue reading to learn more.

Page-Object Features

Here are a few of the features you will find in this gem:

A simple DSL for defining and interacting with the content of your page

The PageObject Module adds several methods to your page object. The methods that are added follow a similar pattern. Let’s take a look at a few of the methods and their generated output.

The call to text_field like this one

text_field(:first_name, :id => 'first')

will produce the following three methods

first_name               # return the value in the text field
first_name=              # set the value in the text field
first_name_element       # return the text field element

There is one special purpose generator method that does not follow this pattern. When you call the page_url method

page_url 'http://google.com'

it will produce a method named goto. To see all of the methods available and what methods the generated read the documentation for the Accessors module here.

Support for both watir-webdriver and selenium-webdriver

The page-object gem can use either watir-webdriver or selenium-webdriver as the underlying gem to drive the browser. The way you choose which driver to use is by passing it into the constructor of your PageObject

browser = Watir::Browser.new :firefox
registration_page = RegistrationPage.new(browser)

or

browser = Selenium::WebDriver.for :firefox
registration_page = RegistrationPage.new(browser)

In order to make this work seamlessly, the page-object gem had to add capabilities found in one driver and not in the other. Let’s look at one example.

Locating elements

There were differences in the way Watir and Selenium allowed you to locate elements on a page. We had to add functionality to page-object to eliminate those differences. Here are a few notable ones:

  • Watir allows you to provide multiple locators when identifying an element on a page. Selenium only allows you to provide one locator. With page-object you can now use multiple parameters when using Selenium.
  • Watir provides the ability to use :index when locating an element. Selenium did not have this capability. With page-object you can now use :index when using Selenium.
  • In Selenium you could not find a link by :href while this ability existed in Watir. With page-object on Selenium you can now use :href when identifying a link.
  • Watir supports finding a hidden field by :text. This ability does not exist in Selenium. With page-object you can find a hidden field by :text when using Selenium.
  • What did not support finding div, span, table, table data, ordered lists, unordered lists, and list items by :name. Selenium had this ability. With page-object you can now find these elements using :name.

Creating and using page objects

Alister Scott blogged about an idea for creating a factory to create instances of page objects. In the past I have blogged about page objects returning page objects. After a lot of contemplation, I am completely in the Alister camp. page-object now supports this approach as well.

PageFactory

A module named PageFactory provides this ability. This module has two methods.

def visit_page(page_class, &block)
def on_page(page_class, visit=false, &block)

Let’s take a look at how you would use these methods in your cucumber scripts.

Given /^I am on the registration page$/ do
  visit_page RegistrationPage
end

This call will cause the browser to open the page specified by the call to page_url in the class.

class RegistrationPage
  include PageObject

  page_url "http://mysite.com/registration"
  ...
end

If you wish to perform some activity on that page when you navigate to it you can pass a block to the method.

When /^I register on the registration page$/ do
  visit_page RegistrationPage do |page|
    page.register_user
  end
end

If you are already on a page and wish to interact with it you can use the on_page method.

Then /^I should be able to cancel my order$/ do
  on_page CheckoutPage do |page|
    page.cancel_order
  end
end

Handling Ajax calls

The secret to working with Ajax is having the ability to wait until different page events occur. The page-object gem supports waiting at two different levels – the page level and the element level. Let’s look at both.

Page level waiting

On the PageObject module (and therefore on your page objects) there is a method that assist with waiting.

def wait_until(timeout=30, message=nil, &block)

This method will wait until the passed in block returns true. If the block does not return true within the specified timeout seconds then an error is thrown with a default message or the provided message. Let’s take a look at how we might use this.

@page.wait_until do
  @page.text.include? "Success"
end

In this example we are using the default timeout and message. This code will wait until the page includes the text “Success”. Let’s look at another example.

@page.wait_until(5, "Call not returned within 5 seconds") do
  @page.text.include? "Value returned from Ajax call"
end

In this example we are setting the timeout to 5 seconds and providing a custom message.

Element level waiting

On the Element class we have several methods to assist with waiting.

def when_present(timeout=5)
def when_visible(timeout=5)
def when_not_visible(timeout=5)
def wait_until(timeout=5, message=nil, &block)

The usage of the first three methods are fairly self explanatory. They will simply wait until the element is present, visible, or not visible and then continue. If the wait exceeds the timeout value then an error occurs. Let’s look at a simple example.

@page.continue_element.when_visible do
  @page.continue
end

As of release 0.3.2 you can combine these into a single call like this:

@page.continue_element.when_visible.continue

In this code we are waiting until a link defined by a call to link(:continue, :id => 'cont') is visible and then we are clicking it.

There are times when you want to wait for something other than the element being present or visible. The last wait call wait_until handles this well. It will wait until the block returns true.

Handling JavaScript popups

Handling JavaScript popups in Watir and Selenium has always been a hassle. The approach the page-object gem takes is to intercept the call to the popup and cause it to not happen. At the same time, the gem provides the information to the user that they would wish to receive if they had access to the popup. Let’s look at a few examples.

Alerts

The PageObject module has a method named alert. Her’s how you can use it.

message = @page.alert do
  @page.button_that_cases_the_alert
end
message.should == "My Alert!"

This call demonstrates that we are passing a block to the alert method. That block is the code that causes the alert to occur. The alert will not popup but the message that was included in the popup is returned by the block.

Confirms

PageObject also has a confirm method that handles confirm popups. It works the same way as the alert method except it requires a boolean parameter which is the value returned to the browser when the confirm popup is called. Here’s an example.

message = @page.confirm(true) do
  @page.button_causing_the_confirm
end

Prompts

Finally, PageObject has a prompt method that follows the same pattern. There are two differences. The first is that it accepts a parameter that is the value returned from the prompt. The second is that it returns a Hash with two keys – :message contains the message from the confirm popup and :default_value contains the default value if one was provided.

confirm_result = @page.prompt("Cheese") do
  @page.what_do_you_like_button
end
confirm_result[:message].should == "What do you like?"

Additional documentation

You can read the RDoc documentation here.

The ChangeLog with the gem’s history can be found here.

Requesting features and reporting issues

The project has an Issues tracker where you can request new functionality and report defects.

85 thoughts on “Introducing page-object gem

  1. Pingback: A Smattering of Selenium #57 « Official Selenium Blog

  2. This is really amazing!! I have been playing around with this gem for a week now, and I should say that this addresses the common issues with UI automated testing.

    I am going to implement this model in my forthcoming project.

    Keep up the good work…kudos to you and Alister as well.

    Cheers,
    Karthik

  3. Hey Cheezy, this gem has undoubtedly saved me literally weeks of work in setting up useful mappings in my scripts!

    One question, though: Is file_field not supported, or am I just missing it while I’m looking through the method and class lists in your docs?

    • Abe,

      I haven’t had a request for file_field support yet. Please add an issue to the project page and I’ll be sure to add it in the next release.

      -Cheezy

  4. This is a really nice framework and I’m learning a lot by studying it.

    I’m curious about one thing: how would you incorporate RSpec::Matchers in this? For example, let’s say I wanted something like this:

    RSpec::Matchers.define :should_have_title do |text|
    match do |the_page|
    the_page.should_have_title?(text)
    end
    end

    It seems I couldn’t just do something like this:

    @page.should_have_title(/^Test App$/)

    The reason I ask is because while I like using RSpec as an underlying check mechanism, it’s messages are often not as helpful when you want business users to be able to get better messages about why something failed. For example, right now RSpec will report the following if a page title is not correct:

    expected “Test” to match /^Test App$/ (RSpec::Expectations::ExpectationNotMetError)

    Without context, that message is a bit confusing. What I’d like is:

    Expected “Test App” for the title of the page. Found “Test” instead.

    So I’m just curious how you might go about incorporating such things into a framework as you have constructed it?

    – Jeff

    • But you can specify the failure message as well:

      RSpec::Matchers.define :be_a_multiple_of do |expected|
      match do |actual|
      actual % expected == 0
      end
      failure_message_for_should do |actual|
      “expected that #{actual} would be a multiple of #{expected}”
      end
      end

      There’s also #failure_message_for_should_not

  5. Wouldn’t you know it? After posting, I figured it out. You could just make a matchers.rb file (with a Matchers module). Have that included in the page-object module. It actually works pretty seamlessly.

    Sorry for the comment bloat to the article.

  6. I don’t know how people are getting this to work because it consistently fails for me. What am I doing wrong?

    I installed the gem.

    I have a basic Cucumber setup:

    (1) I have a features directory. In that directory is a login.feature file.
    (2) I have a login_page.rb file in the features directory. It looks like this:

    class LoginPage
    include PageObject

    page_url “http://mysite”
    end

    I also have some declarations in place that I snipped for brevity’s sake.

    (3) I have a step_definitions folder with a login_steps.rb file.

    It’s not clear from the directions on this blog post that I’m supposed to include page-object but, of course, I know I need to. I put the following line:

    require ‘page-object’

    in my env.rb file. I assume that’s okay. It’s not at all clear where I’m supposed to put this line:

    browser = Watir::Browser.new :firefox

    So, again, I just put it in env.rb.

    One of my steps looks like this:

    Given /^I am on the login page$/ do
    @page = LoginPage.new(browser)
    end

    When I run Cucumber, I get this:

    Using the default profile…
    uninitialized constant Object::Watir (NameError)

    So I took out the browser = line from env.rb.

    That actually gets me farther in that the step tries to run, but then I get this:

    Given I am on the login page
    undefined local variable or method `browser’ for # (NameError)

    I think the main problem I have is I don’t know where to get the browser instance. Even if I put this line:

    browser = Watir::Browser.new :firefox

    In the step definition file itself, it still doesn’t work. I get this:

    Using the default profile…
    uninitialized constant Object::Watir (NameError)
    c:/Terminus/_refs/testerproject/features/step_definitions/login_steps.rb:1:in `’

    Am I missing something obvious?

    • Tatiana,

      The problem you are experiencing is that your browser variable is a local variable and therefore goes out of scope. I would suggest trying the following:

      add the following to your env.rb file:


      require 'page-object'
      require 'watir-webdriver'

      Before do
      @browser = Watir::Browser.new :firefox
      end

      After do
      @browser.close
      end

      When you create your page objects you can pass the @browser instance variable to the constructor. If you wish to use the PageFactory in your step definitions then you should also add this to your env.rb file:


      require 'page-object/page_factory'

      World(PageObject::PageFactory)

      Then your step above could be rewritten as:


      Given /^I am on the login page$/ do
      visit_page LoginPage
      end

      Additional steps could use code like this:


      on_page(SomeOtherPage) do |page|
      page.perform_first_action
      page.perform_second_action
      end

      I hope this helps you solve your issue.

  7. Hi.

    The page factory thing never seems to work for me. I’m wondering if I’m putting the call in the wrong place. I have this line:

    World(PageObject::PageFactory)

    I always get this error when I do:

    uninitialized constant PageObject::PageFactory (NameError)

    I had my World line in my env.rb file at first. Then I moved it to a helpers.rb file that is in the support directory along with env.rb. I always get the above error. My page objects do have the line include PageObject in them. I am requiring page-object from my initial files.

    Is there something else that has to be done to get this to work correctly?

      • Hey Tatiana,

        Do you remember how you figured this problem out because I am running into the same error that you were: uninitialized constant PageObject::PageFactory (NameError). In my env.rb file I have

        require ‘page-object/page_factory’

        World PageObject::PageFactory

        and in my pages.rb file I have:
        class ExamplePage
        include PageObject

        I also have everything set up with cucumber – feature files, step definitions

        I can’t figure out why I keep getting this error no matter what I do. Any insight you have would be greatly appreciated.

        Jenna

        • Jenna,

          Can you post some of the code that demonstrates how you are trying to use the PageFactory module? That would help me help you.

          Thanks
          -Cheezy

          • Hey Cheezy,
            Thank you for responding to me. I guess I made a new post instead of replying to your comment. I provided some of my code below. Any insight you could provide would be greatly appreciated! I am switching from Allister’s watir-page helper to your page-object framework. Thanks in advance!

            Jenna

  8. Pingback: Jason’s Technology Radar | Element 84

  9. Yes. This is my page:

    class AboutCorporatePage
      include PageObject
      
      direct_url "www.master.staging"
      expected_title "OneRecovery"
      
      #Login iframe:
      frame :login_iframe, :id => "login_iframe"
      text_field(:email) { |page| page.frame(:id => "login_iframe").text_field(:class => /email_input/) } #email text field
      text_field(:password) { |page| page.frame(:id => "login_iframe").text_field(:class => /password_input/) } #password text field
      button(:login) { |page| page.frame(:id => "login_iframe").button(:class => /login_submit/) } #login button
      link(:forgot_log) { |page| page.frame(:id => "login_iframe").link(:class => /forgot_login/) }
      checkbox(:remember) { |page| page.frame(:id => "login_iframe").checkbox(:class => "remember_me") }   #remember me checkbox 
      link(:request_invite) { |page| page.frame(:id => "login_iframe").link(:class => /request_invite/) } #request invite link
     
    # Methods
      #Login using login iframe  
      def login_creds(email, pass) 
        self.email = email
        self.password = pass
        self.login     
      end
      
    end
    

    This is my step definition:

    Given /^I am on the corporate website$/ do
      site "About"
      visit :corporate
    end
    
    When /^I enter my login credentials$/ do
      on :corporate do |page|
        page.login_creds("qa+10@onerecovery.com", "Rehabh#123")
      end
    end
    
    Then /^I enter OneRecovery$/ do
      site "Social"
      on :Newsfeed do |page|
        sleep 3
        page.if_emoticon_popup(/emoticon_5/)
        page.first_post_li.wait_until_present  #wait until main column loads - this is last thing to load on page and is often a point of failure.
      end
    end
    

    And I always get this error: “uninitialized constant AboutCorporatePage::PageObject (NameError)”

    If you want to access the page you can go to http://www.onerecovery.com (the one in my code is our testing site and I don’t think it is publicly available)

    My env.rb file contains:

    require 'watir-webdriver'
    require 'pages.rb'
    require 'page-object'
    require 'page-object/page_factory'
    World PageObject::PageFactory
    

    Any insight would be helpful! Thank you!

    Jenna

    • The first thing I see is that you are not using the visit and on methods properly. Try change to this form:

      Given /^I am on the corporate website$/ do
        site "About"
        visit AboutCorporatePage
      end
       
      When /^I enter my login credentials$/ do
        on AboutCorporatePage do |page|
          page.login_creds("qa+10@onerecovery.com", "Rehabh#123")
        end
      end
       

      One other thing, I am not familiar with the call to site. What is this used for?

      • Hey Cheezy,

        Thank you so much for the response. The call to site I have defined in my env.rb file as:
        def site name
        @site = name
        end
        end
        It is something that I used with Alister’s Watir-page-helper gem . I use it because I am testing multiple sites that interact with each other and utilize exact same code in multiple places. So for example there is a place that users can login to the site which is considered the “social” site and then there is another place where admins login which is the “admin” site. The social site and admin sites are two different sites but the code to login is exactly the same so I use the call to site so that I don’t have to duplicate my automation code. Does that make sense?? I am a newbie coder so forgive me if I don’t sound like I have all of the right terminology. The two sites communicate using rest.api

        I changed my visit and on calls as you showed me above but I am still getting the same error: uninitialized constant AboutCorporatePage::PageObject (NameError)

        I tried taking out the site “About” line and I also double checked to make sure that my env.rb file has all the necessary elements (I am comparing to the env.rb file in Alister’s etsy example where he showed how to transition from his watir-page-helper gem to your page-0bject gem) and I still get the same error.

        Is there anything else that you can see that I am doing wrong. Is there other info I can give you that will help you help me? I really appreciate your insight and I will continue to try different things in the mean time. Thank you in advance.

        Jenna

          • Hey Cheezy,

            I figured out what was going on… I had gems that weren’t the correct versions (either ahead or behind) and now my automation is running nicely now that I have installed the correct versions. The only thing I noticed is that I use the li method to index in some of my automation code and the page-object gem does not support that method. Any way that will be included in the gem in the near future?

            I really appreciate you helping me and taking the time to look into my code for me. Thanks.

            Jenna

  10. Hi!

    I have an issue with PageFactory and don’t know if its on my side (probably…) or its a know problem.
    Using:

    Given /^i am on the home page$/ do 
      @BROWSER.goto('http://www.redsauce.net') 
    end
    

    …the three steps are passed. But using:

    Given /^i am on the home page$/ do 
      visit_page HomePage
    end
    

    …with an env.rb:

    require 'page-object'
    require 'watir-webdriver'
    require 'page-object/page_factory'
    require 'rubygems'
    
    World (PageObject::PageFactory)
    
    Before do
      @BROWSER = Watir::Browser.new 'firefox', :profile => $profile
      @home_page = HomePage.new(@BROWSER)
    end
    

    …and a class homepage:

    class HomePage
      include PageObject
      link(:desarrollo, :text => 'desarrollo')
      page_url 'http://www.redsauce.net'
    end
    

    …i get the following error:

    Given i am on the home page # step_definitions/redsauce_link_step.rb:6
    Unable to pick a platform for the provided browser (RuntimeError)
    ./step_definitions/redsauce_link_step.rb:8:in `/^i am on the home page$/’
    ./redsauce_link.feature:6:in `Given i am on the home page’

    Any tip, idea or suggestion about what could be wrong would be appreciated.
    Thank you so much!

    • Pablo,

      Thanks for using the gem. The solution to your problem is very simple. The PageFactory methods assume there is an instance variable named @browser. Simply rename @BROWSER to @browser and everything should work.

      One other note -> there is no need to create the HomePage instance variable in your Before block.

      -Cheezy

      • Thank you so much, it works fine now!

        But there is a strange behavior: creating the object on env.rb inside the “Before do” as:

        Before do
        @browser = Watir::Browser.new 'firefox'
        end

        …works fine, opening a new browser for each feature and closing it on the “After do”. That slows the execution because a new browser starts and closes every feature. But if i create the @browser out of the “Before do” in order to have the same browser session for all the features, i have a:
        ” Unable to pick a platform for the provided browser (RuntimeError)
        “, launching no test at all.

        Can you tell me please what am i doing wrong?
        Thank you so much!

        • The reason for this is that if you create it out of the Before block you are not creating an instance variable. Instead you are creating a class variable. The page-object gem is looking for an instance variable named @browser.

  11. Hi,

    I want to use the method initialize_page for one of my page objects.

    Could anyone please tell me what are the parameters required for this method or the method signature?

    • The method takes no parameters. Simply create the method like this:

      def initialize_page
        # put your code here
      end
      

      Please note that this method will be call when your page object is created. If it is created in multiple steps it will be called multiple times.

      -Cheezy

      • Hi Cheezy,

        Great thanks for your website, and I learn a lot from it.
        But I am two questions about page factory. one is when will the function “initialize_page” be called, like a constructor function of a page class; the other is sometimes when I use page factory like:
        on_page(MyPage).xxx
        even I am in MyPage, the “MyPage” will reload again. This trouble me a lot, and I find my implementation for MyPage is exactly like the way I implement other pages.
        I put my codes here for your reference:
        ***page class**
        class MyPage
        include PageObject
        link(:my_database, text: ‘My Database’)
        def initialize_page
        sleep 1
        end
        end

        ***use page factory to call the link my_database***
        on_page(MyPage).my_database #I have already in my page, but this function call will reload my page.

        Could you please help me out in your convenient time? Thanks a lot.

        • I’ll try to answer both questions here. The call to initialize_page only happens when a new PageObject class is initialized. Here is the initialize method from the PageObject module:

            def initialize(browser, visit=false)
              initialize_browser(browser)
              goto if visit && respond_to?(:goto)
              initialize_page if respond_to?(:initialize_page)
            end
          

          As you see, during class creation that method will be called if it exists.

          As far as on_page, it will not reload the page ever unless you override the default parameters. It will call the constructor of the PageObject class which will in turn call the initialize_page method. Here is the on_page method from PageFactory:

              def on_page(page_class, visit=false, &block)
                @current_page = page_class.new(@browser, visit)
                block.call @current_page if block
                @current_page
              end
          

          To summarize, the initialize_page method will be called every time a new instance of your PageObject class is created. The on_page method will create a new object each time it is called but will not reload your page unless you override the visit default value.

          • If the PageObject class is expecting a @browser variable how can I possibly run tests for different browsers? I was thinking of having @firefox @chrome @ie etc. and eventually run scenarios in parallel.

          • Luca,

            You are correct that if you use the PageFactory methods to instantiate your objects (which I highly recommend) then page-object expects there to be an instance variable named @browser. This in no way prohibits you from running your tests with different browsers. Many people do this all of the time. You will simply assign the proper object to the @browser depending on the browser you wish to run.

            Running your tests in parallel is also fairly easy these days. I would suggest looking at a gem named parallel_tests. If you plan to run many tests in parallel you will also need to setup a Selenium Grid 2. Both projects have good documentation so it you should have no problems setting it up. I have helped several companies setup a grid like this and it takes less than 30 minutes assuming all of the hardware is in place.

            -Cheezy

  12. This is interesting stuff. Is it possible (with PageObject) to dig into the contents of a page, find a specific DIV and then return its contents?

    <div id="abc">This is the stuff</div>
    

    I’d love to be able to ask my PageObject to return the “innerHTML” of the div with the id abc.

  13. Cheezy (or anyone else knowledgeable),

    In implementing page-object into my UI test infrastructure, I’m trying to provide some nested structure for my page definitions. Something like this:

    features
    -support
    -pages
    -city_login_page.rb
    -admin
    -site_settings
    -features_page.rb

    Given this structure, my features_page.rb is defined this way:

    class Admin::SiteSettings::FeaturesPage
    include PageObject

    #page-specific stuff here
    end

    All well and good, but when I actually try to run my cukes that leverage this page object:

    uninitialized constant Admin (NameError)

    Any pointers for how to make nested page_object definitions work?

  14. Hello, awesome gem and blog posts!

    I was wondering if you could write a blog post or elaborate on how your gem can be used to represent a specific area of a page rather than an entire page. I’ve read in several places that Page Objects don’t have to represent a single page and could instead represent a part of a page. For example, say we were writing tests against Gmail’s interface, it might be useful to split up the left hand column into its own “page object”, and then call methods on it.

    I’m new to Page Objects, so I might be way off with my assumptions, but it almost seems like there should be another ComponentObject class which represents part of a page. You would then be able to return instances of this object from PageObjects or other ComponentObjects. Perhaps a helper method ‘component’ would be useful as well. For example, instead of calling the ‘div’ method in the PageObject, we might call component(:side_bar, SideBar) which would then create a new instance of SideBar whenever we call the automatically declared side_bar method. Instead of calling page_url, SideBar could call “component_selector css: ‘div.side_bar'”. Perhaps a ‘components’ method would be useful as well.

    To mimic the PageFactory feature, there might be a ComponentFactory that would make on_component SideBar do { |component| … } work.

    Finally, my last thought was that when you have a component that is tied to a specific section of a page (e.g. a div tag that houses many other elements), it would be useful if each of the selectors used for registering elements (like buttons and div tags) only looked inside this parent tag. For example, Capybara lets me do something like this: find(‘div.side_bar’).find(‘button.submit’). Thus, when I am inside the SideBar class, and I register a button using button(:submit, css: ‘button.submit’), it automatically knows to only search for the ‘.submit’ inside this side bar and doesn’t get confused with elements that match the same selector but lie outside it.

    I was thinking of contributing to your project and incorporating some of these ideas into it, so I would really like to hear your thoughts on this. If your gem can already handle this fairly well, then I would love to see a blog post that would help new people know how to make this work! Thank you very much!

    • You simply declare it in your page class like this:

      select_list(:pay_type, :id => 'pay_type'
      

      And you can select a value like this:

      pay_type = 'Credit card'
      

      -Cheezy

  15. Hi, I’m trying to access the selenium web driver elements (PageObject::Platforms::SeleniumWebDriver::Element). But I’m getting Undefined method error when I use the elements like (visible? , text, exists?). If I need to include any class to use this elements already I have included Page Object.

    Could you please provide the solution?

  16. Could you please explain me how to test scrolling(Scroll down vertically) functionality in a pop up window.

    Thanks.

    • Please provide some information about the pop up window. I am unable to answer your question until I understand the html / page you are attempting to interact with.

    • Dmitriy,

      Many of the elements can be accessed by css but this is often not desired. The css will tend to be more brittle that other selection options. Why do you feel you need to use css?

      Here is the list of elements that can be found by css:

      text_field
      hidden_field
      text_area
      select_list
      link
      checkbox
      radio_button
      button
      div
      span
      table
      cell
      image
      form
      list_item
      unordered_list
      ordered_list
      h1 – h6
      paragraph
      file_field
      label
      area
      canvas
      audio
      video
      all generic element types

      -Cheezy

  17. Hi Cheezy,

    I very like you gem, great job.

    But I’m a new ruby programmer, so I has one question about this.

    I use page-object, and define the page url, how can I open the page?

    My code is this:

    require ‘page-object’
    require ‘watir-webdriver’

    class GooglePage
    include PageObject

    page_url “http://www.google.com.hk”
    text_field(:question, :name => ‘q’)
    button(:search, :name => ‘btnK’)
    end

    browser = Watir::Browser.new :chrome
    google_page = GooglePage.new(browser)

    When I run my code, it only open a chrome, but don’t go to the Google page.

    Do I need some other code? for example, use the google_page.navigation_to(“url”)?

    I’m confused because the RDOC said the page_url will execute “goto” method.

    Thanks.

    • My code like this:

      require ‘page-object’
      require ‘watir-webdriver’

      class GooglePage
      include PageObject

      page_url “http://www.google.com.hk”
      text_field(:question, :name => ‘q’)
      button(:search, :name => ‘btnK’)
      end

      browser = Watir::Browser.new :chrome
      google_page = GooglePage.new(browser)
      google_page.question = “page-object watir-webdriver”
      google_page.search

      And I run it by this command: ruby my_page.rb

      When I run my code like this, I get a error message:

      page_object.rb:976:in `instance_eval': unable to locate element, using {:name=>”q”, :tag_name=>”input or textarea”, :type=>”(any text type)”} (Watir::Exception::UnknownObjectException)

      Could you tell me my problem in this code?

      Thanks.

      • The reason you are getting the above error is that you are not on the page where it is trying to find the element. Go to the page first and then it will work.

    • There are several options here. The first thing you could do is simply call a method named `goto` on the page object. The other thing is that the constructor adds a second parameter that is defaulted to false. If you did this – `GooglePage.new(browser, true)` it would open the browser and take you to the page. Finally, the best way to do this is with the PageFactory but I’ll assume you haven’t gotten that far in the book so I’ll let you discover it as you continue.

  18. Hi cheezy,

    I noticed the PageFactory, but I have a question, does it only work in cucumber context?

    Because I got this error message when run my code.

    $ruby my_page_factory.rb

    my_page_factory.rb:4:in `': undefined method `World’ for main:Object (NoMethodError)

  19. require ‘watir-webdriver’
    require ‘page-object’
    require ‘page-object/page_factory’
    World PageObject::PageFactory

    class GooglePage
    include PageObject

    page_url “http://www.google.com.hk”
    text_field(:question, :name => ‘q’)
    button(:search, :name => ‘btnK’)
    end

    @browser = Watir::Browser.new :chrome

    vist_page GooglePage

    • Sorry for the twice comments, because I can’t successfully submit them in one comment.

      Are there some errors in my code?

      Thanks.

  20. Hi cheezy,

    I want achieve something like this,
    div(:close,:title=>”Close”) # page has multiple close buttons
    div(:domain,:id=>”Dom”)
    div(:role,:id=>”Role”)

    # I want to uniquely identify the close button w.r.t where it is located

    div(:closeDomain) do |page|
    page.domain_element.close_element
    end

    div(:closeRole) do |page|
    page.role_element.close_element
    end

    Currently i am getting error as undefined method “close_element”.
    Can something like this be achieved so that if the id of close changes then the rework is less and the code looks cleaner.

    Thanks,
    Archit

    • If I understand correctly you are saying that the close div is inside of the domain div. You could do something like this:

      div(:domain,:id=>”Dom”)
      div(:close) do |page|
      domain_element.div_element(:title => ‘close’)
      end

      In this case I am stating that you should first of all find the domain div and then find a div element nested within it that has the title ‘close’

      Just one thing – this couples to the structure of a div nested within a div. From my experience I think this is more likely to change than an id but your environment may be different.

      -Cheezy

      • Thanks for the quick reply Cheezy.

        As u suggested the code stands like this now,

        div(:domain,:id=>”Dom”)
        div(:role,:id=>”Role”)
        div(:user,:id=>”User”)

        div(:closeDomain) do |page|
        domain_element.div_element(:title=>”Close”)
        end

        div(:closeRole) do |page|
        role_element.div_element(:title=>”Close”)
        end

        div(:closeUser) do |page|
        user_element.div_element(:title=>”Close”)
        end

        Hope this is correct. But what I would have ideally preferred is a single declaration for the close element, because if the id changes then the rework is less and repetitions are less.

        Still the code u suggested will be helpful as currently we are used to accessing complex nested elements using @browser statements rather than using page objects which are easy to maintain.

        Thanks,
        Archit

        • If you have no need to access the wrapper div then you could combine them into a single call like this:


          div(:closeDomain) do |page|
          div_element(:id=>”Dom”).div_element(:title=>”Close”)
          end

          -Cheezy

  21. Hi Cheezy,
    Can you please tell me what exactly ‘World(PageObject::PageFactory)’ do ?
    adding it to my env.rb file has solved my problem of instantiating a class in which I have defined the elements of my test page and I am able to access the page using visit_page method but I just want understand it better rather than blindly using it .

    here is my class
    class Page
    include PageObject
    page_url “mytestpage”
    end

    my env.rb
    require ‘page-object’
    require ‘page-object/page_factory’
    require ‘watir-webdriver’
    require ‘testpage.rb’
    World(PageObject::PageFactory)
    Before do
    @browser = Watir::Browser.new :firefox
    end

    my cucumber
    Given I am on the testpage

    step_def
    Given(/^I am on the testpage$/) do
    visit_page Page
    end
    Thanks in advance
    Vamsi

    • World is a method provided by Cucumber that allows you to register a module and make the methods of that module available in your step definitions. In the case of your usage, you are registering the PageObject::PageFactory with cucumber which makes the methods on, visit, navigate_to, etc.

  22. Hi Cheezy,

    I’ve just come across the PageObject framework you’ve developed. What a fantastic library!!!! I’m a tester who is primarily involved with Java development. Is there something like this for Java? Is it even possible to have Java generate the various methods based on definitions marked within the page object class?

    Boy, having read through your blog, I’m beginning to think that maybe I should be coding in Ruby :-).

    • There is not something exactly like this in Java. Further, there are several additional ruby gems that I use frequently that also have no counterpart in Java. If you want to learn about these other gems you can get a copy of my book.

  23. Hi Cheezy,

    We are working with iframe, declared the elements in a page as following.
    module Iship
    class EnvoyCheckoutPage
    include PageObject
    include PageFactory

    in_frame(:id => ‘envoyId’) do |frame|
    #Shipping address elements
    text_field(:shipping_first_name, :id => ‘formId:fld_SHIPPING_ADDRESS__FIRST_NAME_id’, :frame => frame)
    text_field(:shipping_last_name, :id => ‘formId:fld_SHIPPING_ADDRESS__LAST_NAME_id’, :frame => frame)
    text_field(:shipping_address_line1, :id => ‘formId:fld_SHIPPING_ADDRESS__ADDRESS1_id’, :frame => frame)
    text_field(:shipping_address_line2, :id => ‘formId:fld_SHIPPING_ADDRESS__ADDRESS2_id’, :frame => frame)
    text_field(:shipping_address_line3, :id => ‘formId:fld_SHIPPING_ADDRESS__ADDRESS3_id’, :frame => frame)
    text_field(:shipping_city, :id => ‘formId:fld_SHIPPING_ADDRESS__CITY_id’, :frame => frame)
    text_field(:shipping_region, :id => ‘fld_SHIPPING_ADDRESS__REGION_id_input’, :frame => frame)
    select_list(:shipping_country, :id => ‘formId:fld_SHIPPING_ADDRESS__COUNTRY_id’, :frame => frame)
    text_field(:shipping_postal_code, :id => ‘formId:fld_SHIPPING_ADDRESS__POSTAL_CODE_id’, :frame => frame)
    text_field(:shipping_primary_phone_number, :id => ‘formId:fld_SHIPPING_ADDRESS__PHONE_PRIMARY_id’, :frame => frame)
    text_field(:shipping_alternate_phone_number, :id => ‘formId:fld_SHIPPING_ADDRESS__PHONE_ALT_id’, :frame => frame)
    text_field(:shipping_email, :id => ‘formId:fld_SHIPPING_ADDRESS__EMAIL_id’, :frame => frame)
    button(:submit_order_button, :id => ‘formId:submitBtnId’, :frame => frame)
    end
    end
    end

    populate_page_with method is not working for the above elements, as we are getting the error “Element belongs to a different frame than the current one – switch to its containing frame to use it”

    if we perform an action using _element like submit_order_button_element.click then also we are getting the same error like above.

    Could you please help us is there any built in method in page object for switch to a frame in a page(Ex:EnvoyCheckoutPage)

    • Observed the same issue for “span” and “cell” as well.

      spans(:item_names, :class => ‘fiftyone-order-item-name’, :frame => frame)
      cells(:quantities, :class => ‘qty’, :frame => frame)

      While accessing content from span and cell as mentioned below, We are getting error “Element belongs to a different frame than the current one – switch to its containing frame to use it”.

      ‘name’ => item_names_elements[i].text,
      ‘quantity’ => quantities_elements[i].text

  24. Hi Cheezy

    What is the best way to use PageObject::PageFactory to expose its methods (visit, on etc…) if I am not using cucumber and therefore cannot do a World PageObject::PageFactory?

    • Zubin,

      PageObject::PageFactory is a simple module. You can mix it in with any class to use it. If you are using RSpec you can add the following to your spec_helper.rb file:

      RSpec.config do |config|
      config.include PageObject::PageFactory
      end

      -Cheezy

  25. hey Cheezy,

    I’m getting the following error: undefined method `params’ for # (NoMethodError)

    here is my project treeL
    features
    -admin.feature
    -pages -> home_page.rb
    -step_definitions
    -support

    env.rb

    require ‘page-object’
    require ‘watir-webdriver’
    require ‘page-object/page_factory’

    World(PageObject::PageFactory)
    browser = Watir::Browser.new :firefox

    Before do
    @browser = browser
    end

    at_exit do
    browser.close
    end

    admin_steps.rb

    Given(/^I am logged in as an admin$/) do
    home_page = HomePage.new(@browser)
    visit_page home_page
    end

    home_page.rb

    class HomePage
    include PageObject

    page_url “http://demo.moodle.net/”
    end

    any thoughts?

    appreciate it

      • AWESOME!!!!!

        So there is no need to actually instantiate the class. This is just awesome.

        I was doing some research and I found another project of yours called ‘testgen’. I really liked it. Will you keep maintaining it too?

        appreciate the quick response

        • Rafael,

          The on method is instantiating the object for you.

          testgen is maintained. It is on of my now 17 ruby gems. Use it to create starter projects.

          • Thank you for the response once more.

            I do have two other questions. How would you do a test that checks if the element exists?

            I tried this, but it is not recognizing the “exists”.

            Is there other way to access the element within a method without the “self”?

            here is the code:

            class DashboardPage
            include PageObject

            page_url ‘http://demo.moodle.net/’

            paragraph(:front_page_settings, id: ‘frontpagesettings’)
            span(:site_administration, xpath: “//span”)

            def admin?
            self.front_page_settings.exists? && self.site_administration.exists?
            end

            end

            thanks again

          • Nevermind, just figured out:

            changed the admin? method to:

            def admin?
            front_page_settings_element.exists? && site_administration_element.exists?
            end

            And the self is only required when it is necessary to explicitly tell to use a global variable and not a local.

            thanks again

  26. hey Cheezy,

    In Watir I know it is possible to do something like this:

    element(:browse_all) { |listing_type| browser.link(:text => “#{listing_type}”) }

    Is it possible to do a similar thing with PageObject?

    thanks

  27. Hello Cheezy,
    Please, help with this.

    I’m just trying to run simple test, but facing some strange staff. Browser is opened, but url is not…

    What I’m doing wrong?

    require ‘page-object’
    require ‘selenium/webdriver’
    browser = Selenium::WebDriver.for :ff

    class SearchPage
    include PageObject
    page_url ‘http://google.com’
    p ‘test’
    end

    SearchPage.new(browser);

    Thanks.

  28. hi Cheezy,

    Can you please help me :
    how I can wrap exception handling in page-objects. ?

    when I define text_field, or button if something goes wrong while we enter text value, or click on button ( may be element not exists, visible etc) does page object take care of handling exceptions?

    Thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>