Sonja Leaf
Sonja Leaf

Reputation: 223

Common Page elements - Can they be inherited?

In a cucumber/Watir-webdriver/page-object environment, I need to verify the presence and functionality of common header and footer links. While these should all be the same, and work, I'd like to include them on all of the relevant pages.

I'm trying to keep up on DRY coding, and want to minimize the amount of times I need to identify Header/Footer elements that are shared on a web application.

I thought I could do this by including somethings 'common_links.rb' in the support/ folder:

module CommonLinks
  include PageObject
  # Header links
  link(:log_out_header_link, :text => "Log Out")
  link(:logo, :id => "logo")

  #Logged in nav links
  div(:nav, :id => "globalnav_wrapper")
  nav.link(:nav_activities, :href => "/activities/")

  # Footer Links
  link(:contact_us_footer, :text => "CONTACT US")

  # Social Media Links
  link(:social_twitter, :id => "soc_twitter")
  link(:social_facebook, :id => "soc_fb")
  link(:social_pinterest, :id => "soc_pin")
  link(:social_google_plus, :id => "soc_gplus")
  link(:social_youtube, :id => "soc_yt")

end

I'm trying to include the above in support/pages/activities_page.rb. When I attempt to load a cucumber feature that calls this, I get the following error:

/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/page-object-0.8.6.1/lib/page-object/accessors.rb:1110:in `block (2 levels) in <module:Accessors>'
/Users/user_name/svn/watir/cukes/client/features/support/common_links.rb:12:in `<module:CommonLinks>'
/Users/user_name/svn/watir/cukes/client/features/support/common_links.rb:1:in `<top (required)>'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/lib/cucumber/rb_support/rb_language.rb:129:in `load'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/lib/cucumber/rb_support/rb_language.rb:129:in `load_code_file'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/lib/cucumber/runtime/support_code.rb:171:in `load_file'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/lib/cucumber/runtime/support_code.rb:83:in `block in load_files!'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/lib/cucumber/runtime/support_code.rb:82:in `each'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/lib/cucumber/runtime/support_code.rb:82:in `load_files!'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/lib/cucumber/runtime.rb:175:in `load_step_definitions'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/lib/cucumber/runtime.rb:40:in `run!'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/lib/cucumber/cli/main.rb:43:in `execute!'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/lib/cucumber/cli/main.rb:20:in `execute'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/gems/cucumber-1.2.1/bin/cucumber:14:in `<top (required)>'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/bin/cucumber:23:in `load'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/bin/cucumber:23:in `<main>'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/bin/ruby_noexec_wrapper:14:in `eval'
/Users/user_name/.rvm/gems/ruby-2.0.0-p0/bin/ruby_noexec_wrapper:14:in `<main>'

I have also attempted to create a 'CommonPage' class, and inherit from that, but I'm getting an unitialized constant error. The CommonPage class is identical to the module.

class CommonPage
  include PageObject
  #Header links    
  link(:log_out_header_link, :text => "Log Out")
  link(:logo, :id => "logo")

  #Logged in nav links
  div(:nav, :id => "globalnav_wrapper")
  nav.link(:nav_activities, :href => "/activities/")


  # Footer Links
  link(:contact_us_footer, :text => "CONTACT US")

  # Social Media Links
  link(:social_twitter, :id => "soc_twitter")
  link(:social_facebook, :id => "soc_fb")
  link(:social_pinterest, :id => "soc_pin")
  link(:social_google_plus, :id => "soc_gplus")
  link(:social_youtube, :id => "soc_yt")

end

Attempting to use either of these approaches has not resulted in success.

Has anyone had success declaring common links (like header/footer) in one file/module/class and then using them from another class?

Any direction will be appreciated.

Upvotes: 3

Views: 1267

Answers (2)

Chuck van der Linden
Chuck van der Linden

Reputation: 6660

This is a standard feature of the Test-Factory gem. It's designed for supporting page objects and data objects with Watir-webdriver. It's a bit more streamlined since there is no support for selenium. I'm using it with the cucumber tests I'm creating for climate.com

Here's an example ripped from my code (shared with permission)

base_page.rb

class BasePage < PageFactory

  class << self

    def header_elements
      element(:logo) { |b| b.link(:id => "logo") }
      element(:session_links) { |b| b.ul(:id => "session-links")}
      element(:logout_link) { |p| p.session_links.link(:text => "Log Out") }
      element(:login_link) { |p| p.session_links.link(:text => "Login") }
      value(:current_user) { |p| p.session_links.li.text }

      action(:login) { |p| p.login_link.click }
      action(:logout) { |p| p.logout_link.click }
    end

    def footer_elements
      #todo add all the footer elements once we have tests that need them
      #climate_dot_com_link
      #about_twi_link
      #about_tcc_link
      #legal_tou_link
    end

    def grower_nav
      element (:grower_appnav) {|b|b.div(:class => "appnav-container")}

      element (:dashboard_link) { |p|p.grower_appnav.link(:text => /Dashboard/)}
      action (:dashboard) { |p|p.dashboard_link.click}
      element (:field_weather_link) { |p|p.grower_appnav.link(:text => /Field Weather/)}
      action (:field_weather) { |p|p.field_weather_link.click}
      element (:forecast_link) { |p|p.grower_appnav.link(:text => /Forecast/)}
      action (:forecast) { |p|p.forecast_link.click}
      element (:toolbox_link) { |p|p.grower_appnav.link(:text => /Toolbox/)}
      action (:toolbox) { |p|p.toolbox_link.click}
      element (:crop_impact_link) { |p|p.grower_appnav.link(:text => /Crop Impact/)}
      action (:crop_impact) { |p|p.crop_impact_link.click}
      element (:policies_link) { |p|p.grower_appnav.link(:text => /Policies/)}
      action (:policies) { |p|p.policies_link.click}
      element (:settings_link) { |p|p.grower_appnav.link(:text => /Settings/)}
      action (:settings) { |p|p.settings_link.click}
      element (:help_link) { |p|p.grower_appnav.link(:text => /Help/)}
      action (:help) { |p|p.help_link.click}

    end

    def agent_nav
      element (:tcc_appnav) { |b| b.div(:id => "tcc_appnav")}

      element (:home_link) { |p|p.tcc_appnav.link(:text => /Home/)}
      action (:home) { |p|p.home_link.click}
      element (:clients_link) { |p| p.tcc_appnav.link(:text => /Clients/)}
      action (:clients) { |p| p.clients_link.click}
      element (:policies_link) { |p|p.tcc_appnav.link(:text => /Policies/)}
      action (:policies) { |p|p.policies_link.click}
      element (:quotes_link) { |p|p.tcc_appnav.link(:text => /Quotes/)}
      action (:quotes) { |p|p.quotes_link.click}
      element (:sales_tools_link) { |p|p.tcc_appnav.link(:text => /Sales Tools/)}
      action (:sales_tools) { |p|p.sales_link.click}
      element (:marketing_hub_link) { |p|p.tcc_appnav.link(:text => /Marketing Hub/)}
      action (:marketing_hub) { |p|p.marketing_hub.click}
      element (:agents_link) { |p|p.tcc_appnav.link(:text => /Agents/)}
      action (:agents) { |p|p.agents_link.click}
      element (:policy_docs_link) { |p|p.tcc_appnav.link(:text => /Policy Docs/)}
      action (:policy_docs) { |p|p.policy_docs.click}
      element (:users_link) { |p|p.tcc_appnav.link(:text => /Users/)}
      action (:users) { |p|p.users_link.click}
      element (:licenses_link) { |p|p.tcc_appnav.link(:text => /Licenses/)}
      action (:licenses) { |p|p.licenses_link.click}

    end

    def notifications
      element (:agent_notification_bar) { |b|b.div(:class => "agent-notification-bar")}
      value (:agent_notification_header) { |p|p.agent_notification_bar.h4.text}
      value (:agent_notification_message) { |p|p.agent_notification_bar.text}
      action (:return_to_client_details) { |p|p.agent_notification_bar.link(:text => "Return to Client Details").click}

      element (:modal_dialog) { |b|b.div(:class => "modal-inner-wrapper")}
      value (:modal_header) { |p|p.modal_dialog.div(:class => "modal-header").text}
      value (:modal_body) { |p|p.modal_dialog.div(:class => "modal-body").text}
    end
  end
end

register.rb a self registration page, has footer elements, but not header or nav

class Register < BasePage

  page_url "#{$test_site}/preso/register.html"

  footer_elements

  element(:first_name) { |b|b.text_field(:id => "first_name")}
  element(:last_name) { |b|b.text_field(:id => "last_name")}
  element(:email) { |b|b.text_field(:id => "email")}
  element(:phone) { |b|b.text_field(:id => "phone")}
  element(:zip_code) { |b|b.text_field(:id => "zip_code")}
  element(:password) { |b|b.text_field(:id => "self_reg_password")}
  element(:confirm_password) { |b|b.text_field(:id => "self_reg_confirm_password")}
  element(:has_agent) { |b|b.select(:id => "has_agent")}
  element(:agents_name) { |b|b.text_field(:id => "source")}
  element(:contact_me) { |b|b.checkbox(:id => "contact_me")}
  element(:agree_to_terms) { |b|b.checkbox(:id => "terms")}
  button ("Create Account")
  element(:spinner) { |b|b.div(:class => "spinner")}
  action(:wait_on_spinner) { |p|p.spinner.wait_while_present}

  element(:agree_to_terms_required_message) {|b|b.div(:class => "terms-error")}

end 

agent_clients.rb a page accessable to 'agent' class users

class AgentClients < BasePage

  page_url "#{$test_site}/apps/admin/named_insureds?q[enabled_eq]=true"

  header_elements
  footer_elements
  agent_nav

  expected_element :session_links

  element (:client_list_table) { |b|b.table(:id => "named_insureds")}

end

dashboard.rb a page accessable to 'grower' class users (different nav)

class Dashboard < BasePage

  page_url "#{$test_site}/apps/preso/growerapps/dashboard.html"

  header_elements
  footer_elements
  grower_nav
  notifications

  expected_element  :session_links

  element(:sort_by_precip) { |b| b.button(:text => "Precip 7d") }
  element(:sort_by_soil_moiosture) { |b| b.button(:text => "Soil Moisture") }
  element(:sort_by_growth_stage) { |b| b.button(:text => "Growth Stage") }

  element(:fields_list) { |b|b.divs(:class => "field-group-card")}


end  

Upvotes: 1

Justin Ko
Justin Ko

Reputation: 46836

It is possible to inherit from a parent class or include a module.

For example, I did the following test where the page object inherited from a parent class. It ran as expected.

test.feature

Feature: Search

    Scenario: Set google search field
    When google search field set

support\pages.rb

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

class CommonPage
    include PageObject

    text_field(:search, :name => 'q')
end

class PageA < CommonPage

end

steps\steps.rb

When /google search field set/ do
    b = Watir::Browser.new
    b.goto 'www.google.ca'
    page = PageA.new(b)
    page.search = 'test'    
end

Similar worked when including CommonPage as a module in PageA.

Possible Issues

The exceptions do not match what I would expect, but it is possible it is due to the line:

nav.link(:nav_activities, :href => "/activities/")

As far as I know and have tried, this will not work. nav will be undefined. I think you want to do (note that I think you also want a regex instead of a string):

link(:nav_activities){ search.link(:href => /activities/) }

Upvotes: 5

Related Questions