├── pageobject.gems ├── .ruby-gemset ├── .ruby-version ├── .coveralls.yml ├── features ├── html │ ├── nested_frame_1.html │ ├── sun.gif │ ├── movie.mp4 │ ├── movie.ogg │ ├── planets.gif │ ├── images │ │ ├── circle.png │ │ ├── submit.gif │ │ └── img_pulpit.jpg │ ├── 04-Death_Becomes_Fur.mp4 │ ├── 04-Death_Becomes_Fur.oga │ ├── failure.html │ ├── success.html │ ├── sun.html │ ├── hover.html │ ├── nested_frame_2.html │ ├── nested_frames.html │ ├── double_click.html │ ├── nested_frame_3.html │ ├── widgets.html │ ├── modal.html │ ├── modal_2.html │ ├── iframes.html │ ├── frames.html │ ├── modal_1.html │ ├── frame_3.html │ ├── frame_2.html │ ├── frame_1.html │ ├── async.html │ ├── nested_elements.html │ └── indexed_property.html ├── support │ ├── hooks.rb │ ├── targets │ │ ├── firefox14_osx.rb │ │ └── firefox14_windows7.rb │ ├── env.rb │ ├── ajax_text_environment.rb │ ├── audio_video_page.rb │ ├── url_helper.rb │ └── persistent_browser.rb ├── step_definitions │ ├── populate_page_with_steps.rb │ ├── page_traversal_steps.rb │ ├── headings_steps.rb │ ├── bold_steps.rb │ ├── italic_steps.rb │ ├── table_cell_steps.rb │ ├── area_steps.rb │ ├── canvas_steps.rb │ ├── form_steps.rb │ ├── div_steps.rb │ ├── span_steps.rb │ ├── label_steps.rb │ ├── list_item_steps.rb │ ├── file_field_steps.rb │ ├── table_row_steps.rb │ ├── ordered_list_steps.rb │ ├── generic_element_steps.rb │ ├── unordered_list_steps.rb │ ├── radio_button_steps.rb │ ├── audio_steps.rb │ ├── paragraph_steps.rb │ ├── hidden_field_steps.rb │ ├── check_box_steps.rb │ ├── text_area_steps.rb │ ├── link_steps.rb │ ├── text_field_steps.rb │ ├── modal_dialog_steps.rb │ ├── button_steps.rb │ ├── radio_button_group_steps.rb │ ├── image_steps.rb │ ├── video_steps.rb │ ├── gxt_table_steps.rb │ ├── javascript_steps.rb │ ├── accessor_steps.rb │ ├── async_steps.rb │ ├── select_list_steps.rb │ ├── table_steps.rb │ └── page_level_actions_steps.rb ├── modal_dialog.feature ├── bold.feature ├── italic.feature ├── sample-app │ ├── public │ │ ├── audio_video.html │ │ ├── jquery.html │ │ └── prototype.html │ └── sample_app.rb ├── async.feature ├── gxt_table_extension.feature ├── file_field.feature ├── area.feature ├── form.feature ├── span.feature ├── list_item.feature ├── generic_elements.feature ├── canvas.feature ├── paragraph.feature ├── populate_page_with.feature ├── javascript.feature ├── hidden_field.feature ├── table_cell.feature ├── div.feature ├── radio_button_group.feature ├── table_row.feature ├── label.feature ├── text_area.feature ├── check_box.feature ├── image.feature ├── ordered_list.feature ├── unordered_list.feature ├── radio_button.feature ├── link.feature ├── text_field.feature ├── audio.feature ├── video.feature ├── headings.feature ├── button.feature ├── select_list.feature ├── page_level_actions.feature └── frames.feature ├── .rspec ├── lib └── page-object │ ├── version.rb │ ├── elements │ ├── div.rb │ ├── area.rb │ ├── bold.rb │ ├── form.rb │ ├── link.rb │ ├── media.rb │ ├── span.rb │ ├── audio.rb │ ├── italic.rb │ ├── label.rb │ ├── video.rb │ ├── list_item.rb │ ├── option.rb │ ├── canvas.rb │ ├── image.rb │ ├── paragraph.rb │ ├── check_box.rb │ ├── file_field.rb │ ├── text_area.rb │ ├── date_field.rb │ ├── hidden_field.rb │ ├── radio_button.rb │ ├── table_cell.rb │ ├── text_field.rb │ ├── button.rb │ ├── heading.rb │ ├── ordered_list.rb │ ├── unordered_list.rb │ ├── select_list.rb │ ├── table_row.rb │ └── table.rb │ ├── javascript │ ├── jquery.rb │ ├── prototype.rb │ ├── angularjs.rb │ └── yui.rb │ ├── nested_elements.rb │ ├── section_collection.rb │ ├── element_locators.rb │ ├── indexed_properties.rb │ ├── platforms │ └── watir.rb │ ├── elements.rb │ ├── javascript_framework_facade.rb │ ├── page_populator.rb │ ├── locator_generator.rb │ ├── widgets.rb │ └── page_factory.rb ├── .gitignore ├── cucumber.yml ├── .travis.yml ├── Gemfile ├── spec ├── page-object │ ├── elements │ │ ├── span_spec.rb │ │ ├── div_spec.rb │ │ ├── bold_spec.rb │ │ ├── media_spec.rb │ │ ├── italic_spec.rb │ │ ├── label_spec.rb │ │ ├── file_field_spec.rb │ │ ├── paragraph_spec.rb │ │ ├── list_item_spec.rb │ │ ├── text_area_spec.rb │ │ ├── hidden_field_spec.rb │ │ ├── link_spec.rb │ │ ├── table_cell_spec.rb │ │ ├── check_box_spec.rb │ │ ├── form_spec.rb │ │ ├── option_spec.rb │ │ ├── button_spec.rb │ │ ├── image_spec.rb │ │ ├── heading_spec.rb │ │ ├── unordered_list_spec.rb │ │ ├── table_row_spec.rb │ │ ├── table_spec.rb │ │ ├── ordered_list_spec.rb │ │ ├── select_list_spec.rb │ │ └── nested_element_spec.rb │ ├── platforms │ │ └── watir │ │ │ └── watir_page_object_spec.rb │ ├── accessors_spec.rb │ ├── javascript_framework_facade_spec.rb │ └── page_section_spec.rb └── spec_helper.rb ├── Rakefile ├── Guardfile ├── LICENSE └── page-object.gemspec /pageobject.gems: -------------------------------------------------------------------------------- 1 | bundler -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | page-object 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.4.6 2 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | -------------------------------------------------------------------------------- /features/html/nested_frame_1.html: -------------------------------------------------------------------------------- 1 | frame 1 -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /features/html/sun.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheezy/page-object/HEAD/features/html/sun.gif -------------------------------------------------------------------------------- /lib/page-object/version.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | # @private 3 | VERSION = "2.3.1" 4 | end 5 | -------------------------------------------------------------------------------- /features/html/movie.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheezy/page-object/HEAD/features/html/movie.mp4 -------------------------------------------------------------------------------- /features/html/movie.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheezy/page-object/HEAD/features/html/movie.ogg -------------------------------------------------------------------------------- /features/html/planets.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheezy/page-object/HEAD/features/html/planets.gif -------------------------------------------------------------------------------- /features/support/hooks.rb: -------------------------------------------------------------------------------- 1 | Before do 2 | @browser = PageObject::PersistentBrowser.get_browser 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | .bundle 3 | *.swp 4 | .idea/ 5 | pkg 6 | TAGS 7 | chromedriver.log 8 | coverage 9 | -------------------------------------------------------------------------------- /features/html/images/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheezy/page-object/HEAD/features/html/images/circle.png -------------------------------------------------------------------------------- /features/html/images/submit.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheezy/page-object/HEAD/features/html/images/submit.gif -------------------------------------------------------------------------------- /features/html/images/img_pulpit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheezy/page-object/HEAD/features/html/images/img_pulpit.jpg -------------------------------------------------------------------------------- /features/html/04-Death_Becomes_Fur.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheezy/page-object/HEAD/features/html/04-Death_Becomes_Fur.mp4 -------------------------------------------------------------------------------- /features/html/04-Death_Becomes_Fur.oga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cheezy/page-object/HEAD/features/html/04-Death_Becomes_Fur.oga -------------------------------------------------------------------------------- /features/html/failure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Failure 4 | 5 | 6 |

Failure

7 | 8 | -------------------------------------------------------------------------------- /features/html/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Success 4 | 5 | 6 |

Success

7 | 8 | -------------------------------------------------------------------------------- /features/html/sun.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

6 | 7 | 8 | -------------------------------------------------------------------------------- /features/step_definitions/populate_page_with_steps.rb: -------------------------------------------------------------------------------- 1 | When(/^I populate the page with the data:$/) do |table| 2 | @page.populate_page_with table.rows_hash 3 | end 4 | -------------------------------------------------------------------------------- /features/support/targets/firefox14_osx.rb: -------------------------------------------------------------------------------- 1 | module Target 2 | def desired_capabilities 3 | capabilities(:firefox, '14', 'Mac 10.6', 'Testing page-object with Firefox 14 on OS X') 4 | end 5 | end 6 | 7 | -------------------------------------------------------------------------------- /features/support/targets/firefox14_windows7.rb: -------------------------------------------------------------------------------- 1 | module Target 2 | def desired_capabilities 3 | capabilities(:firefox, '14', 'Windows 2008', 'Testing page-object with Firefox 14 on Windows 7') 4 | end 5 | end 6 | 7 | -------------------------------------------------------------------------------- /lib/page-object/elements/div.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Div < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:div] = ::PageObject::Elements::Div 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/area.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Area < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:area] = ::PageObject::Elements::Area 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/bold.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Bold < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:b] = ::PageObject::Elements::Bold 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/form.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Form < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:form] = ::PageObject::Elements::Form 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/link.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Link < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:a] = ::PageObject::Elements::Link 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/media.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Media < Element 4 | 5 | def has_controls? 6 | attribute(:controls) 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/page-object/elements/span.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Span < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:span] = ::PageObject::Elements::Span 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/audio.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Audio < Media 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:audio] = ::PageObject::Elements::Audio 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/italic.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Italic < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:i] = ::PageObject::Elements::Italic 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/label.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Label < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:label] = ::PageObject::Elements::Label 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/video.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Video < Media 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:video] = ::PageObject::Elements::Video 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/list_item.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class ListItem < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:li] = ::PageObject::Elements::ListItem 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/option.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Option < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:option] = ::PageObject::Elements::Option 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/canvas.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Canvas < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:canvas] = ::PageObject::Elements::Canvas 8 | end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /lib/page-object/elements/image.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Image < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:img] = ::PageObject::Elements::Image 8 | 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/page-object/elements/paragraph.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Paragraph < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:p] = ::PageObject::Elements::Paragraph 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/check_box.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class CheckBox < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:checkbox] = ::PageObject::Elements::CheckBox 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/file_field.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class FileField < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:file] = ::PageObject::Elements::FileField 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/text_area.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class TextArea < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:textarea] = ::PageObject::Elements::TextArea 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/date_field.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class DateField < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:date] = ::PageObject::Elements::DateField 8 | end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /lib/page-object/elements/hidden_field.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class HiddenField < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:hidden] = ::PageObject::Elements::HiddenField 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/page-object/elements/radio_button.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class RadioButton < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:radio] = ::PageObject::Elements::RadioButton 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /features/html/hover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | hello 11 | 12 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '../../', 'lib')) 2 | 3 | require 'rspec' 4 | require 'watir' 5 | require 'webdrivers' 6 | require 'selenium-webdriver' 7 | require 'page-object' 8 | 9 | World(PageObject::PageFactory) 10 | -------------------------------------------------------------------------------- /cucumber.yml: -------------------------------------------------------------------------------- 1 | <% 2 | std_opts = "--no-source --color --format pretty" # Cucumber::Formatter::Fuubar" 3 | %> 4 | 5 | default: DRIVER=WATIR <%= std_opts %> 6 | selenium: DRIVER=SELENIUM <%= std_opts %> 7 | focus: DRIVER=WATIR <%= std_opts %> --tags ~@selenium_only --tags @focus 8 | 9 | -------------------------------------------------------------------------------- /features/step_definitions/page_traversal_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^I am on the static elements page$/ do 2 | @page = Page.new(@browser) 3 | @page.navigate_to(UrlHelper.static_elements) 4 | end 5 | 6 | Given /^I am on the audio video page$/ do 7 | @avpage = visit AudioVideoPage 8 | end 9 | -------------------------------------------------------------------------------- /features/html/nested_frame_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

8 | 9 |

10 | 11 | 12 | -------------------------------------------------------------------------------- /features/html/nested_frames.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/page-object/javascript/jquery.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Javascript 3 | 4 | module JQuery 5 | # 6 | # return the number of pending ajax requests 7 | # 8 | def self.pending_requests 9 | 'return jQuery.active' 10 | end 11 | end 12 | 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/page-object/elements/table_cell.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class TableCell < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:td] = ::PageObject::Elements::TableCell 8 | ::PageObject::Elements.tag_to_class[:th] = ::PageObject::Elements::TableCell 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/page-object/elements/text_field.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class TextField < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:text] = ::PageObject::Elements::TextField 8 | ::PageObject::Elements.type_to_class[:password] = ::PageObject::Elements::TextField 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/page-object/javascript/prototype.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Javascript 3 | 4 | module Prototype 5 | # 6 | # return the number of pending ajax requests 7 | # 8 | def self.pending_requests 9 | 'return Ajax.activeRequestCount' 10 | end 11 | end 12 | 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | rvm: 4 | - 2.4.6 5 | - 2.5.5 6 | - 2.6.3 7 | cache: bundler 8 | addons: 9 | chrome: stable 10 | before_script: 11 | - export DISPLAY=:99.0 12 | - sh -e /etc/init.d/xvfb start 13 | 14 | script: bundle exec rake $RAKE_TASK 15 | env: 16 | - RAKE_TASK=spec 17 | - RAKE_TASK=features 18 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # adding rake so travis-ci will build properly 4 | gem 'rake' 5 | gem 'rb-fsevent', :require => false if RUBY_PLATFORM =~ /darwin/i 6 | gem 'growl' 7 | gem 'guard-rspec' 8 | gem 'listen', '3.0.8' #Last version that supports ruby 2.0 9 | gem 'guard-cucumber' 10 | gem 'coveralls', require: false 11 | 12 | 13 | gemspec 14 | -------------------------------------------------------------------------------- /spec/page-object/elements/span_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Span do 5 | let(:span) { PageObject::Elements::Span } 6 | 7 | it "should register with tag_name :span" do 8 | expect(::PageObject::Elements.element_class_for(:span)).to eql ::PageObject::Elements::Span 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/page-object/javascript/angularjs.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Javascript 3 | 4 | module AngularJS 5 | # 6 | # return the number of pending ajax requests 7 | # 8 | def self.pending_requests 9 | 'return angular.element(document.body).injector().get(\'$http\').pendingRequests.length;' 10 | end 11 | end 12 | 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /features/step_definitions/headings_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the text for the "([^\"]*)" element$/ do |el| 2 | @heading = @page.send "#{el}_id" 3 | end 4 | 5 | Then /^I should see "([^\"]*)"$/ do |text| 6 | expect(@heading).to eql text 7 | end 8 | 9 | When /^I search for the heading(\d+) by "([^"]*)"$/ do |head_type, type| 10 | @heading = @page.send "h#{head_type}_#{type}" 11 | end 12 | 13 | -------------------------------------------------------------------------------- /features/step_definitions/bold_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the bold text for the "([^\"]*)" element$/ do |el| 2 | @b = @page.send "#{el}_id" 3 | end 4 | 5 | Then /^I should see "([^\"]*)" in bold$/ do |text| 6 | expect(@b).to eql text 7 | end 8 | 9 | When /^I search bold text for the (\w+) by "([^"]*)"$/ do |text_decorator, type| 10 | @b = @page.send "#{text_decorator}_#{type}" 11 | end 12 | 13 | -------------------------------------------------------------------------------- /lib/page-object/nested_elements.rb: -------------------------------------------------------------------------------- 1 | require 'page-object/locator_generator' 2 | 3 | module PageObject 4 | module NestedElements 5 | 6 | def self.included(cls) 7 | ::PageObject::LocatorGenerator.generate_locators(cls) 8 | end 9 | 10 | private 11 | 12 | def locator(identifier) 13 | identifier[0] ? identifier[0] : {:index => 0} 14 | end 15 | 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/page-object/elements/div_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Div do 5 | let(:div) { PageObject::Elements::Div } 6 | 7 | describe "interface" do 8 | it "should register with tag :div" do 9 | expect(::PageObject::Elements.element_class_for(:div)).to eql ::PageObject::Elements::Div 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /features/html/double_click.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

The double click has not occurred.

5 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /features/step_definitions/italic_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the italic text for the "([^\"]*)" element$/ do |el| 2 | @i = @page.send "#{el}_id" 3 | end 4 | 5 | Then /^I should see "([^\"]*)" in italic$/ do |text| 6 | expect(@i).to eql text 7 | end 8 | 9 | When /^I search italic text for the (\w+) by "([^"]*)"$/ do |text_decorator, type| 10 | @i = @page.send "#{text_decorator}_#{type}" 11 | end 12 | 13 | -------------------------------------------------------------------------------- /spec/page-object/elements/bold_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Bold do 5 | let(:bold) { PageObject::Elements::Bold } 6 | 7 | describe "interface" do 8 | 9 | it "should register with tag :b" do 10 | expect(::PageObject::Elements.element_class_for(:b)).to eql ::PageObject::Elements::Bold 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/page-object/elements/media_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Media do 5 | 6 | let(:media) { PageObject::Elements::Media.new(double('')) } 7 | 8 | it "should return controls" do 9 | expect(media).to receive(:attribute).with(:controls).and_return(true) 10 | expect(media.has_controls?).to eq(true) 11 | end 12 | 13 | end -------------------------------------------------------------------------------- /spec/page-object/elements/italic_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Italic do 5 | let(:italic) { PageObject::Elements::Italic } 6 | 7 | describe "interface" do 8 | 9 | it "should register with tag :i" do 10 | expect(::PageObject::Elements.element_class_for(:i)).to eql ::PageObject::Elements::Italic 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /features/html/nested_frame_3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Title 7 | 8 | 9 | 10 |

11 | this link should open the page success page 12 |

13 | 14 | 15 | -------------------------------------------------------------------------------- /features/html/widgets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
Column1Column2
12
34
18 |
19 | 20 | -------------------------------------------------------------------------------- /spec/page-object/elements/label_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Label do 5 | let(:label) { PageObject::Elements::Label } 6 | 7 | describe "interface" do 8 | it "should register with tag :label" do 9 | expect(::PageObject::Elements.element_class_for(:label)).to eql ::PageObject::Elements::Label 10 | end 11 | end 12 | end 13 | 14 | -------------------------------------------------------------------------------- /spec/page-object/elements/file_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::FileField do 5 | 6 | describe "interface" do 7 | let(:filefield) { double('file_field') } 8 | 9 | it "should register as type :file" do 10 | expect(::PageObject::Elements.element_class_for(:input, :file)).to eql ::PageObject::Elements::FileField 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/page-object/elements/paragraph_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Paragraph do 5 | let(:paragraph) { PageObject::Elements::Paragraph } 6 | 7 | describe "interface" do 8 | it "should register with type :checkbox" do 9 | expect(::PageObject::Elements.element_class_for(:p)).to eql ::PageObject::Elements::Paragraph 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/page-object/elements/list_item_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::ListItem do 5 | let(:list_item) { PageObject::Elements::ListItem } 6 | 7 | describe "interface" do 8 | it "should register as tag_name :li" do 9 | expect(::PageObject::Elements.element_class_for(:li)).to eql ::PageObject::Elements::ListItem 10 | end 11 | 12 | end 13 | end 14 | 15 | -------------------------------------------------------------------------------- /spec/page-object/elements/text_area_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::TextArea do 5 | let(:textarea) { PageObject::Elements::TextArea } 6 | 7 | describe "interface" do 8 | it "should register with tag_name :textarea" do 9 | expect(::PageObject::Elements.element_class_for(:textarea)).to eql ::PageObject::Elements::TextArea 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/page-object/elements/hidden_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::HiddenField do 5 | let(:hiddenfield) { PageObject::Elements::HiddenField } 6 | 7 | describe "interface" do 8 | it "should register with type :hidden" do 9 | expect(::PageObject::Elements.element_class_for(:input, :hidden)).to eql ::PageObject::Elements::HiddenField 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /features/modal_dialog.feature: -------------------------------------------------------------------------------- 1 | Feature: handing modal dialogs 2 | 3 | Background: 4 | Given I am on the modal page 5 | 6 | 7 | Scenario: Interacting with a modal dialog 8 | When I open a modal dialog 9 | Then I should be able to close the modal 10 | 11 | Scenario: Nested modal dialogs 12 | When I open a modal dialog 13 | And I open another modal dialog from that one 14 | #Then I should be able to close both modals 15 | 16 | -------------------------------------------------------------------------------- /spec/page-object/elements/link_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Link do 5 | let(:link) { PageObject::Elements::Link } 6 | 7 | describe "interface" do 8 | let(:link_element) { double('link_element') } 9 | 10 | it "should register with tag :a" do 11 | expect(::PageObject::Elements.element_class_for(:a)).to eql ::PageObject::Elements::Link 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/page-object/elements/button.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Button < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.type_to_class[:submit] = ::PageObject::Elements::Button 8 | ::PageObject::Elements.type_to_class[:image] = ::PageObject::Elements::Button 9 | ::PageObject::Elements.type_to_class[:button] = ::PageObject::Elements::Button 10 | ::PageObject::Elements.type_to_class[:reset] = ::PageObject::Elements::Button 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/page-object/section_collection.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | class SectionCollection < Array 3 | def find_by(values_hash) 4 | find do |section| 5 | values_hash.all? { |key, value| value === section.public_send(key) } 6 | end 7 | end 8 | 9 | def select_by(values_hash) 10 | matches = select do |section| 11 | values_hash.all? { |key, value| value === section.public_send(key) } 12 | end 13 | self.class[*matches] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/page-object/javascript/yui.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Javascript 3 | 4 | module YUI 5 | # 6 | # return the number of pending ajax requests 7 | # 8 | def self.pending_requests 9 | "var inProgress=0 10 | for(var i=0; i < YAHOO.util.Connect._transaction_id; i++) { 11 | if(YAHOO.util.Connect.isCallInProgress(i)) 12 | inProgress++; 13 | } 14 | return inProgress;" 15 | end 16 | end 17 | 18 | end 19 | end -------------------------------------------------------------------------------- /lib/page-object/element_locators.rb: -------------------------------------------------------------------------------- 1 | require 'page-object/locator_generator' 2 | 3 | module PageObject 4 | module ElementLocators 5 | 6 | def self.included(cls) 7 | ::PageObject::LocatorGenerator.generate_locators(cls) 8 | end 9 | 10 | def element(tag, identifier={:index => 0}) 11 | platform.element_for(tag, identifier.clone) 12 | end 13 | 14 | private 15 | 16 | def locator(identifier) 17 | identifier[0] ? identifier[0] : {:index => 0} 18 | end 19 | 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /features/html/modal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | modal dialog test page 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /spec/page-object/elements/table_cell_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::TableCell do 5 | 6 | context "interface" do 7 | it "should register with tag_name :td" do 8 | expect(::PageObject::Elements.element_class_for(:td)).to eql ::PageObject::Elements::TableCell 9 | end 10 | 11 | it "should register with tag_name :th" do 12 | expect(::PageObject::Elements.element_class_for(:th)).to eql ::PageObject::Elements::TableCell 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/page-object/platforms/watir/watir_page_object_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe PageObject::Platforms::Watir do 4 | describe "is for?" do 5 | it "should be true when the browser is Watir::Browser" do 6 | browser = mock_watir_browser 7 | expect(PageObject::Platforms::Watir.is_for?(browser)).to be true 8 | end 9 | 10 | it "should be false at any other point" do 11 | browser = 'asdf' 12 | expect(PageObject::Platforms::Watir.is_for?('asdf')).to be false 13 | end 14 | end 15 | end 16 | 17 | -------------------------------------------------------------------------------- /features/step_definitions/table_cell_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I search for the table cell by "([^\"]*)"$/ do |how| 2 | @cell_data = @page.send "cell_#{how}" 3 | end 4 | 5 | When /^I retrieve a table cell element by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 6 | @cell_data = @page.send "cell_#{param1}_#{param2}" 7 | end 8 | 9 | When /^I retrieve a table cell element while the script is executing$/ do 10 | @cell_data = @page.cell_element(:id => 'cell_id').text 11 | end 12 | 13 | Then /^I should see that the cell exists$/ do 14 | expect(@page.cell_id?).to be true 15 | end -------------------------------------------------------------------------------- /spec/page-object/elements/check_box_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::CheckBox do 5 | let(:checkbox) { PageObject::Elements::CheckBox } 6 | 7 | describe "interface" do 8 | let(:check_box) { double('check_box') } 9 | let(:selenium_cb) { PageObject::Elements::CheckBox.new(check_box) } 10 | 11 | it "should register with type :checkbox" do 12 | expect(::PageObject::Elements.element_class_for(:input, :checkbox)).to eql ::PageObject::Elements::CheckBox 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /features/step_definitions/area_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I search for the area by "([^\"]*)"$/ do |how| 2 | @how = how 3 | end 4 | 5 | Then /^I should be able to click the area$/ do 6 | @page.send("area_#{@how}") 7 | end 8 | 9 | Then /^I should see the coordinates are "([^\"]*)"$/ do |coords| 10 | expect(@element.coords).to eql coords 11 | end 12 | 13 | Then /^I should see the shape is "([^\"]*)"$/ do |shape| 14 | expect(@element.shape).to eql shape 15 | end 16 | 17 | Then /^I should see the href is "([^\"]*)"$/ do |href| 18 | expect(@element.href).to include href 19 | end 20 | -------------------------------------------------------------------------------- /features/bold.feature: -------------------------------------------------------------------------------- 1 | Feature: Bold 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Getting the text of headings 7 | When I get the bold text for the "b" element 8 | Then I should see "some text in bold" in bold 9 | 10 | Scenario Outline: Locating b on the Page 11 | When I search bold text for the b by "" 12 | Then I should see "some text in bold" in bold 13 | 14 | Scenarios: 15 | | search_by | 16 | | id | 17 | | class | 18 | | xpath | 19 | | index | 20 | | css | 21 | -------------------------------------------------------------------------------- /features/step_definitions/canvas_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I search for the canvas by "([^\"]*)"$/ do |how| 2 | @element = @page.send "canvas_#{how}_element" 3 | end 4 | 5 | Then /^I should see that the canvas width is "([^\"]*)"$/ do |width| 6 | expect(@element.width).to eql width.to_i 7 | end 8 | 9 | Then /^I should see that the canvas height is "([^\"]*)"$/ do |height| 10 | expect(@element.height).to eql height.to_i 11 | end 12 | 13 | When /^I search for the canvas element by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 14 | @element = @page.send "canvas_#{param1}_#{param2}_element" 15 | end 16 | -------------------------------------------------------------------------------- /features/italic.feature: -------------------------------------------------------------------------------- 1 | Feature: Italic 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Getting the text of headings 7 | When I get the italic text for the "i" element 8 | Then I should see "some text in italic" in italic 9 | 10 | Scenario Outline: Locating i on the Page 11 | When I search italic text for the i by "" 12 | Then I should see "some text in italic" in italic 13 | 14 | Scenarios: 15 | | search_by | 16 | | id | 17 | | class | 18 | | xpath | 19 | | index | 20 | | css | 21 | -------------------------------------------------------------------------------- /features/html/modal_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Modal 2 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 |

Modal 2

21 | 22 |

Close buttons

23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /features/html/iframes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Iframes 5 | 6 | 7 | 8 |

Iframes

9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /features/step_definitions/form_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^I should be able to submit the form$/ do 2 | @element.submit 3 | end 4 | 5 | When /^I locate the form by "([^\"]*)"$/ do |how| 6 | @element = @page.send "form_#{how}_element" 7 | end 8 | 9 | When /^I locate the form using "([^"]*)" and "([^"]*)"$/ do |param1, param2| 10 | @element = @page.send "form_#{param1}_#{param2}_element" 11 | end 12 | 13 | When /^I locate a form while the script is executing$/ do 14 | @element = @page.form_element(:id => 'form_id') 15 | end 16 | 17 | Then /^I should see that the form exists$/ do 18 | expect(@page.form_id?).to be true 19 | end -------------------------------------------------------------------------------- /features/step_definitions/div_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the text from the div$/ do 2 | @text = @page.div_id 3 | end 4 | 5 | When /^I search for the div by "([^\"]*)"$/ do |how| 6 | @text = @page.send "div_#{how}".to_sym 7 | end 8 | 9 | When /^I search for the div by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 10 | @text = @page.send "div_#{param1}_#{param2}".to_sym 11 | end 12 | 13 | When /^I get the text from a div while the script is executing$/ do 14 | @text = @page.div_element(:id => 'div_id').text 15 | end 16 | 17 | Then /^I should see that the div exists$/ do 18 | expect(@page.div_id?).to be true 19 | end 20 | -------------------------------------------------------------------------------- /features/html/frames.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frames 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /features/step_definitions/span_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the text from the span$/ do 2 | @text = @page.span_id 3 | end 4 | 5 | When /^I search for the span by "([^\"]*)"$/ do |how| 6 | @text = @page.send "span_#{how}".to_sym 7 | end 8 | 9 | When /^I search for the span by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 10 | @text = @page.send "span_#{param1}_#{param2}".to_sym 11 | end 12 | 13 | When /^I get the text from a span while the script is executing$/ do 14 | @text = @page.span_element(:id => 'span_id').text 15 | end 16 | 17 | Then /^I should see that the span exists$/ do 18 | expect(@page.span_id?).to eql true 19 | end 20 | -------------------------------------------------------------------------------- /features/step_definitions/label_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the text from the label$/ do 2 | @text = @page.label_id 3 | end 4 | 5 | When /^I search for the label by "([^\"]*)"$/ do |how| 6 | @text = @page.send "label_#{how}".to_sym 7 | end 8 | 9 | When /^I search for the label by "(.*)" and "(.*)"$/ do |param1, param2| 10 | @text = @page.send "label_#{param1}_#{param2}".to_sym 11 | end 12 | 13 | When /^I get the text from a label while the script is executing$/ do 14 | @text = @page.label_element(:id => 'label_id').text 15 | end 16 | 17 | Then /^I should see that the label exists$/ do 18 | expect(@page.label_id?).to be true 19 | end 20 | -------------------------------------------------------------------------------- /features/step_definitions/list_item_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the text from the list item$/ do 2 | @text = @page.li_id 3 | end 4 | 5 | When /^I search for the list item by "([^\"]*)"$/ do |how| 6 | @text = @page.send "li_#{how}" 7 | end 8 | 9 | When /^I search for the list item by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 10 | @text = @page.send "li_#{param1}_#{param2}" 11 | end 12 | 13 | When /^I search for the list item while the script is executing$/ do 14 | @text = @page.list_item_element(:id => 'li_id').text 15 | end 16 | 17 | Then /^I should see that the list item exists$/ do 18 | expect(@page.li_id?).to eql true 19 | end 20 | -------------------------------------------------------------------------------- /features/step_definitions/file_field_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I set the file field to the step definition file$/ do 2 | @page.file_field_id = __FILE__ 3 | end 4 | 5 | Then /^its\' value should equal that file$/ do 6 | expect(__FILE__).to include @page.file_field_id_element.value[/[^\\]*$/] 7 | end 8 | 9 | When /^I search for the file field by "([^\"]*)"$/ do |how| 10 | @how = how 11 | end 12 | 13 | When /^I search for the file field by "([^\"]*)" and "([^\"]*)"$/ do |param1, param2| 14 | @how = "#{param1}_#{param2}" 15 | end 16 | 17 | Then /^I should be able to set the file field$/ do 18 | @page.send "file_field_#{@how}=", __FILE__ 19 | end 20 | -------------------------------------------------------------------------------- /lib/page-object/elements/heading.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Heading < Element 4 | 5 | end 6 | 7 | ::PageObject::Elements.tag_to_class[:h1] = ::PageObject::Elements::Heading 8 | ::PageObject::Elements.tag_to_class[:h2] = ::PageObject::Elements::Heading 9 | ::PageObject::Elements.tag_to_class[:h3] = ::PageObject::Elements::Heading 10 | ::PageObject::Elements.tag_to_class[:h4] = ::PageObject::Elements::Heading 11 | ::PageObject::Elements.tag_to_class[:h5] = ::PageObject::Elements::Heading 12 | ::PageObject::Elements.tag_to_class[:h6] = ::PageObject::Elements::Heading 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /features/support/ajax_text_environment.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + "/../sample-app/sample_app" 2 | 3 | class AjaxTestEnvironment 4 | def run 5 | Thread.abort_on_exception = true 6 | @example_app = Thread.new { SampleApp.start("127.0.0.1", 4567) } 7 | poller = Selenium::WebDriver::SocketPoller.new("127.0.0.1", 4567, 60) 8 | unless poller.connected? 9 | raise "timed out waiting for SampleApp to launch" 10 | end 11 | 12 | self 13 | end 14 | 15 | def stop 16 | @example_app.kill 17 | end 18 | 19 | end 20 | 21 | @server = AjaxTestEnvironment.new 22 | @server.run 23 | 24 | at_exit do 25 | @server.stop 26 | end 27 | -------------------------------------------------------------------------------- /spec/page-object/elements/form_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Form do 5 | describe "interface" do 6 | let(:form_element) { double('form_element') } 7 | 8 | it "should register with tag_name :form" do 9 | expect(::PageObject::Elements.element_class_for(:form)).to eql ::PageObject::Elements::Form 10 | end 11 | 12 | context "for watir" do 13 | it "should submit a form" do 14 | form = PageObject::Elements::Form.new(form_element) 15 | expect(form_element).to receive(:submit) 16 | form.submit 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/page-object/elements/option_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Option do 5 | 6 | describe "interface" do 7 | it "should register as tag_name :option" do 8 | expect(::PageObject::Elements.element_class_for(:option)).to eql ::PageObject::Elements::Option 9 | end 10 | end 11 | 12 | describe "interacting with the option" do 13 | let(:wd) { double('') } 14 | let(:native) { double(wd: wd) } 15 | let(:element) { PageObject::Elements::Option.new(native) } 16 | 17 | it 'should know if it is selected' do 18 | expect(native).to receive(:selected?).and_return true 19 | expect(element.selected?).to be true 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /features/step_definitions/table_row_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I search for the table row by "([^\"]*)"$/ do |how| 2 | @row_data = @page.send "tr_#{how}" 3 | end 4 | 5 | When /^I retrieve a table row element by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 6 | @row_data = @page.send "tr_#{param1}_#{param2}" 7 | end 8 | 9 | When /^I retrieve a table row element while the script is executing$/ do 10 | @row_data = @page.row_element(:id => 'tr_id').text 11 | end 12 | 13 | Then /^I should see that the row exists$/ do 14 | expect(@page.tr_id?).to be true 15 | end 16 | 17 | When /^I retrieve the data from the table row/ do 18 | @row_data = @page.tr_id 19 | end 20 | 21 | Then /^the row data should be '([^"]*)'$/ do |expected| 22 | expect(@row_data).to eql expected 23 | end -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | require 'rspec/core/rake_task' 4 | require 'cucumber' 5 | require 'cucumber/rake/task' 6 | require 'coveralls/rake/task' 7 | 8 | Coveralls::RakeTask.new 9 | Bundler::GemHelper.install_tasks 10 | 11 | RSpec::Core::RakeTask.new(:spec) do |spec| 12 | spec.ruby_opts = "-I lib:spec" 13 | spec.pattern = 'spec/**/*_spec.rb' 14 | end 15 | task :spec 16 | 17 | Cucumber::Rake::Task.new(:features, "Run the cucumber features") 18 | 19 | 20 | desc 'Run all specs and cukes' 21 | task :test => ['spec', 'features'] 22 | 23 | task :lib do 24 | $LOAD_PATH.unshift(File.expand_path("lib", File.dirname(__FILE__))) 25 | end 26 | 27 | task :test_with_coveralls => [:test, 'coveralls:push'] 28 | 29 | task :default => :test_with_coveralls 30 | -------------------------------------------------------------------------------- /features/sample-app/public/audio_video.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Audio and Video 4 | 5 | 6 | 7 |

Audio & Video

8 | 15 | 16 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /features/async.feature: -------------------------------------------------------------------------------- 1 | Feature: Handling Asynch calls 2 | 3 | Background: 4 | Given I am on the async elements page 5 | 6 | Scenario: Click a button when it is visible 7 | When I make the button invisible 8 | Then I should be able to click it when it becomses visible 9 | 10 | Scenario: Wait until something is not visible 11 | Then I should be able to wait until the button becomes invisible 12 | 13 | Scenario: Wait for an element to appear on the page 14 | When I add a button a few seconds from now 15 | Then I should be able to click it when it gets added 16 | 17 | Scenario: Wait for an element to disappear from the page 18 | When I add a button a few seconds from now 19 | And I remove a button a few seconds from now 20 | Then I should not be able to find the button 21 | 22 | -------------------------------------------------------------------------------- /features/step_definitions/ordered_list_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the first item from the ordered list$/ do 2 | @element = @page.ol_id_element[0] 3 | end 4 | 5 | When /^I search for the ordered list by "([^\"]*)"$/ do |how| 6 | @list = @page.send "ol_#{how}_element" 7 | end 8 | 9 | When /^I search for the ordered list by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 10 | @list = @page.send "ol_#{param1}_#{param2}_element" 11 | end 12 | 13 | When /^I search for the ordered list while the script is executing$/ do 14 | @list = @page.ordered_list_element(:id => 'ol_id') 15 | end 16 | 17 | Then /^I should see that the ordered list exists$/ do 18 | expect(@page.ol_id?).to eql true 19 | end 20 | 21 | Then /^the text for the ordered list should contain "(.*)"$/ do |text| 22 | expect(@page.send("ol_id")).to include text 23 | end 24 | -------------------------------------------------------------------------------- /features/step_definitions/generic_element_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the text from the article$/ do 2 | @text = @page.article_id 3 | end 4 | 5 | When /^I get the text from the header$/ do 6 | @text = @page.header_id 7 | end 8 | 9 | When /^I get the text from the footer$/ do 10 | @text = @page.footer_id 11 | end 12 | 13 | When /^I get the text from the summary$/ do 14 | @text = @page.summary_id 15 | end 16 | 17 | When /^I get the text from the details$/ do 18 | @text = @page.details_id 19 | end 20 | 21 | When /^I get the svg element$/ do 22 | @svg = @page.svg_id_element 23 | end 24 | 25 | Then /^the svg width should be "(.*?)"$/ do |width| 26 | expect(@svg.attribute('width')).to eql width 27 | end 28 | 29 | Then /^the svg height should be "(.*?)"$/ do |height| 30 | expect(@svg.attribute('height')).to eql height 31 | end 32 | -------------------------------------------------------------------------------- /features/step_definitions/unordered_list_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the first item from the unordered list$/ do 2 | @element = @page.ul_id_element[0] 3 | end 4 | 5 | When /^I search for the unordered list by "([^\"]*)"$/ do |how| 6 | @list = @page.send "ul_#{how}_element" 7 | end 8 | 9 | When /^I search for the unordered list by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 10 | @list = @page.send "ul_#{param1}_#{param2}_element" 11 | end 12 | 13 | When /^I search for the unordered list while the script is executing$/ do 14 | @list = @page.unordered_list_element(:id => 'ul_id') 15 | end 16 | 17 | Then /^I should see that the unordered list exists$/ do 18 | expect(@page.ul_id?).to be true 19 | end 20 | 21 | Then /^the text for the unordered list should contain "(.*)"$/ do |text| 22 | expect(@page.send("ul_id")).to include text 23 | end 24 | -------------------------------------------------------------------------------- /features/step_definitions/radio_button_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select the "([^\"]*)" radio button$/ do |how| 2 | @page.send "select_#{how.downcase}_id".to_sym 3 | end 4 | 5 | Then /^the "([^\"]*)" radio button should be selected$/ do |how| 6 | @page.send "#{how.downcase}_id_selected?".to_sym 7 | end 8 | 9 | When /^I search for the radio button by "([^\"]*)"$/ do |how| 10 | @how = how 11 | end 12 | 13 | When /^I search for the radio button by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 14 | @how = "#{param1}_#{param2}" 15 | end 16 | 17 | When /^I select the radio button$/ do 18 | @page.send "select_milk_#{@how}".to_sym 19 | end 20 | 21 | When /^I select the radio button while the script is executing$/ do 22 | @page.radio_button_element(:id => 'milk_id').select 23 | end 24 | 25 | Then /^I should see that the radio button exists$/ do 26 | expect(@page.milk_id?).to eql true 27 | end -------------------------------------------------------------------------------- /features/step_definitions/audio_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I search for the audio element by "([^\"]*)"$/ do |how| 2 | @element = @avpage.send "audio_#{how}_element" 3 | end 4 | 5 | When /^I search for the audio element by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 6 | @element = @avpage.send "audio_#{param1}_#{param2}_element" 7 | end 8 | 9 | When /^I retrieve the audio element from the page$/ do 10 | @element = @avpage.audio_id_element 11 | end 12 | 13 | Then /^I should know the audio is not autoplay$/ do 14 | expect(@element).not_to be_autoplay 15 | end 16 | 17 | Then /^I should know that the controls are displayed$/ do 18 | expect(@element).to have_controls 19 | end 20 | 21 | Then /^I should know that the audio is paused$/ do 22 | expect(@element).to be_paused 23 | end 24 | 25 | Then /^I should know that its volume is (\d+)$/ do |volume| 26 | expect(@element.volume).to eql volume.to_f 27 | end 28 | -------------------------------------------------------------------------------- /features/support/audio_video_page.rb: -------------------------------------------------------------------------------- 1 | class AudioVideoPage 2 | include PageObject 3 | 4 | page_url "http://localhost:4567/audio_video.html" 5 | 6 | audio(:audio_id, :id => 'audio') 7 | audio(:audio_name, :name => 'audio') 8 | audio(:audio_class, :class => 'audio') 9 | audio(:audio_css, :css => '.audio') 10 | audio(:audio_index, :index => 0) 11 | audio(:audio_xpath, :xpath => '//audio') 12 | audio(:audio_class_index, :class => 'audio', :index => 0) 13 | audio(:audio_name_index, :name => 'audio', :index => 0) 14 | 15 | video(:video_id, :id => 'video') 16 | video(:video_name, :name => 'video') 17 | video(:video_class, :class => 'video') 18 | video(:video_css, :css => '.video') 19 | video(:video_index, :index => 0) 20 | video(:video_xpath, :xpath => '//video') 21 | video(:video_class_index, :class => 'video', :index => 0) 22 | video(:video_name_index, :name => 'video', :index => 0) 23 | 24 | end 25 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # A sample Guardfile 2 | # More info at https://github.com/guard/guard#readme 3 | 4 | 5 | guard :rspec, cmd: 'rspec --color --format documentation' do 6 | watch(%r{^spec/.+_spec\.rb$}) 7 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } 8 | watch(%r{^lib/page-object/platforms/(.+)/.+\.rb$}) do |m| 9 | ["spec/page-object/platforms/#{m[1]}/", "spec/page-object/page-object_spec.rb"] 10 | end 11 | watch('spec/spec_helper.rb') { "spec" } 12 | end 13 | 14 | guard 'cucumber', notification: true, all_after_pass: false, cli: '--profile focus' do 15 | watch(%r{^features/.+\.feature$}) 16 | watch(%r{^features/support/.+$}) { "features" } 17 | watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' } 18 | watch(%r{^lib/.+\.rb$}) { "features" } 19 | watch(%r{^cucumber.yml$}) { "features" } 20 | end 21 | -------------------------------------------------------------------------------- /features/gxt_table_extension.feature: -------------------------------------------------------------------------------- 1 | Feature: Gxt Table Extension 2 | As a Quality Engineer working on a Gxt or Gwt project 3 | In order to easily create test widgets to interact with application widgets 4 | I need to define, register and use widgets as if they were normal elements 5 | 6 | Background: 7 | Given I have defined a GxtTable class extending Table 8 | And I have registered the GxtTable with PageObject 9 | And I define a page-object using that widget 10 | And I am on the Gxt Examples page 11 | 12 | Scenario: Retrieve a GxtTable 13 | When I retrieve a GxtTable widget 14 | Then I should know it is visible 15 | 16 | Scenario: Determine if a GxtTable exists 17 | When I retrieve a GxtTable widget 18 | Then I should know it exists 19 | 20 | Scenario: Confirm a correct row count from a GxtTable 21 | When I retrieve a GxtTable widget 22 | Then the GxtTable should have "3" rows 23 | -------------------------------------------------------------------------------- /features/step_definitions/paragraph_steps.rb: -------------------------------------------------------------------------------- 1 | 2 | When /^I get the text from the paragraph$/ do 3 | @text = @page.p_id 4 | end 5 | 6 | When /^I search for the paragraph by "([^"]*)"$/ do |how| 7 | @text = @page.send "p_#{how}".to_sym 8 | end 9 | 10 | When /^I search for the paragraph by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 11 | @text = @page.send "p_#{param1}_#{param2}" 12 | end 13 | 14 | When /^I get the text from a paragraph while the script is executing$/ do 15 | @text = @page.paragraph_element(:id => 'p_id').text 16 | end 17 | 18 | Then /^I should see that the paragraph exists$/ do 19 | expect(@page.p_id?).to eql true 20 | end 21 | 22 | Then(/^I should know the paragraph class is "(.*?)"$/) do |class_name| 23 | expect(@page.p_id_element.class_name).to eql class_name 24 | end 25 | 26 | Then(/^I should be able to select "(.*?)" from the paragraph$/) do |text| 27 | @page.p_id_element.select_text text 28 | end 29 | -------------------------------------------------------------------------------- /features/sample-app/sample_app.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rack' 3 | require 'webrick' 4 | 5 | 6 | class SampleApp 7 | 8 | def self.start(host, port) 9 | Rack::Handler::WEBrick.run new, 10 | :Host => host, 11 | :Port => port, 12 | :Logger => ::WEBrick::Log.new(RUBY_PLATFORM =~ /mswin|mingw/ ? 'NUL:' : '/dev/null'), 13 | :AccessLog => [nil, nil] 14 | end 15 | 16 | def initialize 17 | @public = Rack::File.new(File.expand_path("../public", __FILE__)) 18 | end 19 | 20 | def call(env) 21 | req = Rack::Request.new(env) 22 | 23 | case req.path 24 | when "/" 25 | [200, {}, ["Sample Application"]] 26 | when "/compute" 27 | sleep 3 28 | resp = eval(req.params['calculator-expression']).to_s 29 | [200, {}, [resp]] 30 | else 31 | @public.call(env) 32 | end 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /features/file_field.feature: -------------------------------------------------------------------------------- 1 | Feature: File Field 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Setting the value on the file field 7 | When I set the file field to the step definition file 8 | Then its' value should equal that file 9 | 10 | Scenario Outline: Locating file fields on the Page 11 | When I search for the file field by "" 12 | Then I should be able to set the file field 13 | 14 | Examples: 15 | | search_by | 16 | | id | 17 | | class | 18 | | name | 19 | | xpath | 20 | | title | 21 | | index | 22 | | label | 23 | | css | 24 | 25 | Scenario Outline: Locating file fields using multiple parameters 26 | When I search for the file field by "" and "" 27 | Then I should be able to set the file field 28 | 29 | Examples: 30 | | param1 | param2 | 31 | | class | index | 32 | | name | index | 33 | -------------------------------------------------------------------------------- /features/html/modal_1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Modal 1 5 | 6 | 23 | 24 | 25 | 26 | 27 | 28 |

Modal 1

29 | 30 |

Close buttons

31 | 32 | 33 | 34 |

Nested modal

35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /features/step_definitions/hidden_field_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I retrieve the hidden field element$/ do 2 | @element = @page.hidden_field_id_element 3 | end 4 | 5 | Then /^I should see the hidden field contains "([^\"]*)"$/ do |text| 6 | expect(@page.hidden_field_id).to eql text 7 | end 8 | 9 | When /^I search for the hidden field by "([^\"]*)"$/ do |how| 10 | @element = @page.send "hidden_field_#{how}_element" 11 | end 12 | 13 | Then /^the hidden field element should contain "([^\"]*)"$/ do |text| 14 | expect(@element.value).to eql text 15 | end 16 | 17 | When /^I search for the hidden field by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 18 | @element = @page.send "hidden_field_#{param1}_#{param2}_element" 19 | end 20 | 21 | When /^I find a hidden field while the script is executing$/ do 22 | @element = @page.hidden_field_element(:id => 'hidden_field_id') 23 | end 24 | 25 | Then /^I should see that the hidden field exists$/ do 26 | expect(@page.hidden_field_id?).to eql true 27 | end -------------------------------------------------------------------------------- /features/area.feature: -------------------------------------------------------------------------------- 1 | Feature: Area 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Retrieve an area element 7 | When I retrieve the area element 8 | Then I should know it exists 9 | And I should know it is visible 10 | 11 | Scenario Outline: Locating areas on the page 12 | When I search for the area by "" 13 | Then I should be able to click the area 14 | 15 | Examples: 16 | | search_by | 17 | | id | 18 | | class | 19 | | xpath | 20 | | index | 21 | | css | 22 | 23 | Scenario: Getting the coordinates for the area 24 | When I retrieve the area element 25 | Then I should see the coordinates are "0,0,82,126" 26 | 27 | Scenario: Getting the shape for the area 28 | When I retrieve the area element 29 | Then I should see the shape is "rect" 30 | 31 | Scenario: Getting the href from the area 32 | When I retrieve the area element 33 | Then I should see the href is "sun.html" 34 | 35 | -------------------------------------------------------------------------------- /spec/page-object/elements/button_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Button do 5 | let(:button) { PageObject::Elements::Button } 6 | 7 | describe "interface" do 8 | let(:button_element) { double('button_element') } 9 | 10 | it "should register with type :submit" do 11 | expect(::PageObject::Elements.element_class_for(:input, :submit)).to eql ::PageObject::Elements::Button 12 | end 13 | 14 | it "should register with type :image" do 15 | expect(::PageObject::Elements.element_class_for(:input, :image)).to eql ::PageObject::Elements::Button 16 | end 17 | 18 | it "should register with type :button" do 19 | expect(::PageObject::Elements.element_class_for(:input, :button)).to eql ::PageObject::Elements::Button 20 | end 21 | 22 | it "should register with type :reset" do 23 | expect(::PageObject::Elements.element_class_for(:input, :reset)).to eql ::PageObject::Elements::Button 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /features/step_definitions/check_box_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select the First check box$/ do 2 | @page.check_cb_id 3 | end 4 | 5 | Then /^the First check box should be selected$/ do 6 | expect(@page.cb_id_checked?).to be true 7 | end 8 | 9 | When /^I unselect the First check box$/ do 10 | @page.uncheck_cb_id 11 | end 12 | 13 | Then /^the First check box should not be selected$/ do 14 | expect(@page.cb_id_checked?).to be false 15 | end 16 | 17 | When /^I search for the check box by "([^\"]*)"$/ do |how| 18 | @how = how 19 | end 20 | 21 | When /^I search for the check box by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 22 | @how = "#{param1}_#{param2}" 23 | end 24 | 25 | Then /^I should be able to check the check box$/ do 26 | @page.send "check_cb_#{@how}".to_sym 27 | end 28 | 29 | When /^I select the first check box while the script is executing$/ do 30 | @page.checkbox_element(:id => 'cb_id').check 31 | end 32 | 33 | Then /^I should see that the checkbox exists$/ do 34 | expect(@page.button_id?).to be true 35 | end 36 | -------------------------------------------------------------------------------- /features/sample-app/public/jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | jQuery - Sample App 4 | 5 | 6 | 7 |
8 |

AJAX Calculator

9 |
10 | 11 | 12 |

13 |
Result:
14 |
15 | 16 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /features/html/frame_3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frame 1 5 | 6 | 7 | 8 |

Frame 3

9 |

Nam accumsan. Donec nisi pede, interdum eget, ultrices ac, vulputate vitae, nunc. Nulla lorem. Duis cursus pharetra dolor. Nulla accumsan hendrerit leo. Vivamus commodo. Nullam dignissim adipiscing est. Aliquam vitae orci in risus lobortis luctus. Ut luctus fermentum ligula. Nullam ipsum. Suspendisse sit amet nisi.

10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /features/form.feature: -------------------------------------------------------------------------------- 1 | Feature: Form 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Submitting a form 7 | When I locate the form by "id" 8 | Then I should be able to submit the form 9 | 10 | Scenario Outline: Locating a form on the page 11 | When I locate the form by "" 12 | Then I should be able to submit the form 13 | 14 | Scenarios: 15 | | search_by | 16 | | id | 17 | | class | 18 | | xpath | 19 | | index | 20 | | action | 21 | | css | 22 | 23 | Scenario Outline: Locating table using multiple parameters 24 | When I locate the form using "" and "" 25 | Then I should be able to submit the form 26 | 27 | Scenarios: 28 | | param1 | param2 | 29 | | class | index | 30 | | name | index | 31 | 32 | Scenario: Finding a form dynamically 33 | When I locate a form while the script is executing 34 | Then I should see that the form exists 35 | And I should be able to submit the form 36 | -------------------------------------------------------------------------------- /features/span.feature: -------------------------------------------------------------------------------- 1 | Feature: Span 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Getting the text from a span 7 | When I get the text from the span 8 | Then the text should be "My alert" 9 | 10 | Scenario Outline: Locating spans on the page 11 | When I search for the span by "" 12 | Then the text should be "My alert" 13 | 14 | Scenarios: 15 | | search_by | 16 | | id | 17 | | class | 18 | | xpath | 19 | | index | 20 | | text | 21 | | title | 22 | | css | 23 | 24 | Scenario Outline: Locating span using multiple parameters 25 | When I search for the span by "" and "" 26 | Then the text should be "My alert" 27 | 28 | Scenarios: 29 | | param1 | param2 | 30 | | class | index | 31 | 32 | Scenario: Finding a span dynamically 33 | When I get the text from a span while the script is executing 34 | Then I should see that the span exists 35 | And the text should be "My alert" 36 | -------------------------------------------------------------------------------- /features/html/frame_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frame 2 5 | 6 | 7 | 8 |

Frame 2

9 |

Vestibulum dignissim mauris id tellus. Nulla volutpat bibendum ante. Nam malesuada, lacus vel ultrices luctus, lorem purus tristique magna, quis pharetra leo ipsum nec neque. Cras ornare tincidunt sem. In hac habitasse platea dictumst. Suspendisse commodo turpis at est. Sed quis tortor. Aenean non massa. Phasellus scelerisque nulla vel lectus. Quisque lorem. Praesent volutpat dignissim risus. Fusce vulputate ligula eu ipsum.

10 |
11 |
12 | 13 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /features/list_item.feature: -------------------------------------------------------------------------------- 1 | Feature: List item 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Getting the text from a list item 7 | When I get the text from the list item 8 | Then the text should be "Item One" 9 | 10 | Scenario Outline: Locating list items on the page 11 | When I search for the list item by "" 12 | Then the text should be "Item One" 13 | 14 | Scenarios: 15 | | search_by | 16 | | id | 17 | | class | 18 | | xpath | 19 | | index | 20 | | text | 21 | | css | 22 | 23 | Scenario Outline: Locating list items using multiple parameters 24 | When I search for the list item by "" and "" 25 | Then the text should be "Item One" 26 | 27 | Scenarios: 28 | | param1 | param2 | 29 | | class | index | 30 | 31 | Scenario: Finding a list item dynamically 32 | When I search for the list item while the script is executing 33 | Then I should see that the list item exists 34 | And the text should be "Item One" 35 | -------------------------------------------------------------------------------- /features/generic_elements.feature: -------------------------------------------------------------------------------- 1 | Feature: Generic Elements 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Getting the text from the article element 7 | When I get the text from the article 8 | Then the text should be "HTML 5 Article" 9 | 10 | Scenario: Getting the text from the header element 11 | When I get the text from the header 12 | Then the text should be "HTML 5 Header" 13 | 14 | Scenario: Getting the text from the footer element 15 | When I get the text from the footer 16 | Then the text should be "HTML 5 Footer" 17 | 18 | Scenario: Getting the text from the summary element 19 | When I get the text from the summary 20 | Then the text should be "The summary" 21 | 22 | Scenario: Getting the text from the details element 23 | When I get the text from the details 24 | Then the text should be "The summary The details" 25 | 26 | Scenario: getting properties from a svg element 27 | When I get the svg element 28 | Then the svg width should be "100" 29 | And the svg height should be "100" 30 | -------------------------------------------------------------------------------- /features/canvas.feature: -------------------------------------------------------------------------------- 1 | Feature: Support for the canvas element 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Retrieve a canvas element 7 | When I retrieve the canvas element 8 | Then I should know it exists 9 | And I should know it is visible 10 | 11 | Scenario Outline: Locating a canvas on the page 12 | When I search for the canvas by "" 13 | Then I should know it is visible 14 | 15 | Examples: 16 | | search_by | 17 | | id | 18 | | class | 19 | | xpath | 20 | | index | 21 | | css | 22 | 23 | Scenario: Determining the width and height of the canvas 24 | When I retrieve the canvas element 25 | Then I should see that the canvas width is "200" 26 | And I should see that the canvas height is "100" 27 | 28 | Scenario Outline: Locating canvases using multiple parameters 29 | When I search for the canvas element by "" and "" 30 | Then I should know it is visible 31 | 32 | Scenarios: 33 | | param1 | param2 | 34 | | class | index | 35 | 36 | -------------------------------------------------------------------------------- /features/paragraph.feature: -------------------------------------------------------------------------------- 1 | Feature: Paragraph 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Getting the text from a paragraph 7 | When I get the text from the paragraph 8 | Then the text should be "Static Elements Page" 9 | 10 | Scenario Outline: Locating paragraphs on the page 11 | When I search for the paragraph by "" 12 | Then the text should be "Static Elements Page" 13 | 14 | Scenarios: 15 | | search_by | 16 | | id | 17 | | class | 18 | | xpath | 19 | | index | 20 | | css | 21 | 22 | Scenario Outline: Locating paragraphs using multiple parameters 23 | When I search for the paragraph by "" and "" 24 | Then the text should be "Static Elements Page" 25 | 26 | Scenarios: 27 | | param1 | param2 | 28 | | class | index | 29 | 30 | Scenario: Finding a paragraph dynamically 31 | When I get the text from a paragraph while the script is executing 32 | Then I should see that the paragraph exists 33 | And the text should be "Static Elements Page" 34 | -------------------------------------------------------------------------------- /features/step_definitions/text_area_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I type "([^\"]*)" into the text area$/ do |text| 2 | @page.text_area_id = text 3 | end 4 | 5 | Then /^the text area should contain "([^\"]*)"$/ do |expected_text| 6 | expect(@page.text_area_id).to eql expected_text 7 | end 8 | 9 | When /^I search for the text area by "([^\"]*)"$/ do |how| 10 | @how = how 11 | end 12 | 13 | When /^I search for the text area by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 14 | @how = "#{param1}_#{param2}" 15 | end 16 | 17 | Then /^I should be able to type "([^\"]*)" into the area$/ do |value| 18 | @page.send "text_area_#{@how}=".to_sym, value 19 | end 20 | 21 | When /^I find a text area while the script is executing$/ do 22 | @text_area = @page.text_area_element(:id => 'text_area_id') 23 | end 24 | 25 | Then /^I should be able to type "([^"]*)" into the area element$/ do |value| 26 | @text_area.value = value 27 | end 28 | 29 | When /^I clear the text area$/ do 30 | @page.text_area_id_element.clear 31 | end 32 | 33 | Then /^I should see that the text area exists$/ do 34 | expect(@page.text_area_id?).to be true 35 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2012 Jeff Morgan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /spec/page-object/elements/image_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Image do 5 | let(:image) { PageObject::Elements::Image } 6 | 7 | describe "interface" do 8 | let(:image_element) { double('image_element') } 9 | 10 | before(:each) do 11 | allow(image_element).to receive(:size).and_return(image_element) 12 | end 13 | 14 | it "should register with tag_name :img" do 15 | expect(::PageObject::Elements.element_class_for(:img)).to eql ::PageObject::Elements::Image 16 | end 17 | 18 | context "for watir" do 19 | it "should know the images width" do 20 | image = PageObject::Elements::Image.new(image_element) 21 | expect(image_element).to receive(:width).and_return(100) 22 | expect(image.width).to eql 100 23 | end 24 | 25 | it "should know the images height" do 26 | image = PageObject::Elements::Image.new(image_element) 27 | expect(image_element).to receive(:height).and_return(120) 28 | expect(image.height).to eql 120 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /features/step_definitions/link_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select the link labeled "([^\"]*)"$/ do |text| 2 | @page.google_search_id 3 | end 4 | 5 | When /^I search for the link by "([^\"]*)"$/ do |how| 6 | @how = how 7 | end 8 | 9 | Then /^I should be able to select the link$/ do 10 | @page.send "google_search_#{@how}".to_sym 11 | end 12 | 13 | When /^I select a link labeled "([^"]*)" and index "([^"]*)"$/ do |label, index| 14 | @page.send "#{label.downcase}#{index}".to_sym 15 | end 16 | 17 | When /^I select a link while the script is executing$/ do 18 | link = @page.link_element(:id => 'link_id') 19 | link.click 20 | end 21 | 22 | Then(/^I should see that the link exists$/) do 23 | expect(@page.link_id?).to be true 24 | end 25 | 26 | When(/^I get the href for the link$/) do 27 | @href = @page.google_search_id_element.href 28 | end 29 | 30 | Then(/^I should know it was "(.*?)"$/) do |href| 31 | expect(@href).to include href 32 | end 33 | 34 | When(/^I get the link using the href success$/) do 35 | @link = @page.link_element(:href => /succ.*html/) 36 | end 37 | 38 | Then(/^I should be able to click the link$/) do 39 | @link.click 40 | end 41 | -------------------------------------------------------------------------------- /features/populate_page_with.feature: -------------------------------------------------------------------------------- 1 | Feature: Populate Page With 2 | In order to quickly fill out forms on a page 3 | A tester will use the populate_page_with method 4 | To fill in text, select options, check boxes, and select radio buttons 5 | 6 | 7 | Background: 8 | Given I am on the static elements page 9 | 10 | Scenario: 11 | When I populate the page with the data: 12 | | text_field_id | abcDEF | 13 | | text_area_id | abcdefghijklmnop | 14 | | sel_list_id | Test 2 | 15 | | cb_id | check | 16 | | butter_id | check | 17 | | favorite_cheese | muen | 18 | Then the text field should contain "abcDEF" 19 | And the text area should contain "abcdefghijklmnop" 20 | And the selected option should be "Test 2" 21 | And the First check box should be selected 22 | And the "Butter" radio button should be selected 23 | And the "muen" radio button should be selected in the group 24 | When I populate the page with the data: 25 | | sel_list_id | option3 | 26 | Then the selected option should be "Test/Test 3" 27 | -------------------------------------------------------------------------------- /lib/page-object/elements/ordered_list.rb: -------------------------------------------------------------------------------- 1 | 2 | module PageObject 3 | module Elements 4 | class OrderedList < Element 5 | include Enumerable 6 | 7 | # 8 | # iterator that yields with a PageObject::Elements::ListItem 9 | # 10 | # @return [PageObject::Elements::ListItem] 11 | # 12 | def each(&block) 13 | list_items.each(&block) 14 | end 15 | 16 | # 17 | # Return the PageObject::Elements::ListItem for the index provided. Index 18 | # is zero based. 19 | # 20 | # @return [PageObject::Elements::ListItem] 21 | # 22 | def [](idx) 23 | list_items[idx] 24 | end 25 | 26 | # 27 | # Return the number of items contained in the ordered list 28 | # 29 | def items 30 | list_items.size 31 | end 32 | 33 | # 34 | # Return Array of ListItem objects that are children of the OrderedList 35 | # 36 | def list_items 37 | @list_items ||= children(tag_name: 'li') 38 | end 39 | end 40 | 41 | ::PageObject::Elements.tag_to_class[:ol] = ::PageObject::Elements::OrderedList 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/page-object/elements/unordered_list.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class UnorderedList < Element 4 | include Enumerable 5 | 6 | # 7 | # iterator that yields with a PageObject::Elements::ListItem 8 | # 9 | # @return [PageObject::Elements::ListItem] 10 | # 11 | def each(&block) 12 | list_items.each(&block) 13 | end 14 | 15 | # 16 | # Return the PageObject::Elements::ListItem for the index provided. Index 17 | # is zero based. 18 | # 19 | # @return [PageObject::Elements::ListItem] 20 | # 21 | def [](idx) 22 | list_items[idx] 23 | end 24 | 25 | # 26 | # Return the number of items contained in the unordered list 27 | # 28 | def items 29 | list_items.size 30 | end 31 | 32 | # 33 | # Return Array of ListItem objects that are children of the UnorderedList 34 | # 35 | def list_items 36 | @list_items ||= children(tag_name: 'li') 37 | end 38 | end 39 | 40 | ::PageObject::Elements.tag_to_class[:ul] = ::PageObject::Elements::UnorderedList 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /features/step_definitions/text_field_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I type "([^\"]*)" into the text field$/ do |text| 2 | @page.text_field_id = text 3 | end 4 | 5 | Then /^the text field should contain "([^\"]*)"$/ do |expected_text| 6 | expect(@page.text_field_id).to eql expected_text 7 | end 8 | 9 | When /^I search for the text field by "([^\"]*)"$/ do |how| 10 | @how = how 11 | end 12 | 13 | When /^I search for the text field by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 14 | @how = "#{param1}_#{param2}" 15 | end 16 | 17 | Then /^I should be able to type "([^\"]*)" into the field$/ do |value| 18 | @page.send "text_field_#{@how}=".to_sym, value 19 | end 20 | 21 | When /^I find a text field while the script is executing$/ do 22 | @text_field = @page.text_field_element(:id => 'text_field_id') 23 | end 24 | 25 | Then /^I should be able to type "([^\"]*)" into the field element$/ do |value| 26 | @text_field.value = value 27 | end 28 | 29 | Then /^I should see that the text field exists$/ do 30 | expect(@page.text_field_id?).to be true 31 | end 32 | 33 | When /^I append "([^\"]*)" to the text field$/ do |text| 34 | @page.text_field_id_element.append text 35 | end 36 | -------------------------------------------------------------------------------- /features/javascript.feature: -------------------------------------------------------------------------------- 1 | Feature: Handling javascript events 2 | 3 | Background: 4 | 5 | Scenario: Waiting for ajax to complete with jQuery 6 | Given I am on jQuery example page 7 | When I ask to compute "2 + 2" 8 | Then I should be able to wait for the answer "4" 9 | 10 | Scenario: Waiting for ajax to complete with Prototype 11 | Given I am on the Prototype example page 12 | When I ask to compute "2 + 2" 13 | Then I should be able to wait for the answer "4" 14 | 15 | Scenario: Executing javascript in the browser 16 | Given I am on the static elements page 17 | Given I execute the javascript "return 2 + 2;" 18 | Then I should get the answer "4" 19 | 20 | Scenario: Executing javascript in the browser with value argument 21 | Given I am on the static elements page 22 | Given I execute the javascript "return 2 + Number(arguments[0]);" with an argument of "2" 23 | Then I should get the answer "4" 24 | 25 | Scenario: Executing javascript in the browser with element argument 26 | Given I am on the static elements page 27 | Given I execute the javascript "arguments[0].value = 'abcDEF';" with a text field argument 28 | Then the text field should contain "abcDEF" -------------------------------------------------------------------------------- /spec/page-object/elements/heading_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Heading do 5 | let(:heading) { PageObject::Elements::Heading } 6 | 7 | describe "interface" do 8 | it "should register with tag :h1" do 9 | expect(::PageObject::Elements.element_class_for(:h1)).to eql ::PageObject::Elements::Heading 10 | end 11 | 12 | it "should register with tag :h2" do 13 | expect(::PageObject::Elements.element_class_for(:h2)).to eql ::PageObject::Elements::Heading 14 | end 15 | 16 | it "should register with tag :h3" do 17 | expect(::PageObject::Elements.element_class_for(:h3)).to eql ::PageObject::Elements::Heading 18 | end 19 | 20 | it "should register with tag :h4" do 21 | expect(::PageObject::Elements.element_class_for(:h4)).to eql ::PageObject::Elements::Heading 22 | end 23 | 24 | it "should register with tag :h5" do 25 | expect(::PageObject::Elements.element_class_for(:h5)).to eql ::PageObject::Elements::Heading 26 | end 27 | 28 | it "should register with tag :h6" do 29 | expect(::PageObject::Elements.element_class_for(:h6)).to eql ::PageObject::Elements::Heading 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /features/html/frame_1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Frame 1 5 | 6 | 7 | 8 |

Frame 1

9 |

Nam accumsan. Donec nisi pede, interdum eget, ultrices ac, vulputate vitae, nunc. Nulla lorem. Duis cursus pharetra dolor. Nulla accumsan hendrerit leo. Vivamus commodo. Nullam dignissim adipiscing est. Aliquam vitae orci in risus lobortis luctus. Ut luctus fermentum ligula. Nullam ipsum. Suspendisse sit amet nisi.

10 |
11 |
12 | 13 | 14 |
15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /features/sample-app/public/prototype.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Prototype - Sample App 4 | 5 | 6 | 7 |
8 |

AJAX Calculator

9 |
10 | 11 | 12 |

13 |
Result:
14 |
15 | 16 | 32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /features/hidden_field.feature: -------------------------------------------------------------------------------- 1 | Feature: Hidden Fields 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Setting and getting a value from a hidden field 7 | When I retrieve the hidden field element 8 | Then I should see the hidden field contains "12345" 9 | 10 | Scenario Outline: Locating hidden fields on the Page 11 | When I search for the hidden field by "" 12 | Then the hidden field element should contain "12345" 13 | 14 | Scenarios: 15 | | search_by | 16 | | id | 17 | | class | 18 | | name | 19 | | xpath | 20 | | index | 21 | | text | 22 | | value | 23 | | css | 24 | 25 | Scenario Outline: Locating a hidden field using multiple parameters 26 | When I search for the hidden field by "" and "" 27 | Then the hidden field element should contain "12345" 28 | 29 | Scenarios: 30 | | param1 | param2 | 31 | | class | index | 32 | | name | index | 33 | 34 | Scenario: Finding a hidden field dynamically 35 | When I find a hidden field while the script is executing 36 | Then I should see that the hidden field exists 37 | And the hidden field element should contain "12345" 38 | -------------------------------------------------------------------------------- /page-object.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "page-object/version" 4 | 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "page-object" 8 | s.version = PageObject::VERSION 9 | s.platform = Gem::Platform::RUBY 10 | s.authors = ["Jeff Morgan", 'Alexis Andersen'] 11 | s.email = ["jeff.morgan@leandog.com", 'alexis.t.andersen@gmail.com'] 12 | s.license = 'MIT' 13 | s.homepage = "http://github.com/cheezy/page-object" 14 | s.summary = %q{Page Object DSL for browser testing} 15 | s.description = %q{Page Object DSL that works with both Watir and Selenium} 16 | 17 | s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(pkg|spec|features|coverage)/}) } 18 | s.require_paths = ["lib"] 19 | 20 | s.add_dependency 'watir', '>= 6.10.3' 21 | s.add_dependency 'page_navigation', '>= 0.10' 22 | 23 | s.add_development_dependency 'rspec', '~> 3.0' 24 | s.add_development_dependency 'cucumber', '~> 2.0' 25 | s.add_development_dependency 'yard', '>= 0.7.2' 26 | s.add_development_dependency 'rack', '~> 1.0' 27 | s.add_development_dependency 'coveralls', '~> 0.8.1' 28 | s.add_development_dependency 'net-http-persistent' 29 | s.add_development_dependency 'webdrivers' 30 | 31 | end 32 | -------------------------------------------------------------------------------- /lib/page-object/elements/select_list.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class SelectList < Element 4 | 5 | # 6 | # Return the PageObject::Elements::Option for the index provided. Index 7 | # is zero based. 8 | # 9 | # @return [PageObject::Elements::Option] 10 | # 11 | def [](idx) 12 | options[idx] 13 | end 14 | 15 | # 16 | # Return an array of Options contained in the select list. 17 | # 18 | # @return [array of PageObject::Elements::Option] 19 | # 20 | def options 21 | element.options.map { |e| PageObject::Elements::Option.new(e) } 22 | end 23 | 24 | # 25 | # @return [Array] An array of strings representing the text of the currently selected options. 26 | # 27 | def selected_options 28 | element.selected_options.map(&:text).compact 29 | end 30 | 31 | # 32 | # @return [Array] An array of strings representing the value of the currently selected options. 33 | # 34 | def selected_values 35 | element.selected_options.map(&:value).compact 36 | end 37 | 38 | end 39 | 40 | ::PageObject::Elements.tag_to_class[:select] = ::PageObject::Elements::SelectList 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/page-object/elements/unordered_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::UnorderedList do 5 | let(:ul) { PageObject::Elements::UnorderedList.new(ul_element) } 6 | 7 | describe "interface" do 8 | let(:ul_element) { double('ul_element').as_null_object } 9 | let(:li_element) { double('li_element').as_null_object } 10 | 11 | it "should register with tag_name :ul" do 12 | expect(::PageObject::Elements.element_class_for(:ul)).to eql ::PageObject::Elements::UnorderedList 13 | end 14 | 15 | context "for watir" do 16 | before(:each) do 17 | allow(ul_element).to receive(:children).and_return([li_element, li_element]) 18 | allow(li_element).to receive(:tag_name).and_return(:li) 19 | end 20 | 21 | it "should return a list item when indexed" do 22 | expect(ul[1]).to be_instance_of PageObject::Elements::ListItem 23 | end 24 | 25 | it "should know how many items it contains" do 26 | expect(ul.items).to eql 2 27 | end 28 | 29 | it "should know how to iterate over the items" do 30 | count = 0 31 | ul.each { |item| count += 1 } 32 | expect(count).to eql 2 33 | end 34 | end 35 | 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /features/step_definitions/modal_dialog_steps.rb: -------------------------------------------------------------------------------- 1 | class ModalPage 2 | include PageObject 3 | 4 | button(:launch_modal, :id => 'launch_modal_button') 5 | end 6 | 7 | class ModalDialog 8 | include PageObject 9 | 10 | button(:close_window, :id => 'close_window') 11 | button(:close_window_with_delay, :id => 'delayed_close') 12 | button(:launch_another_modal, :id => 'launch_modal_button') 13 | end 14 | 15 | class AnotherModalDialog 16 | include PageObject 17 | 18 | button(:close_window, :id => 'close_window2') 19 | button(:close_window_with_delay, :id => 'delayed_close2') 20 | end 21 | 22 | 23 | Given /^I am on the modal page$/ do 24 | ModalPage.new(@browser).navigate_to(UrlHelper.modal) 25 | end 26 | 27 | When /^I open a modal dialog$/ do 28 | page = ModalPage.new(@browser) 29 | page.modal_dialog do 30 | page.launch_modal 31 | end 32 | end 33 | 34 | Then /^I should be able to close the modal$/ do 35 | dialog = ModalDialog.new(@browser) 36 | dialog.attach_to_window(:title => 'Modal 1') do 37 | dialog.close_window 38 | end 39 | end 40 | 41 | When /^I open another modal dialog from that one$/ do 42 | dialog = ModalDialog.new(@browser) 43 | dialog.attach_to_window(:title => 'Modal 1') 44 | dialog.modal_dialog 45 | dialog.launch_another_modal 46 | end 47 | -------------------------------------------------------------------------------- /spec/page-object/elements/table_row_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::TableRow do 5 | let(:table_cell) { double('table_cell') } 6 | let(:table_row_driver) { double('table_row_driver') } 7 | let(:table_row) { PageObject::Elements::TableRow.new(table_row_driver) } 8 | 9 | describe "interface" do 10 | 11 | it "should register with tag_name :tr" do 12 | expect(::PageObject::Elements.element_class_for(:tr)).to eql ::PageObject::Elements::TableRow 13 | end 14 | 15 | context "for watir" do 16 | before(:each) do 17 | allow(table_row_driver).to receive(:find_elements).and_return(table_row_driver) 18 | allow(table_row_driver).to receive(:cells).and_return(Array.new(2, Watir::TableCell)) 19 | end 20 | 21 | it "should return a table cell when indexed" do 22 | expect(table_row[1]).to be_instance_of PageObject::Elements::TableCell 23 | end 24 | 25 | it "should return the number of columns" do 26 | expect(table_row.columns).to eql 2 27 | end 28 | 29 | it "should iterate over the table columns" do 30 | count = 0 31 | table_row.each { count += 1 } 32 | expect(count).to eql 2 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /features/step_definitions/button_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I click the button$/ do 2 | @page.button_id 3 | end 4 | 5 | When /^I click the button with type image$/ do 6 | @page.button_image_id 7 | end 8 | 9 | When /^I click the image button using src$/ do 10 | @page.button_image_src 11 | end 12 | 13 | When /^I click the image button using alt$/ do 14 | @page.button_image_alt 15 | end 16 | 17 | When /^I search for the button by "([^\"]*)"$/ do |how| 18 | @how = how 19 | end 20 | 21 | When /^I search for the button by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 22 | @how = "#{param1}_#{param2}" 23 | end 24 | 25 | Then /^I should be able to click the button$/ do 26 | @page.send "button_#{@how}" 27 | end 28 | 29 | Then /^I should be able to click the real button$/ do 30 | @page.send "btn_#{@how}" 31 | end 32 | 33 | When /^I find a button while the script is executing$/ do 34 | @button = @page.button_element(:id => 'button_id') 35 | end 36 | 37 | Then /^I should be able to click the button element$/ do 38 | @button.click 39 | end 40 | 41 | Then /^I should see that the button exists$/ do 42 | expect(@page.button_id?).to be true 43 | end 44 | 45 | 46 | When(/^I get the text for a button that is a "([^"]*)"$/) do |tagname| 47 | @text = @page.button_element(:id => "#{tagname}_button").text 48 | end -------------------------------------------------------------------------------- /features/step_definitions/radio_button_group_steps.rb: -------------------------------------------------------------------------------- 1 | When /^no radio buttons have been selected$/ do 2 | # nothing to do here 3 | end 4 | 5 | Then /^no radio buttons should be selected in the group$/ do 6 | expect(@page.favorite_cheese_selected?).to be false 7 | end 8 | 9 | When /^I select the "([^\"]*)" radio button in the group$/ do |how| 10 | @page.select_favorite_cheese("#{how}") 11 | end 12 | 13 | Then /^the "([^\"]*)" radio button should be selected in the group$/ do |how| 14 | expect(@page.favorite_cheese_selected?).to eql "#{how}" 15 | end 16 | 17 | Then /^the "([^\"]*)" radio button should not be selected$/ do |how| 18 | expect(@page.favorite_cheese_selected?).not_to eql "#{how}" 19 | end 20 | 21 | Then /^I should see that the radio button group exists$/ do 22 | expect(@page.favorite_cheese?).to be true 23 | end 24 | 25 | When /^I ask for the elements of a radio button group$/ do 26 | @elems = @page.favorite_cheese_elements 27 | end 28 | 29 | Then /^I should have an array with elements for each radio button$/ do 30 | expect(@elems.length).to eql 3 31 | end 32 | 33 | And /^the radio button element values should be "([^\"]*)", "([^\"]*)", "([^\"]*)"$/ do |val1, val2, val3| 34 | elem_arr = @elems.collect { |elem| elem.value } 35 | expect(elem_arr).to eql [val1, val2, val3] 36 | end -------------------------------------------------------------------------------- /features/step_definitions/image_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I get the image element$/ do 2 | @element = @page.image_id_element 3 | end 4 | 5 | When /^I get the image element load status$/ do 6 | @status = @page.image_id_loaded? 7 | end 8 | 9 | When /^I get the broken image element load status$/ do 10 | @status = @page.image_broken_loaded? 11 | end 12 | 13 | Then /^the image should be "([^\"]*)" pixels wide$/ do |width| 14 | expect(@element.width).to eql width.to_i 15 | end 16 | 17 | Then /^the image should be "([^\"]*)" pixels tall$/ do |height| 18 | expect(@element.height).to eql height.to_i 19 | end 20 | 21 | When /^I get the image element by "([^\"]*)"$/ do |how| 22 | @element = @page.send "image_#{how}_element" 23 | end 24 | 25 | When /^I get the image element by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 26 | @element = @page.send "image_#{param1}_#{param2}_element" 27 | end 28 | 29 | When /^I get the image element while the script is executing$/ do 30 | @element = @page.image_element(:id => 'image_id') 31 | end 32 | 33 | Then /^I should see that the image exists$/ do 34 | expect(@page.image_id?).to be true 35 | end 36 | 37 | Then /^I should see that the image loaded$/ do 38 | expect(@status).to be true 39 | end 40 | 41 | Then /^I should see that the image is not loaded$/ do 42 | expect(@status).to be false 43 | end 44 | -------------------------------------------------------------------------------- /features/step_definitions/video_steps.rb: -------------------------------------------------------------------------------- 1 | 2 | When /^I retrieve the video element$/ do 3 | @element = @avpage.video_id_element 4 | end 5 | 6 | When /^I search for the video element by "([^\"]*)"$/ do |how| 7 | @element = @avpage.send "video_#{how}_element" 8 | end 9 | 10 | When /^I search for the video element by "([^\"]*)" and "([^\"]*)"$/ do |param1, param2| 11 | @element = @avpage.send "video_#{param1}_#{param2}_element" 12 | end 13 | 14 | Then /^I should know the video is not autoplay$/ do 15 | expect(@element).not_to be_autoplay 16 | end 17 | 18 | Then /^I should know that the video is paused$/ do 19 | expect(@element).to be_paused 20 | end 21 | 22 | Then /^I should know that its height is (\d+) pixels$/ do |height| 23 | expect(@element.height).to eql height.to_i 24 | end 25 | 26 | Then /^I should knot what its width is (\d+) pixels$/ do |width| 27 | expect(@element.width).to eql width.to_i 28 | end 29 | 30 | Then /^I should know that it has not ended$/ do 31 | expect(@element).not_to be_ended 32 | end 33 | 34 | Then /^I should know that it is not seeking$/ do 35 | expect(@element).not_to be_seeking 36 | end 37 | 38 | Then /^I should know that it is not in a loop$/ do 39 | expect(@element).not_to be_loop 40 | end 41 | 42 | Then /^I should know that it is muted$/ do 43 | expect(@element).not_to be_muted 44 | end 45 | -------------------------------------------------------------------------------- /features/table_cell.feature: -------------------------------------------------------------------------------- 1 | Feature: Table Cell 2 | 3 | 4 | Background: 5 | Given I am on the static elements page 6 | 7 | Scenario: Retrieving the text from a table cell 8 | When I retrieve the data from the table cell 9 | Then the cell data should be 'Data4' 10 | 11 | Scenario Outline: Locating table cells on the Page 12 | When I search for the table cell by "" 13 | Then the cell data should be 'Data4' 14 | 15 | Scenarios: 16 | | search_by | 17 | | id | 18 | | class | 19 | | xpath | 20 | | text | 21 | | css | 22 | 23 | Scenario Outline: Locating table cells on the Page with watir 24 | When I search for the table cell by "" 25 | Then the cell data should be 'Data4' 26 | 27 | Scenarios: 28 | | search_by | 29 | | index | 30 | 31 | Scenario Outline: Locating table cell using multiple parameters 32 | When I retrieve a table cell element by "" and "" 33 | Then the cell data should be 'Data4' 34 | 35 | Scenarios: 36 | | param1 | param2 | 37 | | class | index | 38 | 39 | Scenario: Finding a table cell dynamically 40 | When I retrieve a table cell element while the script is executing 41 | Then I should see that the cell exists 42 | And the cell data should be 'Data4' 43 | -------------------------------------------------------------------------------- /features/div.feature: -------------------------------------------------------------------------------- 1 | Feature: Div 2 | In order to interact with divs 3 | Testers will need access and interrogation ability 4 | 5 | 6 | Background: 7 | Given I am on the static elements page 8 | 9 | Scenario: Getting the text from a div 10 | When I get the text from the div 11 | Then the text should be "page-object rocks!" 12 | 13 | Scenario: Getting the div element 14 | When I retrieve the div element 15 | Then I should know it exists 16 | And I should know it is visible 17 | 18 | Scenario Outline: Locating divs on the page 19 | When I search for the div by "" 20 | Then the text should be "page-object rocks!" 21 | 22 | Scenarios: 23 | | search_by | 24 | | id | 25 | | class | 26 | | xpath | 27 | | index | 28 | | text | 29 | | title | 30 | | css | 31 | 32 | Scenario Outline: Locating divs using multiple parameters 33 | When I search for the div by "" and "" 34 | Then the text should be "page-object rocks!" 35 | 36 | Scenarios: 37 | | param1 | param2 | 38 | | class | index | 39 | 40 | Scenario: Finding a div dynamically 41 | When I get the text from a div while the script is executing 42 | Then I should see that the div exists 43 | And the text should be "page-object rocks!" 44 | -------------------------------------------------------------------------------- /features/support/url_helper.rb: -------------------------------------------------------------------------------- 1 | module UrlHelper 2 | class << self 3 | def html 4 | File.expand_path("#{File.dirname(__FILE__)}/../html") 5 | end 6 | 7 | def files 8 | target = ENV['BROWSER'] 9 | return "file://#{html}" if target.nil? or target.include? 'local' 10 | 'http://ec2-107-22-131-88.compute-1.amazonaws.com' 11 | end 12 | 13 | def static_elements 14 | "#{files}/static_elements.html" 15 | end 16 | 17 | def frame_elements 18 | "#{files}/frames.html" 19 | end 20 | 21 | def iframe_elements 22 | "#{files}/iframes.html" 23 | end 24 | 25 | def nested_frame_elements 26 | "#{files}/nested_frames.html" 27 | end 28 | 29 | def nested_elements 30 | "#{files}/nested_elements.html" 31 | end 32 | 33 | def modal 34 | "#{files}/modal.html" 35 | end 36 | 37 | def async 38 | "#{files}/async.html" 39 | end 40 | 41 | def multi 42 | "#{files}/multi_elements.html" 43 | end 44 | 45 | def indexed 46 | "#{files}/indexed_property.html" 47 | end 48 | 49 | def hover 50 | "#{files}/hover.html" 51 | end 52 | 53 | def double_click 54 | "#{files}/double_click.html" 55 | end 56 | 57 | def widgets 58 | "#{files}/widgets.html" 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/page-object/accessors_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class GenericPage 4 | include PageObject 5 | 6 | wait_for_expected_title 'expected title' 7 | end 8 | 9 | describe 'accessors' do 10 | let(:browser) { mock_watir_browser } 11 | let(:page) { GenericPage.new browser } 12 | 13 | context '#wait_for_expected_title' do 14 | before(:each) do 15 | allow(browser).to receive(:wait_until).and_yield 16 | end 17 | 18 | it 'true if already there' do 19 | allow(browser).to receive(:title).and_return 'expected title' 20 | expect(page.wait_for_expected_title?).to be_truthy 21 | end 22 | 23 | it 'does not wait if it already is there' do 24 | allow(browser).to receive(:title).and_return 'expected title' 25 | expect(browser).to_not receive(:wait_until) 26 | 27 | expect(page.wait_for_expected_title?).to be_truthy 28 | end 29 | 30 | it 'errors when it does not match' do 31 | allow(browser).to receive(:title).and_return 'wrong title' 32 | expect { page.wait_for_expected_title? }.to raise_error "Expected title 'expected title' instead of 'wrong title'" 33 | end 34 | 35 | it 'picks up when the title changes' do 36 | allow(browser).to receive(:title).and_return 'wrong title', 'expected title' 37 | expect(page.wait_for_expected_title?).to be_truthy 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 3 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 4 | 5 | require 'simplecov' 6 | require 'coveralls' 7 | SimpleCov.start { add_filter 'spec/' } 8 | 9 | require 'rspec' 10 | require 'watir' 11 | require 'webdrivers' 12 | require 'selenium-webdriver' 13 | 14 | require 'page-object' 15 | 16 | def mock_watir_browser 17 | watir_browser = double('watir') 18 | allow(watir_browser).to receive(:is_a?).with(anything).and_return(false) 19 | allow(watir_browser).to receive(:is_a?).with(Watir::Browser).and_return(true) 20 | allow(watir_browser).to receive(:exists?).and_return(true) 21 | allow(watir_browser).to receive(:to_subtype).and_return(watir_browser) 22 | watir_browser 23 | end 24 | 25 | 26 | def mock_adapter(browser, page_object) 27 | adapter = double('adapter') 28 | allow(adapter).to receive(:is_for?).with(anything()).and_return false 29 | allow(adapter).to receive(:is_for?).with(browser).and_return true 30 | allow(adapter).to receive(:create_page_object).and_return page_object 31 | allow(adapter).to receive(:root_element_for).and_return browser 32 | allow(adapter).to receive(:browser_for).and_return browser 33 | adapter 34 | end 35 | 36 | def mock_adapters(adapters) 37 | allow(PageObject::Platforms).to receive(:get).and_return adapters 38 | end 39 | -------------------------------------------------------------------------------- /features/html/async.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Async Page 4 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /features/radio_button_group.feature: -------------------------------------------------------------------------------- 1 | Feature: Radio Button Groups 2 | In order to interact with radio button groups 3 | Testers will need access and interrogation ability 4 | 5 | 6 | Background: 7 | Given I am on the static elements page 8 | 9 | Scenario: Confirm existence of a radio button group 10 | Then I should see that the radio button group exists 11 | 12 | Scenario: No radio buttons in the group have been selected 13 | When no radio buttons have been selected 14 | Then no radio buttons should be selected in the group 15 | 16 | Scenario: Selecting grouped radio buttons by value 17 | When I select the "ched" radio button in the group 18 | Then the "ched" radio button should be selected in the group 19 | And the "emmen" radio button should not be selected 20 | And the "muen" radio button should not be selected 21 | When I select the "muen" radio button in the group 22 | Then the "ched" radio button should not be selected 23 | And the "emmen" radio button should not be selected 24 | And the "muen" radio button should be selected in the group 25 | 26 | Scenario: Getting an array of elements for each radio button in the group 27 | When I ask for the elements of a radio button group 28 | Then I should have an array with elements for each radio button 29 | And the radio button element values should be "ched", "emmen", "muen" 30 | -------------------------------------------------------------------------------- /features/table_row.feature: -------------------------------------------------------------------------------- 1 | Feature: Table Row 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Retrieving the text from a table row 7 | When I retrieve the data from the table row 8 | Then the row data should be 'Data1 Data2' 9 | 10 | Scenario Outline: Locating table rows on the Page 11 | When I search for the table row by "" 12 | Then the row data should be 'Data1 Data2' 13 | 14 | Scenarios: 15 | | search_by | 16 | | id | 17 | | class | 18 | | xpath | 19 | | css | 20 | 21 | 22 | Scenario: Locating table rows on the Page by text 23 | When I search for the table row by "text" 24 | Then the row data should be 'Data1 Data2' 25 | 26 | Scenario Outline: Locating table rows on the Page with watir 27 | When I search for the table row by "" 28 | Then the row data should be 'Data1 Data2' 29 | 30 | Scenarios: 31 | | search_by | 32 | | index | 33 | 34 | Scenario: Locating table row using multiple parameters 35 | When I retrieve a table row element by "class" and "index" 36 | Then the row data should be 'Data1 Data2' 37 | 38 | Scenario: Finding a table row dynamically 39 | When I retrieve a table row element while the script is executing 40 | Then I should see that the row exists 41 | And the row data should be 'Data1 Data2' 42 | -------------------------------------------------------------------------------- /features/step_definitions/gxt_table_steps.rb: -------------------------------------------------------------------------------- 1 | 2 | def class_exists?(class_name) 3 | begin 4 | klass = Module.const_get(class_name) 5 | return klass.is_a?(Class) 6 | rescue NameError 7 | return false 8 | end 9 | end 10 | 11 | Given /^I am on the Gxt Examples page$/ do 12 | visit GxtSamplePageObject 13 | end 14 | 15 | When /^I have defined a GxtTable class extending Table$/ do 16 | class GxtTable < PageObject::Elements::Table 17 | 18 | def self.accessor_methods(accessor, name) 19 | accessor.send :define_method, "#{name}_rows" do 20 | self.send("#{name}_element").rows 21 | end 22 | end 23 | 24 | protected 25 | 26 | def strategy 27 | :descendants 28 | end 29 | end 30 | end 31 | 32 | When /^I define a page-object using that widget$/ do 33 | class GxtSamplePageObject 34 | include PageObject 35 | 36 | page_url UrlHelper.widgets 37 | 38 | gxt_table(:gxt_table, :class => "x-grid3") 39 | end unless class_exists? 'GxtSamplePageObject' 40 | end 41 | 42 | When /^I have registered the GxtTable with PageObject$/ do 43 | PageObject.register_widget :gxt_table, GxtTable, 'div' 44 | end 45 | 46 | When /^I retrieve a GxtTable widget$/ do 47 | @element = on(GxtSamplePageObject).gxt_table_element 48 | end 49 | 50 | 51 | When /^the GxtTable should have "(\d+)" rows$/ do |rows| 52 | expect(on(GxtSamplePageObject).gxt_table_rows).to eql rows.to_i 53 | end 54 | 55 | -------------------------------------------------------------------------------- /features/label.feature: -------------------------------------------------------------------------------- 1 | Feature: Handling labels with page object 2 | 3 | In order to interact with labels, 4 | Testers will need to access the element 5 | and the ability to interrogate 6 | 7 | Background: 8 | Given I am on the static elements page 9 | 10 | Scenario: Getting the text from a label 11 | When I get the text from the label 12 | Then the text should be "page-object is the best!" 13 | 14 | Scenario: Getting the label element 15 | When I retrieve the label element 16 | Then I should know it exists 17 | And I should know it is visible 18 | 19 | Scenario Outline: Locating labels on the page 20 | When I search for the label by "" 21 | Then the text should be "page-object is the best!" 22 | 23 | Scenarios: 24 | | search_by | 25 | | id | 26 | | class | 27 | | xpath | 28 | | index | 29 | | text | 30 | | css | 31 | 32 | Scenario Outline: Locating labels using multiple parameters 33 | When I search for the label by "" and "" 34 | Then the text should be "page-object is the best!" 35 | 36 | Scenarios: 37 | | param1 | param2 | 38 | | class | index | 39 | 40 | Scenario: Finding a label dynamically 41 | When I get the text from a label while the script is executing 42 | Then I should see that the label exists 43 | And the text should be "page-object is the best!" 44 | 45 | -------------------------------------------------------------------------------- /spec/page-object/elements/table_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::Table do 5 | describe "interface" do 6 | let(:table_element) { double('table_element') } 7 | 8 | before(:each) do 9 | allow(table_element).to receive(:rows).and_return(Array.new(2, Watir::TableRow)) 10 | end 11 | 12 | it "should register with tag_name :table" do 13 | expect(::PageObject::Elements.element_class_for(:table)).to eql ::PageObject::Elements::Table 14 | end 15 | 16 | context "for watir" do 17 | let(:watir_table) { PageObject::Elements::Table.new(table_element) } 18 | 19 | it "should return a table row when indexed" do 20 | expect(watir_table[1]).to be_instance_of PageObject::Elements::TableRow 21 | end 22 | 23 | it "should return the first row" do 24 | expect(watir_table.first_row).to be_instance_of PageObject::Elements::TableRow 25 | end 26 | 27 | it "shoudl return the last row" do 28 | expect(watir_table.last_row).to be_instance_of PageObject::Elements::TableRow 29 | end 30 | 31 | it "should return the number of rows" do 32 | expect(watir_table.rows).to eql 2 33 | end 34 | 35 | it "should iterate over the table rows" do 36 | count = 0 37 | watir_table.each { |e| count += 1 } 38 | expect(count).to eql 2 39 | end 40 | end 41 | 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /features/text_area.feature: -------------------------------------------------------------------------------- 1 | Feature: Text Area 2 | 3 | 4 | Background: 5 | Given I am on the static elements page 6 | 7 | Scenario: Setting and getting a value from a text area 8 | When I type "abcdefghijklmnop" into the text area 9 | Then the text area should contain "abcdefghijklmnop" 10 | 11 | Scenario Outline: Locating text area on the Page 12 | When I search for the text area by "" 13 | Then I should be able to type "I found it" into the area 14 | 15 | Scenarios: 16 | | search_by | 17 | | id | 18 | | class | 19 | | name | 20 | | xpath | 21 | | index | 22 | | label | 23 | | css | 24 | 25 | Scenario Outline: Locating a text area using multiple parameters 26 | When I search for the text area by "" and "" 27 | Then I should be able to type "I found it" into the area 28 | 29 | Scenarios: 30 | | param1 | param2 | 31 | | class | index | 32 | | name | index | 33 | 34 | Scenario: Finding a text area dynamically 35 | When I find a text area while the script is executing 36 | Then I should see that the text area exists 37 | And I should be able to type "I found it" into the area element 38 | 39 | Scenario: Clearing the text area 40 | When I type "abcdefghijklmnop" into the text area 41 | Then the text area should contain "abcdefghijklmnop" 42 | When I clear the text area 43 | Then the text area should contain "" 44 | -------------------------------------------------------------------------------- /lib/page-object/elements/table_row.rb: -------------------------------------------------------------------------------- 1 | require 'watir/elements/html_elements' 2 | 3 | module PageObject 4 | module Elements 5 | class TableRow < Element 6 | include Enumerable 7 | 8 | # 9 | # iterator that yields with a PageObject::Elements::TableCell 10 | # 11 | def each(&block) 12 | cell_items.each(&block) 13 | end 14 | 15 | # 16 | # Return the PageObject::Elements::TableCell for the index provided. Index 17 | # is zero based. If the index provided is a String then it 18 | # will be matched with the text from the columns in the first row. 19 | # The text can be a substring of the full column text. 20 | # 21 | def [](what) 22 | idx = find_index(what) 23 | idx && cell_items[idx] 24 | end 25 | 26 | # 27 | # Returns the number of columns in the table. 28 | # 29 | def columns 30 | cell_items.size 31 | end 32 | 33 | protected 34 | 35 | def cell_items 36 | @cell_items ||= element.cells.map do |obj| 37 | ::PageObject::Elements::TableCell.new(obj) 38 | end 39 | end 40 | 41 | def find_index(what) 42 | return what if what.is_a? Integer 43 | 44 | parent(tag_name: 'table').headers.find_index do |header| 45 | header.text.include? what 46 | end 47 | end 48 | end 49 | 50 | ::PageObject::Elements.tag_to_class[:tr] = ::PageObject::Elements::TableRow 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/page-object/elements/ordered_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::OrderedList do 5 | let(:ol) { PageObject::Elements::OrderedList } 6 | 7 | describe "interface" do 8 | let(:ol_element) { double('ol_element').as_null_object } 9 | let(:li_element) { double('li_element') } 10 | 11 | it "should register as tag_name :ol" do 12 | expect(::PageObject::Elements.element_class_for(:ol)).to eql ::PageObject::Elements::OrderedList 13 | end 14 | 15 | context "for watir" do 16 | before(:each) do 17 | allow(ol_element).to receive(:tag_name).and_return(:ol) 18 | end 19 | 20 | it "should return a list item when indexed" do 21 | ol = PageObject::Elements::OrderedList.new(ol_element) 22 | expect(ol_element).to receive(:children). 23 | and_return([ol_element, ol_element]) 24 | ol[1] 25 | end 26 | 27 | it "should know how many list items it contains" do 28 | ol = PageObject::Elements::OrderedList.new(ol_element) 29 | expect(ol_element).to receive(:children).and_return([ol_element]) 30 | expect(ol.items).to eql 1 31 | end 32 | 33 | it "should iterate over the list items" do 34 | ol = PageObject::Elements::OrderedList.new(ol_element) 35 | allow(ol).to receive(:list_items).and_return(Array.new(5, ol_element)) 36 | expect(ol.items).to eql 5 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/page-object/indexed_properties.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module IndexedProperties 3 | class TableOfElements 4 | include PageObject 5 | 6 | def initialize (browser, identifier_list) 7 | initialize_browser(browser) 8 | @identifier_list = identifier_list 9 | @indexed_property_class = Class.new { 10 | include PageObject 11 | extend Accessors 12 | 13 | def initialize (browser, index, identifier_list) 14 | initialize_browser(browser) 15 | 16 | identifier_list.each do |identifier| 17 | type = identifier[0] 18 | name = identifier[1] 19 | how_and_what = identifier[2].clone # Cannot modify the original... 20 | how_and_what.each do |key, value| 21 | next if value.is_a? Regexp # Cannot format Regexp with % 22 | if key == :index 23 | how_and_what[key] = (value % index).to_i 24 | elsif key == :frame 25 | how_and_what[key] = value #passthrough frame without modification 26 | else 27 | how_and_what[key] = value % index 28 | end 29 | end 30 | self.class.send type, name, how_and_what unless Class.instance_methods.include? name 31 | end 32 | end 33 | } 34 | end 35 | 36 | def [] (index) 37 | @indexed_property_class.new(@browser, index, @identifier_list) 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /features/check_box.feature: -------------------------------------------------------------------------------- 1 | Feature: Check Box 2 | In order to interact with check boxes 3 | Testers will need access and interrogation ability 4 | 5 | 6 | Background: 7 | Given I am on the static elements page 8 | 9 | Scenario: Selecting a check box 10 | When I select the First check box 11 | Then the First check box should be selected 12 | When I unselect the First check box 13 | Then the First check box should not be selected 14 | 15 | Scenario Outline: Locating check boxes on the page 16 | When I search for the check box by "" 17 | Then I should be able to check the check box 18 | 19 | Scenarios: 20 | | search_by | 21 | | id | 22 | | class | 23 | | name | 24 | | xpath | 25 | | index | 26 | | value | 27 | | label | 28 | | css | 29 | 30 | Scenario Outline: Locating check boxes using multiple parameters 31 | When I search for the check box by "" and "" 32 | Then I should be able to check the check box 33 | 34 | Scenarios: 35 | | param1 | param2 | 36 | | class | index | 37 | | name | index | 38 | 39 | Scenario: Retrieve a CheckBox 40 | When I retrieve a check box element 41 | Then I should know it exists 42 | And I should know it is visible 43 | 44 | Scenario: Finding a check box dynamically 45 | When I select the first check box while the script is executing 46 | Then I should see that the checkbox exists 47 | And the First check box should be selected 48 | -------------------------------------------------------------------------------- /lib/page-object/platforms/watir.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Platforms 3 | module Watir 4 | 5 | def self.create_page_object(browser) 6 | browser = selenium_browser(browser) unless watir?(browser) 7 | return Watir::PageObject.new(browser) 8 | end 9 | 10 | def self.is_for?(browser) 11 | require 'watir' 12 | watir?(browser) || selenium?(browser) 13 | end 14 | 15 | def self.browser_for root 16 | return watir_browser(root) if watir?(root) 17 | return selenium_browser(root) if selenium?(root) 18 | nil 19 | end 20 | 21 | def self.root_element_for root 22 | Elements::Element.new(root) if is_for?(root) 23 | end 24 | 25 | def self.browser_root_for browser 26 | browser.element 27 | end 28 | 29 | private 30 | 31 | def self.watir_browser(root) 32 | return root if root.is_a?(::Watir::Browser) 33 | root.browser 34 | end 35 | 36 | def self.selenium_browser(root) 37 | return ::Watir::Browser.new(root) if root.is_a?(::Selenium::WebDriver::Driver) 38 | ::Watir::Browser.new(Selenium::WebDriver::Driver.new(root.send(:bridge))) 39 | end 40 | 41 | def self.watir?(browser) 42 | browser.is_a?(::Watir::Browser) || browser.is_a?(::Watir::HTMLElement) 43 | end 44 | 45 | def self.selenium?(browser) 46 | browser.is_a?(::Selenium::WebDriver::Driver) || browser.is_a?(::Selenium::WebDriver::Element) 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /features/image.feature: -------------------------------------------------------------------------------- 1 | Feature: Image 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Getting the image element 7 | When I get the image element 8 | Then the image should be "106" pixels wide 9 | And the image should be "106" pixels tall 10 | 11 | Scenario Outline: Locating an image on the page 12 | When I get the image element by "" 13 | Then the image should be "106" pixels wide 14 | And the image should be "106" pixels tall 15 | 16 | Scenarios: 17 | | search_by | 18 | | id | 19 | | class | 20 | | name | 21 | | xpath | 22 | | index | 23 | | alt | 24 | | src | 25 | | css | 26 | 27 | Scenario Outline: Locating an image using multiple parameters 28 | When I get the image element by "" and "" 29 | Then the image should be "106" pixels wide 30 | And the image should be "106" pixels tall 31 | 32 | Scenarios: 33 | | param1 | param2 | 34 | | class | index | 35 | | name | index | 36 | 37 | Scenario: Finding an image dynamically 38 | When I get the image element while the script is executing 39 | Then I should see that the image exists 40 | And the image should be "106" pixels wide 41 | And the image should be "106" pixels tall 42 | 43 | Scenario: Check an image loaded 44 | When I get the image element load status 45 | Then I should see that the image loaded 46 | 47 | Scenario: Check an image is not loaded 48 | When I get the broken image element load status 49 | Then I should see that the image is not loaded 50 | -------------------------------------------------------------------------------- /features/ordered_list.feature: -------------------------------------------------------------------------------- 1 | Feature: Ordered list 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Getting the first element from the ordered list 7 | When I get the first item from the ordered list 8 | Then the list items text should be "Number One" 9 | 10 | Scenario Outline: Locating ordered lists on the page 11 | When I search for the ordered list by "" 12 | And I get the first item from the list 13 | Then the list items text should be "Number One" 14 | And the list should contain 3 items 15 | And each item should contain "Number" 16 | 17 | Scenarios: 18 | | search_by | 19 | | id | 20 | | class | 21 | | xpath | 22 | | index | 23 | | css | 24 | 25 | Scenario Outline: Locating ordered lists using multiple parameters 26 | When I search for the ordered list by "" and "" 27 | And I get the first item from the list 28 | Then the list items text should be "Number One" 29 | 30 | Scenarios: 31 | | param1 | param2 | 32 | | class | index | 33 | 34 | Scenario: Finding a ordered list dynamically 35 | When I search for the ordered list while the script is executing 36 | Then I should see that the ordered list exists 37 | When I get the first item from the list 38 | Then the list items text should be "Number One" 39 | 40 | Scenario: Getting the test for an ordered list 41 | Then the text for the ordered list should contain "Number One" 42 | And the text for the ordered list should contain "Number Two" 43 | And the text for the ordered list should contain "Number Three" 44 | -------------------------------------------------------------------------------- /features/unordered_list.feature: -------------------------------------------------------------------------------- 1 | Feature: Unordered list 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Getting the first element from the unordered list 7 | When I get the first item from the unordered list 8 | Then the list items text should be "Item One" 9 | 10 | Scenario Outline: Locating unordered lists on the page 11 | When I search for the unordered list by "" 12 | And I get the first item from the list 13 | Then the list items text should be "Item One" 14 | And the list should contain 3 items 15 | And each item should contain "Item" 16 | 17 | Scenarios: 18 | | search_by | 19 | | id | 20 | | class | 21 | | xpath | 22 | | index | 23 | | css | 24 | 25 | Scenario Outline: Locating unordered lists using multiple parameters 26 | When I search for the unordered list by "" and "" 27 | And I get the first item from the list 28 | Then the list items text should be "Item One" 29 | 30 | Scenarios: 31 | | param1 | param2 | 32 | | class | index | 33 | 34 | Scenario: Finding a unordered list dynamically 35 | When I search for the unordered list while the script is executing 36 | Then I should see that the unordered list exists 37 | When I get the first item from the list 38 | Then the list items text should be "Item One" 39 | 40 | Scenario: Getting the text from an unordered list 41 | Then the text for the unordered list should contain "Item One" 42 | And the text for the unordered list should contain "Item Two" 43 | And the text for the unordered list should contain "Item Three" 44 | -------------------------------------------------------------------------------- /features/radio_button.feature: -------------------------------------------------------------------------------- 1 | Feature: Radio Buttons 2 | In order to interact with radio buttons 3 | Testers will need access and interrogation ability 4 | 5 | 6 | Background: 7 | Given I am on the static elements page 8 | 9 | Scenario: Selecting and clearing a radio button 10 | When I select the "Milk" radio button 11 | Then the "Milk" radio button should be selected 12 | When I select the "Butter" radio button 13 | Then the "Butter" radio button should be selected 14 | 15 | Scenario Outline: Locating radio buttons on the Page 16 | When I search for the radio button by "" 17 | And I select the radio button 18 | Then the "Milk" radio button should be selected 19 | 20 | Scenarios: 21 | | search_by | 22 | | id | 23 | | class | 24 | | name | 25 | | xpath | 26 | | value | 27 | | index | 28 | | label | 29 | | css | 30 | 31 | Scenario Outline: Locating radio buttons using multiple parameters 32 | When I search for the radio button by "" and "" 33 | And I select the radio button 34 | Then the "Milk" radio button should be selected 35 | 36 | Scenarios: 37 | | param1 | param2 | 38 | | class | index | 39 | | name | index | 40 | 41 | Scenario: Retrieve a radio button 42 | When I retrieve a radio button 43 | Then I should know it exists 44 | And I should know it is visible 45 | 46 | Scenario: Finding a radio button dynamically 47 | When I select the radio button while the script is executing 48 | Then I should see that the radio button exists 49 | And the "Milk" radio button should be selected 50 | -------------------------------------------------------------------------------- /features/link.feature: -------------------------------------------------------------------------------- 1 | Feature: Links 2 | In order to interact with links 3 | Testers will need access and interrogation ability 4 | 5 | Background: 6 | Given I am on the static elements page 7 | 8 | Scenario: Selecting a link 9 | When I select the link labeled "Google Search" 10 | Then the page should contain the text "Success" 11 | 12 | Scenario Outline: Locating links on the Page 13 | When I search for the link by "" 14 | Then I should be able to select the link 15 | 16 | Scenarios: 17 | | search_by | 18 | | id | 19 | | class | 20 | | name | 21 | | xpath | 22 | | text | 23 | | index | 24 | | href | 25 | | css | 26 | | title | 27 | 28 | Scenario: Support for multiple parameters 29 | When I select a link labeled "Hello" and index "0" 30 | Then the page should contain the text "Success" 31 | Given I am on the static elements page 32 | When I select a link labeled "Hello" and index "1" 33 | Then the page should contain the text "Success" 34 | 35 | Scenario: Retrieve a Link 36 | When I retrieve a link element 37 | Then I should know it exists 38 | And I should know it is visible 39 | 40 | Scenario: Finding a link dynamically 41 | When I select a link while the script is executing 42 | And the page should contain the text "Success" 43 | 44 | Scenario: Getting the href for a link 45 | When I get the href for the link 46 | Then I should know it was "success.html" 47 | 48 | Scenario: Locating a link using a partial href 49 | When I get the link using the href success 50 | Then I should be able to click the link 51 | -------------------------------------------------------------------------------- /features/step_definitions/javascript_steps.rb: -------------------------------------------------------------------------------- 1 | class JavascriptPage 2 | include PageObject 3 | 4 | text_field(:expression, :id => 'calculator-expression') 5 | text_field(:results, :id => 'calculator-result') 6 | button(:compute, :value => 'Compute') 7 | 8 | end 9 | 10 | def build_url(page) 11 | target = ENV['BROWSER'] 12 | return "http://localhost:4567/#{page}" if target.nil? or target.include? 'local' 13 | "http://ec2-107-22-131-88.compute-1.amazonaws.com:4567/#{page}" 14 | end 15 | 16 | Given /^I am on jQuery example page$/ do 17 | PageObject.javascript_framework = :jquery 18 | @page = JavascriptPage.new(@browser) 19 | @page.navigate_to build_url("jquery.html") 20 | end 21 | 22 | Given /^I am on the Prototype example page$/ do 23 | PageObject.javascript_framework = :prototype 24 | @page = JavascriptPage.new(@browser) 25 | @page.navigate_to build_url('prototype.html') 26 | end 27 | 28 | When /^I ask to compute "([^\"]*)"$/ do |expression| 29 | @page.expression = expression 30 | @page.compute 31 | end 32 | 33 | Then /^I should be able to wait for the answer "([^\"]*)"$/ do |answer| 34 | @page.wait_for_ajax 35 | expect(@page.results).to eql answer 36 | end 37 | 38 | Given /^I execute the javascript "([^\"]*)"$/ do |script| 39 | @answer = @page.execute_script script 40 | end 41 | 42 | Given /^I execute the javascript "([^\"]*)" with an argument of "([^\"]*)"$/ do |script, arg| 43 | @answer = @page.execute_script script, arg 44 | end 45 | 46 | Given /^I execute the javascript "([^\"]*)" with a text field argument$/ do |script| 47 | text_field = @page.text_field_element(:id => 'text_field_id') 48 | @page.execute_script(script, text_field) 49 | end 50 | 51 | Then /^I should get the answer "([^\"]*)"$/ do |answer| 52 | expect(@answer).to eql answer.to_i 53 | end -------------------------------------------------------------------------------- /features/html/nested_elements.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Nested Elements 4 | 5 | 6 | 7 |
8 | Success 9 |
10 | 11 |
12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 |
24 | page-object rocks! 25 |
26 | 27 | My alert 28 | 29 | 32 | 33 | 34 |
Data1Data2
35 | 36 |

This is a paragraph.

37 | 38 |

h1's are cool

39 |

h2's are cool

40 |

h3's are cool

41 |

h4's are cool

42 |
h5's are cool
43 |
h6's are cool
44 | 45 |
    46 |
  1. Number One
  2. 47 |
  3. Number Two
  4. 48 |
49 |
    50 |
  • Item One
  • 51 |
  • Item Two
  • 52 |
53 | 54 |
55 | 56 |
    57 |
  • One
  • 58 |
  • Two
  • 59 |
      60 |
    • Nested One
    • 61 |
    • Nested Two
    • 62 |
    63 |
  • Three
  • 64 |
65 | 66 |
    67 |
  1. One
  2. 68 |
  3. Two
  4. 69 |
      70 |
    1. Nested One
    2. 71 |
    3. Nested Two
    4. 72 |
    73 |
  5. Three
  6. 74 |
75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /features/support/persistent_browser.rb: -------------------------------------------------------------------------------- 1 | require 'selenium/webdriver/remote/http/persistent' 2 | 3 | module PageObject 4 | module PersistentBrowser 5 | @@browser = false 6 | def self.get_browser 7 | if !@@browser 8 | target = ENV['BROWSER'] || 'local_chrome' 9 | 10 | if is_remote?(target) 11 | require File.dirname(__FILE__) + "/targets/#{target}" 12 | extend Target 13 | end 14 | 15 | @@browser = ENV['DRIVER'] == 'SELENIUM' ? selenium_browser(target) : watir_browser(target) 16 | end 17 | @@browser 18 | end 19 | 20 | def self.quit 21 | @@browser.quit 22 | end 23 | 24 | private 25 | 26 | def self.is_remote?(target) 27 | not target.include? 'local' 28 | end 29 | 30 | def self.watir_browser(target) 31 | if is_remote?(target) 32 | Watir::Browser.new(:remote, 33 | :url => url, 34 | :desired_capabilities => desired_capabilities) 35 | else 36 | browser = target.gsub('local_', '').to_sym 37 | Watir::Browser.new browser 38 | end 39 | end 40 | 41 | def self.selenium_browser(target) 42 | if is_remote?(target) 43 | Selenium::WebDriver.for(:remote, 44 | :url => url, 45 | :desired_capabilities => desired_capabilities) 46 | else 47 | browser = target.gsub('local_', '').to_sym 48 | Selenium::WebDriver.for browser 49 | end 50 | end 51 | 52 | def self.capabilities(browser, version, platform, name) 53 | caps = Selenium::WebDriver::Remote::Capabilities.send browser 54 | caps.version = version 55 | caps.platform = platform 56 | caps[:name] = name 57 | caps 58 | end 59 | 60 | def self.url 61 | "http://pageobject:18002ee8-963b-472e-9425-2baf0a2b6d95@ondemand.saucelabs.com:80/wd/hub" 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /features/text_field.feature: -------------------------------------------------------------------------------- 1 | Feature: Text Fields 2 | In order to interact with text fields 3 | Testers will need access and interrogation ability 4 | 5 | 6 | Background: 7 | Given I am on the static elements page 8 | 9 | Scenario: Setting and getting a value from a text field 10 | When I type "abcDEF" into the text field 11 | Then the text field should contain "abcDEF" 12 | 13 | Scenario Outline: Locating text fields on the Page 14 | When I search for the text field by "" 15 | Then I should be able to type "I found it" into the field 16 | 17 | Examples: 18 | | search_by | 19 | | id | 20 | | class | 21 | | name | 22 | | xpath | 23 | | index | 24 | | title | 25 | | label | 26 | | css | 27 | 28 | 29 | Scenario Outline: Locating text fields on the Page using Watir 30 | When I search for the text field by "" 31 | Then I should be able to type "I found it" into the field 32 | Examples: 33 | | search_by | 34 | | data_field | 35 | 36 | Scenario Outline: Locating a text field using multiple parameters 37 | When I search for the text field by "" and "" 38 | Then I should be able to type "I found it" into the field 39 | 40 | Examples: 41 | | param1 | param2 | 42 | | class | index | 43 | | name | index | 44 | 45 | Scenario: Retrieve a text field 46 | When I retrieve a text field 47 | Then I should know it exists 48 | And I should know it is visible 49 | 50 | Scenario: Finding a text field dynamically 51 | When I find a text field while the script is executing 52 | Then I should see that the text field exists 53 | And I should be able to type "I found it" into the field element 54 | 55 | Scenario: Appending text to a text field 56 | When I type "abcd" into the text field 57 | And I append "efg" to the text field 58 | Then the text field should contain "abcdefg" 59 | 60 | -------------------------------------------------------------------------------- /features/step_definitions/accessor_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^the current item should be "([^\"]*)"$/ do |expected_text| 2 | expect(@page.sel_list_id).to eql expected_text 3 | end 4 | 5 | Then /^the text should be "([^\"]*)"$/ do |expected_text| 6 | expect(@text.tr("\n", ' ')).to eql expected_text 7 | end 8 | 9 | Then /^the text should include "([^\"]*)"$/ do |expected_text| 10 | expect(@text).to include expected_text 11 | end 12 | 13 | Then /^I should be on the success page$/ do 14 | expect(@page.text).to include 'Success' 15 | expect(@page.title).to eql 'Success' 16 | end 17 | 18 | Then /^the list items text should be "([^\"]*)"$/ do |expected_text| 19 | expect(@element.text).to eql expected_text 20 | end 21 | 22 | When /^I get the first item from the list$/ do 23 | @element = @list[0] 24 | end 25 | 26 | Then /^the table should have "([^\"]*)" rows$/ do |rows| 27 | expect(@element.rows).to eql rows.to_i 28 | end 29 | 30 | Then /^each row should contain "([^\"]*)"$/ do |text| 31 | @element.each do |row| 32 | expect(row.text).to include text 33 | end 34 | end 35 | 36 | Then /^row "([^\"]*)" should have "([^\"]*)" columns$/ do |row, cols| 37 | expect(@element[row.to_i - 1].columns).to eql cols.to_i 38 | end 39 | 40 | Then /^each column should contain "([^\"]*)"$/ do |text| 41 | row = @element[0] 42 | row.each do |column| 43 | expect(column.text).to include text 44 | end 45 | end 46 | 47 | Then /^the list should contain (\d+) items$/ do |items| 48 | expect(@list.items).to eql items.to_i 49 | end 50 | 51 | Then /^each item should contain "([^\"]*)"$/ do |text| 52 | @list.each { |item| expect(item.text).to include text } 53 | end 54 | 55 | Then /^option "([^\"]*)" should contain "([^\"]*)"$/ do |opt_num, text| 56 | @element = @page.send "sel_list_#{@how}_element".to_sym 57 | expect(@element[opt_num.to_i - 1].text).to eql text 58 | end 59 | 60 | Then /^each option should contain "([^\"]*)"$/ do |text| 61 | @element.options.each do |option| 62 | expect(option.text).to include text 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/page-object/elements.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class << self 4 | 5 | # 6 | # method to return the collection of tag_name to class mappings 7 | # 8 | def tag_to_class 9 | @tag_to_class ||= {} 10 | end 11 | 12 | def type_to_class 13 | @type_to_class ||= {} 14 | end 15 | 16 | # 17 | # method to return the element for a tag_name 18 | # 19 | def element_class_for(tag_name, type=nil) 20 | return type_to_class[type.to_sym] if type 21 | tag_to_class[tag_name.to_sym] || ::PageObject::Elements::Element 22 | end 23 | 24 | end 25 | end 26 | end 27 | 28 | 29 | require 'page-object/elements/element' 30 | require 'page-object/elements/link' 31 | require 'page-object/elements/text_field' 32 | require 'page-object/elements/date_field' 33 | require 'page-object/elements/select_list' 34 | require 'page-object/elements/check_box' 35 | require 'page-object/elements/button' 36 | require 'page-object/elements/radio_button' 37 | require 'page-object/elements/div' 38 | require 'page-object/elements/table' 39 | require 'page-object/elements/table_cell' 40 | require 'page-object/elements/table_row' 41 | require 'page-object/elements/span' 42 | require 'page-object/elements/image' 43 | require 'page-object/elements/hidden_field' 44 | require 'page-object/elements/form' 45 | require 'page-object/elements/text_area' 46 | require 'page-object/elements/list_item' 47 | require 'page-object/elements/unordered_list' 48 | require 'page-object/elements/ordered_list' 49 | require 'page-object/elements/option' 50 | require 'page-object/elements/heading' 51 | require 'page-object/elements/paragraph' 52 | require 'page-object/elements/label' 53 | require 'page-object/elements/file_field' 54 | require 'page-object/elements/area' 55 | require 'page-object/elements/canvas' 56 | require 'page-object/elements/media' 57 | require 'page-object/elements/audio' 58 | require 'page-object/elements/video' 59 | require 'page-object/elements/bold' 60 | require 'page-object/elements/italic' 61 | 62 | 63 | -------------------------------------------------------------------------------- /spec/page-object/javascript_framework_facade_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class TestClass 4 | include PageObject::JavascriptFrameworkFacade 5 | end 6 | 7 | 8 | describe PageObject::JavascriptFrameworkFacade do 9 | let(:facade) { PageObject::JavascriptFrameworkFacade } 10 | 11 | it "should allow the selection of a javascript framework" do 12 | facade.framework = :jquery 13 | expect(facade.framework).to eql :jquery 14 | end 15 | 16 | it "should register the jQuery script builder" do 17 | facade.framework = :jquery 18 | expect(facade.script_builder).to eql ::PageObject::Javascript::JQuery 19 | end 20 | 21 | it "should return script for pending requests in jQuery" do 22 | facade.framework = :jquery 23 | expect(facade.pending_requests).to eql 'return jQuery.active' 24 | end 25 | 26 | it "should register the Prototype script builder" do 27 | facade.framework = :prototype 28 | expect(facade.script_builder).to eql ::PageObject::Javascript::Prototype 29 | end 30 | 31 | it "should return script for pending requests in Prototype" do 32 | facade.framework = :prototype 33 | expect(facade.pending_requests).to eql 'return Ajax.activeRequestCount' 34 | end 35 | 36 | it "should not allow you to set the framework to one it does not know about" do 37 | expect{ facade.framework = :blah }.to raise_error "You specified the Javascript framework blah and it is unknown to the system" 38 | end 39 | 40 | it "should allow you to add a new javascript framework" do 41 | module GoodFake 42 | def self.pending_requests 43 | "fake" 44 | end 45 | end 46 | 47 | facade.add_framework(:blah, GoodFake) 48 | facade.framework = :blah 49 | expect(facade.pending_requests).to eql "fake" 50 | end 51 | 52 | it "should not allow you to add a new javascript framework that is invalid" do 53 | module BadFake 54 | def self.blah 55 | end 56 | end 57 | 58 | expect{ facade.add_framework(:blah, BadFake) }.to raise_error "The Javascript framework you provided does not implement the necessary methods" 59 | end 60 | end 61 | 62 | -------------------------------------------------------------------------------- /features/audio.feature: -------------------------------------------------------------------------------- 1 | Feature: Support for the audio element 2 | 3 | Background: 4 | Given I am on the audio video page 5 | 6 | Scenario: finding an audio element 7 | When I retrieve the audio element from the page 8 | Then I should know it exists 9 | And I should know it is visible 10 | 11 | Scenario Outline: Locating an audio element on the page 12 | When I search for the audio element by "" 13 | Then I should know it is visible 14 | 15 | Examples: 16 | | search_by | 17 | | id | 18 | | class | 19 | | xpath | 20 | | index | 21 | | css | 22 | 23 | Scenario Outline: Locating audios using multiple parameters 24 | When I search for the audio element by "" and "" 25 | Then I should know it is visible 26 | 27 | Examples: 28 | | param1 | param2 | 29 | | class | index | 30 | 31 | Scenario: Should know if it is autoplay 32 | When I retrieve the audio element from the page 33 | Then I should know the audio is not autoplay 34 | 35 | Scenario: Should know if the controls are displayed 36 | When I retrieve the audio element from the page 37 | Then I should know that the controls are displayed 38 | 39 | Scenario: Should know if it is paused 40 | When I retrieve the audio element from the page 41 | Then I should know that the audio is paused 42 | 43 | Scenario: Should know its volume 44 | When I retrieve the audio element from the page 45 | Then I should know that its volume is 1 46 | 47 | Scenario: Should know if it has ended 48 | When I retrieve the audio element from the page 49 | Then I should know that it has not ended 50 | 51 | Scenario: Should know if it is seeking 52 | When I retrieve the audio element from the page 53 | Then I should know that it is not seeking 54 | 55 | Scenario: Should know if it is in a loop 56 | When I retrieve the audio element from the page 57 | Then I should know that it is not in a loop 58 | 59 | Scenario: Should know if it is muted 60 | When I retrieve the audio element from the page 61 | Then I should know that it is muted 62 | 63 | -------------------------------------------------------------------------------- /features/step_definitions/async_steps.rb: -------------------------------------------------------------------------------- 1 | def success 2 | 3 | end 4 | 5 | Then /^I should be able to wait until it is present$/ do 6 | @element.when_present do 7 | success 8 | end 9 | end 10 | 11 | Then /^I should be able to wait until it is visible$/ do 12 | @element.when_visible do 13 | success 14 | end 15 | end 16 | 17 | Then /^I should be able to wait until it is not visible$/ do 18 | begin 19 | @element.when_not_visible do 20 | fail 21 | end 22 | rescue 23 | success 24 | end 25 | end 26 | 27 | Then /^I should be able to wait until a block returns true$/ do 28 | @element.wait_until do 29 | @element.visible? 30 | end 31 | end 32 | 33 | class AsyncPage 34 | include PageObject 35 | button(:target, :value => 'Target') 36 | button(:hide, :value => 'Hide Button') 37 | button(:unhide, :value => 'Unhide Button') 38 | button(:create_button, :value => 'Create Button') 39 | button(:remove_button, :value => 'Remove Button') 40 | button(:created_button, :value => 'New Button') 41 | end 42 | 43 | Given /^I am on the async elements page$/ do 44 | @page = AsyncPage.new(@browser) 45 | @page.navigate_to(UrlHelper.async) 46 | end 47 | 48 | When /^I make the button invisible$/ do 49 | @page.hide 50 | sleep 2 51 | end 52 | 53 | Then /^I should be able to click it when it becomses visible$/ do 54 | @page.unhide 55 | @page.target_element.when_visible do 56 | @page.target 57 | end 58 | end 59 | 60 | Then /^I should be able to wait until the button becomes invisible$/ do 61 | @page.hide 62 | @page.target_element.when_not_visible do 63 | expect(@page.target_element.attribute("block")).to eql "none" 64 | end 65 | end 66 | 67 | When /^I add a button a few seconds from now$/ do 68 | @page.create_button 69 | end 70 | 71 | Then /^I should be able to click it when it gets added$/ do 72 | @page.created_button_element.when_present.click 73 | end 74 | 75 | When /^I remove a button a few seconds from now$/ do 76 | @page.created_button_element.when_present 77 | @page.remove_button 78 | end 79 | 80 | Then /^I should not be able to find the button$/ do 81 | @page.created_button_element.when_not_present 82 | expect(@page.created_button_element.exists?).to be false 83 | end 84 | -------------------------------------------------------------------------------- /features/video.feature: -------------------------------------------------------------------------------- 1 | Feature: Support for video element 2 | 3 | Background: 4 | Given I am on the audio video page 5 | 6 | Scenario: finding an video element 7 | When I retrieve the video element 8 | Then I should know it exists 9 | And I should know it is visible 10 | 11 | Scenario Outline: Locating a video element on the page 12 | When I search for the video element by "" 13 | Then I should know it is visible 14 | 15 | Examples: 16 | | search_by | 17 | | id | 18 | | class | 19 | | xpath | 20 | | index | 21 | | css | 22 | 23 | Scenario Outline: Locating videos using multiple parameters 24 | When I search for the video element by "" and "" 25 | Then I should know it is visible 26 | 27 | Examples: 28 | | param1 | param2 | 29 | | class | index | 30 | 31 | Scenario: Should know if it is autoplay 32 | When I retrieve the video element 33 | Then I should know the video is not autoplay 34 | 35 | Scenario: Should know if the controls are displayed 36 | When I retrieve the video element 37 | Then I should know that the controls are displayed 38 | 39 | Scenario: Should know if it is paused 40 | When I retrieve the video element 41 | Then I should know that the video is paused 42 | 43 | Scenario: Should know its volume 44 | When I retrieve the video element 45 | Then I should know that its volume is 1 46 | 47 | Scenario: Should know its height and width 48 | When I retrieve the video element 49 | Then I should know that its height is 240 pixels 50 | And I should knot what its width is 320 pixels 51 | 52 | Scenario: Should know if it has ended 53 | When I retrieve the video element 54 | Then I should know that it has not ended 55 | 56 | Scenario: Should know if it is seeking 57 | When I retrieve the video element 58 | Then I should know that it is not seeking 59 | 60 | Scenario: Should know if it is in a loop 61 | When I retrieve the video element 62 | Then I should know that it is not in a loop 63 | 64 | Scenario: Should know if it is muted 65 | When I retrieve the video element 66 | Then I should know that it is muted 67 | 68 | -------------------------------------------------------------------------------- /features/step_definitions/select_list_steps.rb: -------------------------------------------------------------------------------- 1 | When /^I select "([^\"]*)" from the select list$/ do |text| 2 | @page.sel_list_id = text 3 | end 4 | 5 | When /^I search for the select list by "([^\"]*)"$/ do |how| 6 | @how = how 7 | end 8 | 9 | When /^I search for the select list by "([^"]*)" and "([^"]*)"$/ do |param1, param2| 10 | @how = "#{param1}_#{param2}" 11 | end 12 | 13 | Then /^I should be able to select "([^\"]*)"$/ do |value| 14 | @page.send "sel_list_#{@how}=".to_sym, value 15 | end 16 | 17 | Then /^the value for the selected item should be "([^\"]*)"$/ do |value| 18 | result = @page.send "sel_list_#{@how}".to_sym 19 | expect(result).to eql value 20 | end 21 | 22 | When /^I find a select list while the script is executing$/ do 23 | @select_list = @page.select_list_element(:id => 'sel_list_id') 24 | end 25 | 26 | Then /^I should be able to select "([^\"]*)" from the list$/ do |value| 27 | @select_list.select(value) 28 | end 29 | 30 | Then /^I should see that the select list exists$/ do 31 | expect(@page.sel_list_id?).to eql true 32 | end 33 | 34 | Then /^the selected option should be "([^\"]*)"$/ do |text| 35 | expect(@page.select_list_element(:id => 'sel_list_id').selected_options[0]).to eql text 36 | end 37 | 38 | Then /^the select list should include "([^\"]*)"$/ do |text| 39 | expect(@page.sel_list_id_element).to include text 40 | end 41 | 42 | Then /^the select list should know that "([^\"]*)" is selected$/ do |text| 43 | expect(@page.sel_list_id_element.selected?(text)).to be true 44 | end 45 | 46 | Then /^the value for the option should be "([^\"]*)"$/ do |value| 47 | element = @page.send "sel_list_#{@how}_element".to_sym 48 | expect(element.value).to eql value 49 | end 50 | 51 | When /^I clear multiple select list$/ do 52 | @page.sel_list_multiple_element.clear 53 | end 54 | 55 | Then /^multiple select list should have no selected options$/ do 56 | expect(@page.sel_list_multiple_element.selected_options).to be_empty 57 | end 58 | 59 | When /^I select an option using the value "([^\"]*)"$/ do |value| 60 | @page.sel_list_id_element.select_value(value) 61 | end 62 | 63 | Then /^the selected option should have a value of "([^\"]*)"$/ do |value| 64 | expect(@page.sel_list_id_element.selected_values[0]).to eql value 65 | end 66 | 67 | 68 | And(/^cleared multiple select list should return nil for value$/) do 69 | expect(@page.sel_list_multiple).to be nil 70 | end -------------------------------------------------------------------------------- /features/headings.feature: -------------------------------------------------------------------------------- 1 | Feature: Headings 2 | 3 | Background: 4 | Given I am on the static elements page 5 | 6 | Scenario: Getting the text of headings 7 | When I get the text for the "h1" element 8 | Then I should see "h1's are cool" 9 | When I get the text for the "h2" element 10 | Then I should see "h2's are cool" 11 | When I get the text for the "h3" element 12 | Then I should see "h3's are cool" 13 | When I get the text for the "h4" element 14 | Then I should see "h4's are cool" 15 | When I get the text for the "h5" element 16 | Then I should see "h5's are cool" 17 | When I get the text for the "h6" element 18 | Then I should see "h6's are cool" 19 | 20 | Scenario Outline: Locating h1s on the Page 21 | When I search for the heading1 by "" 22 | Then I should see "h1's are cool" 23 | 24 | Scenarios: 25 | | search_by | 26 | | id | 27 | | class | 28 | | xpath | 29 | | index | 30 | | css | 31 | 32 | Scenario Outline: Locating h2s on the Page 33 | When I search for the heading2 by "" 34 | Then I should see "h2's are cool" 35 | 36 | Scenarios: 37 | | search_by | 38 | | id | 39 | | class | 40 | | xpath | 41 | | index | 42 | | css | 43 | 44 | Scenario Outline: Locating h3s on the Page 45 | When I search for the heading3 by "" 46 | Then I should see "h3's are cool" 47 | 48 | Scenarios: 49 | | search_by | 50 | | id | 51 | | class | 52 | | xpath | 53 | | index | 54 | | css | 55 | 56 | Scenario Outline: Locating h4s on the Page 57 | When I search for the heading4 by "" 58 | Then I should see "h4's are cool" 59 | 60 | Scenarios: 61 | | search_by | 62 | | id | 63 | | class | 64 | | xpath | 65 | | index | 66 | | css | 67 | 68 | Scenario Outline: Locating h5s on the Page 69 | When I search for the heading5 by "" 70 | Then I should see "h5's are cool" 71 | 72 | Scenarios: 73 | | search_by | 74 | | id | 75 | | class | 76 | | xpath | 77 | | index | 78 | | css | 79 | 80 | Scenario Outline: Locating h6s on the Page 81 | When I search for the heading6 by "" 82 | Then I should see "h6's are cool" 83 | 84 | Scenarios: 85 | | search_by | 86 | | id | 87 | | class | 88 | | xpath | 89 | | index | 90 | | css | 91 | 92 | -------------------------------------------------------------------------------- /lib/page-object/elements/table.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module Elements 3 | class Table < Element 4 | include Enumerable 5 | 6 | # 7 | # iterator that yields with a PageObject::Elements::TableRow 8 | # 9 | # @return [PageObject::Elements::TableRow] 10 | # 11 | def each(&block) 12 | row_items.each(&block) 13 | end 14 | 15 | alias_method :first_row, :first 16 | 17 | # 18 | # return the last row 19 | # 20 | # @return PageObject::Elements::TableRow 21 | # 22 | def last_row 23 | self[-1] 24 | end 25 | 26 | # 27 | # Return the PageObject::Elements::TableRow for the index provided. Index 28 | # is zero based. If the index provided is a String then it 29 | # will be matched with the text from any column. The text can be a substring of the full column text. 30 | # 31 | # @return [PageObject::Elements::TableRow] 32 | # 33 | def [](what) 34 | idx = find_index(what) 35 | idx && row_items[idx] 36 | end 37 | 38 | # 39 | # Returns the number of rows in the table. 40 | # 41 | def rows 42 | row_items.size 43 | end 44 | 45 | # 46 | # Returns the Array of values(String) in a column for the index provided. Index 47 | # is zero based. If the index provided is a String then it 48 | # will be matched with the text from the header. The text can be a substring of the full header text. 49 | # 50 | def column_values(what) 51 | idx = find_index_of_header(what) 52 | idx && row_items.drop(1).collect { |row| row.cell(index: idx).text } 53 | end 54 | 55 | protected 56 | 57 | def row_items 58 | meth = strategy == :descendants ? :trs : :rows 59 | @row_items ||= element.send(meth).map do |obj| 60 | ::PageObject::Elements::TableRow.new(obj) 61 | end 62 | end 63 | 64 | def strategy 65 | :children 66 | end 67 | 68 | def find_index_of_header(what) 69 | return what if what.is_a? Integer 70 | row_items[0].cells.find_index do |cell| 71 | cell.text.include? Regexp.escape(what) 72 | end 73 | end 74 | 75 | def find_index(what) 76 | return what if what.is_a? Integer 77 | row_items.find_index do |row| 78 | row.cell(text: /#{Regexp.escape(what)}/).exist? 79 | end 80 | end 81 | end 82 | 83 | ::PageObject::Elements.tag_to_class[:table] = ::PageObject::Elements::Table 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /spec/page-object/page_section_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | class Container 5 | include PageObject 6 | end 7 | class SectionsPage 8 | include PageObject 9 | 10 | page_section(:container, Container, :id => 'blah') 11 | page_sections(:containers, Container, :class => 'foo') 12 | end 13 | 14 | describe PageObject::Accessors do 15 | context 'when using watir' do 16 | let(:watir_browser) { mock_watir_browser } 17 | let(:watir_page_object) { SectionsPage.new(watir_browser) } 18 | 19 | it 'it should find a page section' do 20 | expect(watir_browser).to receive(:element).with(:id => 'blah').and_return(watir_browser) 21 | section = watir_page_object.container 22 | expect(section).to be_instance_of Container 23 | end 24 | it 'it should find page sections' do 25 | expect(watir_browser).to receive(:elements).with(:class => 'foo').and_return([watir_browser, watir_browser]) 26 | sections = watir_page_object.containers 27 | expect(sections).to be_instance_of(PageObject::SectionCollection) 28 | sections.each do |section| 29 | expect(section).to be_instance_of(Container) 30 | end 31 | end 32 | end 33 | end 34 | 35 | describe PageObject::SectionCollection do 36 | ContainedItem = Struct.new(:type, :name) 37 | let(:section_collection) do 38 | contained_items = [ContainedItem.new(:sandwich, :reuben), ContainedItem.new(:soup, :lobster_bisque), ContainedItem.new(:sandwich, :dagwood)] 39 | PageObject::SectionCollection[*contained_items] 40 | end 41 | it 'should inherit from Array' do 42 | expect(PageObject::SectionCollection.superclass).to eq Array 43 | end 44 | it 'should have functioning array methods' do 45 | expect(section_collection.methods).to include *Array.instance_methods 46 | expect(section_collection.last.type).to eq :sandwich 47 | end 48 | it 'should be indexed to the sections' do 49 | expect(section_collection[0]).to be_an_instance_of ContainedItem 50 | expect(section_collection[-1]).to be_an_instance_of ContainedItem 51 | end 52 | it 'should be able to iterate over the sections' do 53 | section_collection.each do |section| 54 | expect(section).to be_an_instance_of ContainedItem 55 | end 56 | end 57 | it 'should find a section by one of its values' do 58 | expect(section_collection.find_by(name: :dagwood).name).to eq :dagwood 59 | end 60 | it 'should find all sections matching a value' do 61 | expect(section_collection.select_by(type: :sandwich).map(&:type)).to eq [:sandwich, :sandwich] 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/page-object/elements/select_list_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | describe PageObject::Elements::SelectList do 5 | let(:selectlist) { PageObject::Elements::SelectList } 6 | 7 | describe "interface" do 8 | let(:sel_list) { double('select_list') } 9 | let(:opts) { [sel_list, sel_list] } 10 | 11 | before(:each) do 12 | allow(sel_list).to receive(:find_elements).and_return(sel_list) 13 | allow(sel_list).to receive(:each) 14 | allow(sel_list).to receive(:wd).and_return(sel_list) 15 | allow(sel_list).to receive(:map).and_return(opts) 16 | allow(sel_list).to receive(:any?) 17 | allow(sel_list).to receive(:include?) 18 | allow(sel_list).to receive(:select).and_return(opts) 19 | allow(sel_list).to receive(:text).and_return('blah') 20 | end 21 | 22 | it "should register with tag_name :select" do 23 | expect(::PageObject::Elements.element_class_for(:select)).to eql ::PageObject::Elements::SelectList 24 | end 25 | 26 | context "for watir" do 27 | let(:watir_sel_list) { PageObject::Elements::SelectList.new(sel_list) } 28 | 29 | it "should return an option when indexed" do 30 | expect(sel_list).to receive(:options).with(no_args).and_return(opts) 31 | expect(watir_sel_list[0]).to be_instance_of PageObject::Elements::Option 32 | end 33 | 34 | it "should return an array of options" do 35 | expect(sel_list).to receive(:options).with(no_args).and_return(opts) 36 | expect(watir_sel_list.options.size).to eql 2 37 | end 38 | 39 | it "should return an array of selected options" do 40 | allow(sel_list).to receive(:selected_options).and_return(opts) 41 | allow(sel_list).to receive(:text).and_return(sel_list) 42 | expect(watir_sel_list.selected_options).to eql opts 43 | end 44 | 45 | it "should know if it includes some value" do 46 | allow(sel_list).to receive(:include?).with('blah').and_return(true) 47 | watir_sel_list.include?('blah') 48 | end 49 | 50 | it "should know if an option is selected" do 51 | allow(sel_list).to receive(:selected?).with('blah').and_return(true) 52 | watir_sel_list.selected?('blah') 53 | end 54 | 55 | it "should be able to get the value for the selected options" do 56 | allow(sel_list).to receive(:selected_options).and_return(opts) 57 | allow(sel_list).to receive(:value).and_return(sel_list) 58 | expect(watir_sel_list.selected_values).to eql opts 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/page-object/javascript_framework_facade.rb: -------------------------------------------------------------------------------- 1 | require 'page-object/javascript/jquery' 2 | require 'page-object/javascript/prototype' 3 | require 'page-object/javascript/yui' 4 | require 'page-object/javascript/angularjs' 5 | 6 | 7 | module PageObject 8 | # 9 | # Provide hooks into different common Javascript Frameworks. 10 | # Currently this module only supports jQuery and Prototype but it 11 | # has the ability for you to plug your own framework into it and 12 | # therefore have it work with this gem. You do this by calling the 13 | # #add_framework method. The module you provide must implement the 14 | # necessary methods. Please look at the jQuery or Prototype 15 | # implementations to determine the necessary methods 16 | # 17 | module JavascriptFrameworkFacade 18 | 19 | class << self 20 | # 21 | # Set the framework to use. 22 | # 23 | # @param[Symbol] the framework to use. :jquery, :prototype, :yui, 24 | # and :angularjs are supported 25 | # 26 | def framework=(framework) 27 | initialize_script_builder unless @builder 28 | raise unknown_framework(framework) unless @builder[framework] 29 | @framework = framework 30 | end 31 | 32 | # 33 | # Get the framework that will be used 34 | # 35 | def framework 36 | @framework 37 | end 38 | 39 | # 40 | # Add a framework and make it available to the system. 41 | # 42 | def add_framework(key, value) 43 | raise invalid_framework unless value.respond_to? :pending_requests 44 | initialize_script_builder unless @builder 45 | @builder[key] = value 46 | end 47 | 48 | # 49 | # get the javascript to determine number of pending requests 50 | # 51 | def pending_requests 52 | script_builder.pending_requests 53 | end 54 | 55 | def script_builder 56 | initialize_script_builder unless @builder 57 | @builder[@framework] 58 | end 59 | 60 | private 61 | 62 | def initialize_script_builder 63 | @builder = { 64 | :jquery => ::PageObject::Javascript::JQuery, 65 | :prototype => ::PageObject::Javascript::Prototype, 66 | :yui => ::PageObject::Javascript::YUI, 67 | :angularjs => ::PageObject::Javascript::AngularJS 68 | } 69 | end 70 | 71 | def unknown_framework(framework) 72 | "You specified the Javascript framework #{framework} and it is unknown to the system" 73 | end 74 | 75 | def invalid_framework 76 | "The Javascript framework you provided does not implement the necessary methods" 77 | end 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /features/button.feature: -------------------------------------------------------------------------------- 1 | Feature: Button 2 | In order to interact with buttons 3 | Testers will need access and interrogation ability 4 | 5 | 6 | Background: 7 | Given I am on the static elements page 8 | 9 | Scenario: Clicking a button (type=submit) 10 | When I click the button 11 | Then I should be on the success page 12 | 13 | Scenario: Clicking a button (type=image) 14 | When I click the button with type image 15 | Then I should be on the success page 16 | 17 | Scenario: Clicking an image button by src 18 | When I click the image button using src 19 | Then I should be on the success page 20 | 21 | Scenario: Clicking an image button by alt 22 | When I click the image button using alt 23 | Then I should be on the success page 24 | 25 | Scenario: Retrieve a button element 26 | When I retrieve a button element 27 | Then I should know it exists 28 | And I should know it is visible 29 | 30 | Scenario Outline: Locating buttons on the page 31 | When I search for the button by "" 32 | Then I should be able to click the button 33 | 34 | Scenarios: 35 | | search_by | 36 | | id | 37 | | class | 38 | | name | 39 | | xpath | 40 | | index | 41 | | value | 42 | | css | 43 | 44 | Scenario Outline: Locating real buttons on the page 45 | When I search for the button by "" 46 | Then I should be able to click the real button 47 | 48 | Scenarios: 49 | | search_by | 50 | | id | 51 | | class | 52 | | name | 53 | | index | 54 | | value | 55 | 56 | Scenario Outline: Locating buttons on Watir only 57 | When I search for the button by "" 58 | Then I should be able to click the button 59 | 60 | Scenarios: 61 | | search_by | 62 | | text | 63 | 64 | Scenario Outline: Getting the text from a button 65 | When I get the text for a button that is a "" 66 | Then the text should be "This button is a " 67 | 68 | Scenarios: 69 | | tag | 70 | | button | 71 | | input | 72 | 73 | Scenario Outline: Locating button using multiple parameters 74 | When I search for the button by "" and "" 75 | Then I should be able to click the button 76 | 77 | Scenarios: 78 | | param1 | param2 | 79 | | class | index | 80 | | name | index | 81 | 82 | Scenario Outline: Locating real button using multiple parameters 83 | When I search for the button by "" and "" 84 | Then I should be able to click the real button 85 | 86 | Scenarios: 87 | | param1 | param2 | 88 | | class | index | 89 | | name | index | 90 | 91 | 92 | Scenario: Finding a button dynamically 93 | When I find a button while the script is executing 94 | Then I should see that the button exists 95 | And I should be able to click the button element 96 | 97 | -------------------------------------------------------------------------------- /features/select_list.feature: -------------------------------------------------------------------------------- 1 | Feature: Select List 2 | In order to interact with select lists 3 | Testers will need access and interrogation ability 4 | 5 | 6 | Background: 7 | Given I am on the static elements page 8 | 9 | Scenario: Selecting an element on the select list 10 | When I select "Test 2" from the select list 11 | Then the current item should be "Test 2" 12 | 13 | Scenario: Selecting an element when there is a forward slash 14 | When I select "Test/Test 3" from the select list 15 | Then the current item should be "Test/Test 3" 16 | 17 | Scenario Outline: Locating select lists on the Page 18 | When I search for the select list by "" 19 | Then I should be able to select "Test 2" 20 | And the value for the selected item should be "Test 2" 21 | And the value for the option should be "option2" 22 | 23 | Scenarios: 24 | | search_by | 25 | | id | 26 | | class | 27 | | name | 28 | | xpath | 29 | | index | 30 | | label | 31 | | css | 32 | 33 | Scenario Outline: Locating a select list using multiple parameters 34 | When I search for the select list by "" and "" 35 | Then I should be able to select "Test 2" 36 | And the value for the selected item should be "Test 2" 37 | And the value for the option should be "option2" 38 | 39 | Scenarios: 40 | | param1 | param2 | 41 | | class | index | 42 | | name | index | 43 | 44 | Scenario: Iterating through the options in the select list 45 | When I search for the select list by "id" 46 | Then option "1" should contain "Test 1" 47 | And option "2" should contain "Test 2" 48 | And each option should contain "Test" 49 | 50 | Scenario: Finding a select list dynamically 51 | When I find a select list while the script is executing 52 | Then I should see that the select list exists 53 | And I should be able to select "Test 2" from the list 54 | 55 | Scenario: Getting the selected option 56 | When I select "Test 2" from the select list 57 | Then the selected option should be "Test 2" 58 | 59 | Scenario: Determining if a select list includes some option 60 | Then the select list should include "Test 2" 61 | 62 | Scenario: It should know if an option is selected 63 | When I select "Test 2" from the select list 64 | Then the select list should know that "Test 2" is selected 65 | 66 | Scenario: Clearing multiple select list 67 | When I clear multiple select list 68 | Then multiple select list should have no selected options 69 | And cleared multiple select list should return nil for value 70 | 71 | Scenario: Selecting an option by its value 72 | When I select an option using the value "option2" 73 | Then the selected option should be "Test 2" 74 | 75 | Scenario: Getting the value from a selected option 76 | When I select an option using the value "option2" 77 | Then the selected option should have a value of "option2" 78 | -------------------------------------------------------------------------------- /features/step_definitions/table_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^the data for row "([^\"]*)" should be "([^\"]*)" and "([^\"]*)"$/ do |row, col1, col2| 2 | row = (row.to_i - 1) if row.to_i > 0 3 | table_row = @element[row] 4 | expect(table_row[0].text).to eql col1 5 | expect(table_row[1].text).to eql col2 6 | end 7 | 8 | When /^I retrieve the data from the table cell$/ do 9 | @cell_data = @page.cell_id 10 | end 11 | 12 | Then /^the cell data should be '([^"]*)'$/ do |expected| 13 | expect(@cell_data).to eql expected 14 | end 15 | 16 | When /^I retrieve a table element by "([^\"]*)"$/ do |how| 17 | @element = @page.send "table_#{how}_element" 18 | end 19 | 20 | When /^I retrieve a table element by "([^\"]*)" and "([^\"]*)"$/ do |param1, param2| 21 | @element = @page.send "table_#{param1}_#{param2}_element" 22 | end 23 | 24 | When /^I retrieve a table element while the script is executing$/ do 25 | @element = @page.table_element(:id => 'table_id') 26 | end 27 | 28 | Then /^the data for the first row should be "([^\"]*)" and "([^\"]*)"$/ do |col1, col2| 29 | expect(@element.first_row[0].text).to eql col1 30 | expect(@element.first_row[1].text).to eql col2 31 | end 32 | 33 | Then /^the data for the second row should be "([^\"]*)" and "([^\"]*)"$/ do |col1, col2| 34 | expect(@element[1][0].text).to eql col1 35 | expect(@element[1][1].text).to eql col2 36 | end 37 | 38 | Then /^the data for the last row should be "([^\"]*)" and "([^\"]*)"$/ do |col1, col2| 39 | expect(@element.last_row[0].text).to eql col1 40 | expect(@element.last_row[1].text).to eql col2 41 | end 42 | 43 | Then /^I should see that the table exists$/ do 44 | expect(@page.table_id?).to be true 45 | end 46 | 47 | Then /^the data for column "([^\"]*)" and row "([^\"]*)" should be "([^\"]*)"$/ do |column, row, value| 48 | expect(@element[row.to_i - 1][column].text).to eql value 49 | end 50 | 51 | Then /^the data for row "([^\"]*)" and column "([^\"]*)" should be "([^\"]*)"$/ do |row, column, value| 52 | expect(@element[row][column].text).to eql value 53 | end 54 | 55 | Then /^the data for row "([^\"]*)" should be nil$/ do |row| 56 | expect(@element[row]).to be_nil 57 | end 58 | 59 | Then /^the data for row "([^\"]*)" and column "([^\"]*)" should be nil$/ do |row, column| 60 | expect(@element[row][column]).to be_nil 61 | end 62 | 63 | Then /^I should see the text includes "([^"]*)" when I retrieve it by "([^"]*)"$/ do |text, how| 64 | expect(@page.send("table_#{how}")).to include text 65 | end 66 | 67 | Then /^the table should be like the expected one$/ do |expected_table| 68 | expect((expected_table.diff!@element.hashes)).to be_nil 69 | end 70 | 71 | When(/^I ask for the column values for "([^"]*)"$/) do |header| 72 | @values = @page.table_id_element.column_values(header) 73 | end 74 | 75 | Then(/^I should receive:$/) do |table| 76 | expect(@values.size).to eql 2 77 | table.hashes.each do |hsh| 78 | expect(@values).to include hsh['values'] 79 | end 80 | end 81 | 82 | When(/^I ask for the column values for column (\d+)$/) do |index| 83 | @values = @page.table_id_element.column_values(index.to_i) 84 | end -------------------------------------------------------------------------------- /features/html/indexed_property.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Indexed Property Page 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
123!
BAD ELEMENT NAME
foo!
BAD ELEMENT NAME
12test!
BAD ELEMENT NAME
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
foo2!
bar!
47 | 48 |
49 |
50 |
51 | 52 |
53 |
54 | 55 | -------------------------------------------------------------------------------- /features/page_level_actions.feature: -------------------------------------------------------------------------------- 1 | Feature: Page level actions 2 | In order to act on pages from a web site 3 | Testers will need to use the page object to encapsulate access 4 | 5 | Background: 6 | Given I am on the static elements page 7 | 8 | 9 | Scenario: Getting the text from a web page 10 | Then the page should contain the text "Static Elements Page" 11 | 12 | Scenario: Getting the html from a web page 13 | Then the page should contain the html "Static Elements Page" 14 | 15 | Scenario: Getting the title from a web page 16 | Then the page should have the title "Static Elements Page" 17 | 18 | Scenario: Validating the page title 19 | Then the page should have the expected title 20 | 21 | Scenario: Validating the expected element 22 | Then the page should have the expected element 23 | 24 | Scenario: Validating that an expected element does not exist 25 | Then the page should not have the expected element 26 | 27 | Scenario: Waiting for something 28 | Then I should be able to wait for a block to return true 29 | 30 | Scenario: Handling alert popups 31 | When I handle the alert 32 | Then I should be able to get the alert's message 33 | 34 | Scenario: Handling possible alert popups 35 | When I handle the possible alert 36 | Then I should be able to verify the popup didn't have a message 37 | 38 | Scenario: Handling alert popups that reload the page 39 | When I handle the alert that reloads the page 40 | Then I should be able to get the alert's message 41 | 42 | Scenario: Handling confirm popups 43 | When I handle the confirm 44 | Then I should be able to get the confirm message 45 | 46 | Scenario: Handling possible confirm popups 47 | When I handle the possible confirm 48 | Then I should be able to verify the popup didn't have a message 49 | 50 | Scenario: Handling confirm popups that reload the page 51 | When I handle the confirm that reloads the page 52 | Then I should be able to get the confirm message 53 | 54 | Scenario: Handling prompt popups 55 | When I handle the prompt 56 | Then I should be able to get the message and default value 57 | 58 | Scenario: Handling possible prompt popups 59 | When I handle the possible prompt 60 | Then I should be able to verify the popup didn't have a message 61 | 62 | Scenario: Attach to window using title 63 | When I open a second window 64 | Then I should be able to attach to a page object using title 65 | 66 | Scenario: Attach to window using title with multiple windows 67 | When I open a second window 68 | When I open a third window 69 | Then I should be able to attach to a page object using title 70 | 71 | Scenario: Attach to window using url 72 | When I open a second window 73 | Then I should be able to attach to a page object using url 74 | 75 | Scenario: Attach to window using url with multiple windows 76 | When I open a second window 77 | When I open a third window 78 | Then I should be able to attach to a page object using url 79 | 80 | Scenario: Refreshing the page 81 | Then I should be able to refresh the page 82 | 83 | Scenario: Going back and forward 84 | When I select the link labeled "Google Search" 85 | Then the page should contain the text "Success" 86 | When I press the back button 87 | Then the page should contain the text "Static Elements Page" 88 | When I press the forward button 89 | Then the page should contain the text "Success" 90 | 91 | -------------------------------------------------------------------------------- /features/step_definitions/page_level_actions_steps.rb: -------------------------------------------------------------------------------- 1 | Then /^the page should contain the text "([^\"]*)"$/ do |text| 2 | expect(@page.text).to include text 3 | end 4 | 5 | Then /^the page should contain the html "([^\"]*)"$/ do |html| 6 | expect(@page.html).to include html 7 | end 8 | 9 | Then /^the page should have the title "([^\"]*)"$/ do |title| 10 | expect(@page.title).to include title 11 | end 12 | 13 | Then /^I should be able to wait for a block to return true$/ do 14 | @page.google_search_id 15 | @page.wait_until(10, "too long to display page") do 16 | @page.text.include? 'Success' 17 | end 18 | end 19 | 20 | When /^I handle the alert$/ do 21 | @msg = @page.alert do 22 | @page.alert_button 23 | end 24 | end 25 | 26 | When /^I handle the possible alert$/ do 27 | @msg = @page.alert do 28 | @page.alert_button_element.focus 29 | end 30 | end 31 | 32 | When /^I handle the alert that reloads the page$/ do 33 | @msg = @page.alert do 34 | @page.alert_button_that_reloads 35 | end 36 | end 37 | 38 | Then /^I should be able to get the alert\'s message$/ do 39 | expect(@msg).to eql "I am an alert" 40 | end 41 | 42 | Then /^I should be able to verify the popup didn\'t have a message$/ do 43 | expect(@msg).to be_nil 44 | end 45 | 46 | When /^I handle the confirm$/ do 47 | @msg = @page.confirm(true) do 48 | @page.confirm_button 49 | end 50 | end 51 | 52 | When /^I handle the possible confirm$/ do 53 | @msg = @page.confirm(true) do 54 | @page.confirm_button_element.focus 55 | end 56 | end 57 | 58 | When /^I handle the confirm that reloads the page$/ do 59 | @msg = @page.confirm(true) do 60 | @page.confirm_button_that_reloads 61 | end 62 | end 63 | 64 | Then /^I should be able to get the confirm message$/ do 65 | expect(@msg).to eql 'set the value' 66 | end 67 | 68 | When /^I handle the prompt$/ do 69 | @msg = @page.prompt("Cheezy") do 70 | @page.prompt_button 71 | end 72 | end 73 | 74 | When /^I handle the possible prompt$/ do 75 | @msg = @page.prompt("Cheezy") do 76 | @page.prompt_button_element.focus 77 | end 78 | end 79 | 80 | Then /^I should be able to get the message and default value$/ do 81 | expect(@msg[:message]).to eql "enter your name" 82 | expect(@msg[:default_value]).to eql 'John Doe' 83 | end 84 | 85 | When /^I open a second window$/ do 86 | @page.open_window 87 | end 88 | 89 | When /^I open a third window$/ do 90 | @page.open_another_window 91 | end 92 | 93 | class SecondPage 94 | include PageObject 95 | end 96 | 97 | Then /^I should be able to attach to a page object using title$/ do 98 | @second_page = SecondPage.new(@browser) 99 | @second_page.attach_to_window(:title => "Success") 100 | expect(@second_page.title).to eql "Success" 101 | end 102 | 103 | Then /^I should be able to attach to a page object using url$/ do 104 | @second_page = SecondPage.new(@browser) 105 | @second_page.attach_to_window(:url => "success.html") 106 | expect(@second_page.current_url).to include 'success.html' 107 | end 108 | 109 | Then /^I should be able to refresh the page$/ do 110 | @page.refresh 111 | end 112 | 113 | When /^I press the back button$/ do 114 | @page.back 115 | end 116 | 117 | When /^I press the forward button$/ do 118 | @page.forward 119 | end 120 | 121 | Then /^the page should have the expected title$/ do 122 | expect(@page).to have_expected_title 123 | end 124 | 125 | Then /^the page should have the expected element$/ do 126 | expect(@page).to have_expected_element 127 | end 128 | 129 | Then /^the page should not have the expected element$/ do 130 | class FakePage 131 | include PageObject 132 | expected_element :blah 133 | end 134 | expect(FakePage.new(@browser)).not_to have_expected_element 135 | end 136 | -------------------------------------------------------------------------------- /spec/page-object/elements/nested_element_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'page-object/elements' 3 | 4 | 5 | describe "Element with nested elements" do 6 | before(:each) do 7 | @watir_driver = instance_double('Watir::Element') 8 | @watir_element = PageObject::Elements::Element.new(@watir_driver) 9 | end 10 | 11 | it "should find nested links" do 12 | expect(@watir_driver).to receive(:link).with(:id => 'blah') 13 | @watir_element.link_element(:id => 'blah') 14 | end 15 | 16 | it "should find nested buttons" do 17 | expect(@watir_driver).to receive(:button).with(:id => 'blah') 18 | @watir_element.button_element(:id => 'blah') 19 | end 20 | 21 | it "should find nested text fields" do 22 | expect(@watir_driver).to receive(:text_field).with(:id => 'blah') 23 | @watir_element.text_field_element(:id => 'blah') 24 | end 25 | 26 | it "should find nested hidden fields" do 27 | expect(@watir_driver).to receive(:hidden) 28 | @watir_element.hidden_field_element 29 | end 30 | 31 | it "should find nested text areas" do 32 | expect(@watir_driver).to receive(:textarea) 33 | @watir_element.text_area_element 34 | end 35 | 36 | it "should find a nested select list" do 37 | expect(@watir_driver).to receive(:select_list) 38 | @watir_element.select_list_element 39 | end 40 | 41 | it "should find a nested checkbox" do 42 | expect(@watir_driver).to receive(:checkbox) 43 | @watir_element.checkbox_element 44 | end 45 | 46 | it "should find a nested radio button" do 47 | expect(@watir_driver).to receive(:radio) 48 | @watir_element.radio_button_element 49 | end 50 | 51 | it "should find a nested div" do 52 | expect(@watir_driver).to receive(:div) 53 | @watir_element.div_element 54 | end 55 | 56 | it "should find a nested span" do 57 | expect(@watir_driver).to receive(:span) 58 | @watir_element.span_element 59 | end 60 | 61 | it "should find a nested table" do 62 | expect(@watir_driver).to receive(:table) 63 | @watir_element.table_element 64 | end 65 | 66 | it "should find a nested cell" do 67 | expect(@watir_driver).to receive(:td) 68 | @watir_element.cell_element 69 | end 70 | 71 | it "should find a nested image" do 72 | expect(@watir_driver).to receive(:image) 73 | @watir_element.image_element 74 | end 75 | 76 | it "should find a nested form" do 77 | expect(@watir_driver).to receive(:form) 78 | @watir_element.form_element 79 | end 80 | 81 | it "should find a nested ordered list" do 82 | expect(@watir_driver).to receive(:ol) 83 | @watir_element.ordered_list_element 84 | end 85 | 86 | it "should find a nested unordered list" do 87 | expect(@watir_driver).to receive(:ul) 88 | @watir_element.unordered_list_element 89 | end 90 | 91 | it "should find a nested list item" do 92 | expect(@watir_driver).to receive(:li) 93 | @watir_element.list_item_element 94 | end 95 | 96 | it "should find a nested h1" do 97 | expect(@watir_driver).to receive(:h1) 98 | @watir_element.h1_element 99 | end 100 | 101 | it "should find a nested h2" do 102 | expect(@watir_driver).to receive(:h2) 103 | @watir_element.h2_element 104 | end 105 | 106 | it "should find a nested h3" do 107 | expect(@watir_driver).to receive(:h3) 108 | @watir_element.h3_element 109 | end 110 | 111 | it "should find a nested h4" do 112 | expect(@watir_driver).to receive(:h4) 113 | @watir_element.h4_element 114 | end 115 | 116 | it "should find a nested h5" do 117 | expect(@watir_driver).to receive(:h5) 118 | @watir_element.h5_element 119 | end 120 | 121 | it "should find a nested h6" do 122 | expect(@watir_driver).to receive(:h6) 123 | @watir_element.h6_element 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /lib/page-object/page_populator.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module PagePopulator 3 | 4 | # 5 | # This method will populate all matched page TextFields, 6 | # TextAreas, SelectLists, FileFields, Checkboxes, and Radio Buttons from the 7 | # Hash passed as an argument. The way it find an element is by 8 | # matching the Hash key to the name you provided when declaring 9 | # the element on your page. 10 | # 11 | # Checkbox and Radio Button values must be true or false. 12 | # 13 | # @example 14 | # class ExamplePage 15 | # include PageObject 16 | # 17 | # text_field(:username, :id => 'username_id') 18 | # checkbox(:active, :id => 'active_id') 19 | # end 20 | # 21 | # ... 22 | # 23 | # @browser = Watir::Browser.new :firefox 24 | # example_page = ExamplePage.new(@browser) 25 | # example_page.populate_page_with :username => 'a name', :active => true 26 | # 27 | # @param data [Hash] the data to use to populate this page. The key 28 | # can be either a string or a symbol. The value must be a string 29 | # for TextField, TextArea, SelectList, and FileField and must be true or 30 | # false for a Checkbox or RadioButton. 31 | # 32 | def populate_page_with(data) 33 | data.to_h.each do |key, value| 34 | populate_section(key, value) if value.respond_to?(:to_h) 35 | populate_value(self, key, value) 36 | end 37 | end 38 | 39 | private 40 | 41 | def populate_section(section, data) 42 | return unless self.respond_to? section 43 | data.to_h.each do |key, value| 44 | populate_value(self.send(section), key, value) 45 | end 46 | end 47 | 48 | def populate_value(receiver, key, value) 49 | populate_checkbox(receiver, key, value) if is_checkbox?(receiver, key) and is_enabled?(receiver, key) 50 | populate_radiobuttongroup(receiver, key, value) if is_radiobuttongroup?(receiver, key) 51 | populate_radiobutton(receiver, key, value) if is_radiobutton?(receiver, key) and is_enabled?(receiver, key) 52 | populate_select_list(receiver, key, value) if is_select_list?(receiver, key) 53 | populate_text(receiver, key, value) if is_text?(receiver, key) and is_enabled?(receiver, key) 54 | end 55 | 56 | def populate_text(receiver, key, value) 57 | receiver.send "#{key}=", value 58 | end 59 | 60 | def populate_checkbox(receiver, key, value) 61 | return receiver.send "check_#{key}" if value 62 | receiver.send "uncheck_#{key}" 63 | end 64 | 65 | def populate_radiobutton(receiver, key, value) 66 | receiver.send "select_#{key}" if value 67 | end 68 | 69 | def populate_radiobuttongroup(receiver, key, value) 70 | receiver.send("select_#{key}", value) 71 | end 72 | 73 | def populate_select_list(receiver, key, value) 74 | receiver.send "#{key}=", value 75 | end 76 | 77 | def is_text?(receiver, key) 78 | return false if is_select_list?(receiver, key) 79 | receiver.respond_to?("#{key}=".to_sym) 80 | end 81 | 82 | def is_checkbox?(receiver, key) 83 | receiver.respond_to?("check_#{key}".to_sym) 84 | end 85 | 86 | def is_radiobutton?(receiver, key) 87 | receiver.respond_to?("select_#{key}".to_sym) 88 | end 89 | 90 | def is_radiobuttongroup?(receiver, key) 91 | receiver.respond_to?("select_#{key}".to_sym) and receiver.respond_to?("#{key}_values") 92 | end 93 | 94 | def is_select_list?(receiver, key) 95 | receiver.respond_to?("#{key}_options".to_sym) 96 | end 97 | 98 | def is_enabled?(receiver, key) 99 | return false if is_radiobuttongroup?(receiver, key) 100 | return true if (receiver.send "#{key}_element").tag_name == "textarea" 101 | element = receiver.send("#{key}_element") 102 | element.enabled? and element.present? 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/page-object/locator_generator.rb: -------------------------------------------------------------------------------- 1 | module PageObject 2 | module LocatorGenerator 3 | 4 | BASIC_ELEMENTS = %i( 5 | abbr 6 | address 7 | animate 8 | animate_motion 9 | animate_transform 10 | article 11 | as 12 | aside 13 | base 14 | bdi 15 | bdo 16 | blockquote 17 | body 18 | br 19 | caption 20 | circle 21 | cite 22 | code 23 | col 24 | colgroup 25 | command 26 | cursor 27 | data 28 | datalist 29 | dd 30 | defs 31 | del 32 | desc 33 | details 34 | dfn 35 | dialog 36 | discard 37 | dl 38 | dt 39 | ellipse 40 | em 41 | embed 42 | fieldset 43 | figcaption 44 | figure 45 | font 46 | footer 47 | foreign_object 48 | g 49 | head 50 | header 51 | hgroup 52 | hr 53 | html 54 | ins 55 | kbd 56 | keygen 57 | legend 58 | line 59 | linear_gradient 60 | main 61 | map 62 | mark 63 | marker 64 | menu 65 | menuitem 66 | mesh_gradient 67 | mesh_patch 68 | mesh_row 69 | meta 70 | metadata 71 | meter 72 | mpath 73 | nav 74 | noscript 75 | object 76 | optgroup 77 | output 78 | p 79 | param 80 | path 81 | pattern 82 | polygon 83 | polyline 84 | pre 85 | progress 86 | q 87 | radial_gradient 88 | rect 89 | rp 90 | rt 91 | ruby 92 | s 93 | samp 94 | script 95 | section 96 | set 97 | small 98 | source 99 | stop 100 | strong 101 | style 102 | sub 103 | summary 104 | sup 105 | switch 106 | symbol 107 | template 108 | text_path 109 | thread 110 | time 111 | title 112 | track 113 | tspan 114 | u 115 | use 116 | var 117 | view 118 | wbr 119 | ) 120 | 121 | 122 | ADVANCED_ELEMENTS = %i( 123 | text_field 124 | date_field 125 | hidden_field 126 | text_area 127 | select_list 128 | link 129 | checkbox 130 | radio_button 131 | button 132 | div 133 | span 134 | table 135 | cell 136 | row 137 | image 138 | form 139 | list_item 140 | ordered_list 141 | unordered_list 142 | h1 143 | h2 144 | h3 145 | h4 146 | h5 147 | h6 148 | paragraph 149 | label 150 | file_field 151 | area 152 | canvas 153 | audio 154 | video 155 | b 156 | i 157 | svg 158 | ) 159 | 160 | def self.generate_locators(target) 161 | ADVANCED_ELEMENTS.each do |tag| 162 | target.send(:define_method, "#{tag.to_s}_element") do |*identifier| 163 | @platform.send "#{tag.to_s}_for", locator(identifier) 164 | end 165 | 166 | target.send(:define_method, "#{tag.to_s}_elements") do |*identifier| 167 | @platform.send("#{tag.to_s}s_for", identifier[0] ? identifier[0] : {}) 168 | end 169 | end 170 | 171 | BASIC_ELEMENTS.each do |tag| 172 | target.send(:define_method, "#{tag.to_s}_element") do |*identifier| 173 | @platform.send :element_for, tag, locator(identifier) 174 | end 175 | 176 | target.send(:define_method, "#{tag.to_s}_elements") do |*identifier| 177 | @platform.send(:elements_for, tag, identifier[0] ? identifier[0] : {}) 178 | end 179 | end 180 | end 181 | 182 | end 183 | end 184 | -------------------------------------------------------------------------------- /lib/page-object/widgets.rb: -------------------------------------------------------------------------------- 1 | require 'page-object/elements' 2 | require 'page-object/platforms/watir/page_object' 3 | 4 | module PageObject 5 | module Widgets 6 | 7 | # 8 | # Module that allows for the registration of widget classes which extend the functionality of PageObject 9 | # Allows any classes which extend PageObject::Element to be used as PageObject elements. 10 | # This allows such widgets to be created using the defined tags. 11 | # 12 | # @param [Symbol] defines the symbol which will be used as an accessor name. 13 | # @param [Class] the widget class extending PageObject::Elements::Element 14 | # @param [Symbol] the symbol of the html element used when searching for this widget. 15 | # 16 | # 17 | def self.register_widget(widget_tag, widget_class, base_element_tag) 18 | if widget_class.ancestors.include? Elements::Element 19 | define_accessors(Accessors, widget_tag, widget_class) 20 | define_nested_elements(Elements::Element, widget_tag) 21 | define_locators(PageObject, widget_tag) 22 | 23 | PageObject::Platforms::Watir::PageObject.define_widget_accessors(widget_tag, widget_class, base_element_tag) 24 | end 25 | end 26 | 27 | private 28 | 29 | def self.define_accessors(base, widget_tag, widget_class) 30 | accessors_module = Module.new do 31 | define_method widget_tag do |name, *identifier_args, &block| 32 | 33 | identifier = identifier_args.first 34 | identifier = {:index => 0} if identifier.nil? 35 | 36 | define_method("#{name}_element") do 37 | return call_block(&block) if block 38 | platform.send("#{widget_tag}_for", identifier.clone) 39 | end 40 | define_method("#{name}?") do 41 | return call_block(&block).exists? if block 42 | platform.send("#{widget_tag}_for", identifier.clone).exists? 43 | end 44 | if widget_class.respond_to? :accessor_methods 45 | widget_class.accessor_methods(self, name) 46 | end 47 | end 48 | define_method widget_class.plural_form do |name, *identifier_args, &block| 49 | define_method("#{name}_elements") do 50 | return call_block(&block) unless block.nil? 51 | platform_method = "#{widget_tag.to_s}s_for" 52 | platform.send platform_method, (identifier_args.first ? identifier_args.first.clone : {}) 53 | end 54 | end 55 | end 56 | 57 | base.send(:include, accessors_module) 58 | end 59 | 60 | def self.define_nested_elements(base, widget_tag) 61 | define_singular_nested_accessor(base, widget_tag) 62 | define_multiple_nested_accessor(base, widget_tag) 63 | end 64 | 65 | def self.define_multiple_nested_accessor(base, widget_tag) 66 | base.send(:define_method, "#{widget_tag}_elements") do |*args| 67 | identifier = args[0] ? args[0] : {} 68 | @platform.send("#{widget_tag}s_for", identifier.clone) 69 | end 70 | end 71 | 72 | def self.define_singular_nested_accessor(base, widget_tag) 73 | base.send(:define_method, "#{widget_tag}_element") do |*args| 74 | identifier = args[0] ? args[0] : {:index => 0} 75 | @platform.send("#{widget_tag}_for", identifier.clone) 76 | end 77 | end 78 | 79 | def self.define_locators(base, widget_tag) 80 | define_singular_locator(base, widget_tag) 81 | define_multiple_locator(base, widget_tag) 82 | end 83 | 84 | def self.define_multiple_locator(base, widget_tag) 85 | base.send(:define_method, "#{widget_tag}_elements") do |*args| 86 | identifier = args[0] ? args[0] : {} 87 | platform.send("#{widget_tag}s_for", identifier.clone) 88 | end 89 | end 90 | 91 | def self.define_singular_locator(base, widget_tag) 92 | base.send(:define_method, "#{widget_tag}_element") do |*args| 93 | identifier = args[0] ? args[0] : {:index => 0} 94 | platform.send("#{widget_tag}_for", identifier.clone) 95 | end 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /features/frames.feature: -------------------------------------------------------------------------------- 1 | Feature: Handling frames 2 | 3 | Scenario: Accessing elements within the frame 4 | Given I am on the frame elements page 5 | When I type "page-object" into the text field for frame 2 using "id" 6 | Then I should verify "page-object" is in the text field for frame 2 using "id" 7 | When I type "page-object" into the text field for frame 2 using "name" 8 | Then I should verify "page-object" is in the text field for frame 2 using "name" 9 | When I type "page-object" into the text field for frame 2 using "index" 10 | Then I should verify "page-object" is in the text field for frame 2 using "index" 11 | 12 | Scenario: Accessing elements within the frame using Regexp 13 | Given I am on the frame elements page 14 | When I type "page-object" into the text field for frame 2 using "regex" 15 | Then I should verify "page-object" is in the text field for frame 2 using "regex" 16 | 17 | Scenario: Accessing elements within the frame using multiple identifiers 18 | Given I am on the iframe elements page 19 | When I type "page-object" into the text field for frame 2 using "visible_true" 20 | Then I should verify "page-object" is in the text field for frame 2 using "visible_true" 21 | 22 | Scenario: Accessing elements within the frame using visible: true identifier 23 | Given I am on the iframe elements page 24 | When I type "page-object" into the text field for frame 2 using "multiple identifiers" 25 | Then I should verify "page-object" is in the text field for frame 2 using "multiple identifiers" 26 | 27 | Scenario: Switching between frames 28 | Given I am on the frame elements page 29 | When I type "page-object" into the text field for frame 2 using "id" 30 | And I type "page-object" into the text field from frame 1 using "id" 31 | Then I should verify "page-object" is in the text field for frame 2 using "id" 32 | And I should verify "page-object" is in the text field for frame 1 using "id" 33 | 34 | Scenario: Accessing elements within the frame 35 | Given I am on the iframe elements page 36 | When I type "page-object" into the text field for frame 2 using "id" 37 | Then I should verify "page-object" is in the text field for frame 2 using "id" 38 | When I type "page-object" into the text field for frame 2 using "name" 39 | Then I should verify "page-object" is in the text field for frame 2 using "name" 40 | When I type "page-object" into the text field for frame 2 using "index" 41 | Then I should verify "page-object" is in the text field for frame 2 using "index" 42 | 43 | Scenario: Switching between frames 44 | Given I am on the iframe elements page 45 | When I type "page-object" into the text field for frame 2 using "id" 46 | And I type "page-object" into the text field from frame 1 using "id" 47 | Then I should verify "page-object" is in the text field for frame 2 using "id" 48 | And I should verify "page-object" is in the text field for frame 1 using "id" 49 | 50 | Scenario: Nested frames 51 | Given I am on the nested frame elements page 52 | Then I should be able to click the link in the frame 53 | 54 | Scenario: Identifying items in frames at runtime 55 | Given I am on the frame elements page 56 | When I type "page-object" into the text field from frame 1 identified dynamically 57 | Then I should verify "page-object" in the text field for frame 1 identified dynamically 58 | 59 | Scenario: Identifying items in iframes at runtime 60 | Given I am on the iframe elements page 61 | When I type "page-object" into the text field from iframe 1 identified dynamically 62 | Then I should verify "page-object" in the text field for iframe 1 identified dynamically 63 | 64 | Scenario: Handling alerts inside frames 65 | Given I am on the frame elements page 66 | When I trigger an alert within a frame 67 | Then I should be able to get the alert's message 68 | 69 | Scenario: Handling confirms inside frames 70 | Given I am on the frame elements page 71 | When I trigger a confirm within a frame 72 | Then I should be able to get the confirm message 73 | 74 | Scenario: Handling prompts inside frames 75 | Given I am on the frame elements page 76 | When I trigger a prompt within a frame 77 | Then I should be able to get the message and default value 78 | 79 | Scenario: Frames in sections 80 | Given I am on the frame section page 81 | Then I should be able to access an element in the frame in the section repeatedly 82 | 83 | Scenario: Indexed Properties in frames 84 | Given I am on the frame elements page 85 | Then I can access elements in indexed properties -------------------------------------------------------------------------------- /lib/page-object/page_factory.rb: -------------------------------------------------------------------------------- 1 | require 'page_navigation' 2 | 3 | module PageObject 4 | # 5 | # Module to facilitate to creating of page objects in step definitions. You 6 | # can make the methods below available to all of your step definitions by adding 7 | # this module to World. This idea was first discussed in Alister Scott's blog 8 | # entry http://watirmelon.com/2011/06/07/removing-local-page-references-from-cucumber-steps/. 9 | # 10 | # @example Making the PageFactory available to your step definitions 11 | # World PageObject::PageFactory 12 | # 13 | # @example Visiting a page for the first time in a Scenario 14 | # visit_page MyPageObject do |page| 15 | # page.name = 'Cheezy' 16 | # end 17 | # 18 | # @example using a page that has already been visited in a Scenario 19 | # on_page MyPageObject do |page| 20 | # page.name.should == 'Cheezy' 21 | # end 22 | # 23 | # If you plan to use the navigate_to method you will need to ensure 24 | # you setup the possible routes ahead of time. You must always have 25 | # a default route in order for this to work. Here is an example of 26 | # how you define routes: 27 | # 28 | # @example Example routes defined in env.rb 29 | # PageObject::PageFactory.routes = { 30 | # :default => [[PageOne,:method1], [PageTwoA,:method2], [PageThree,:method3]], 31 | # :another_route => [[PageOne,:method1, "arg1"], [PageTwoB,:method2b], [PageThree,:method3]] 32 | # } 33 | # 34 | # Notice the first entry of :another_route is passing an argument 35 | # to the method. 36 | # 37 | module PageFactory 38 | include PageNavigation 39 | 40 | 41 | # 42 | # Create and navigate to a page object. The navigation will only work if the 43 | # 'page_url' method was call on the page object. 44 | # 45 | # @param [PageObject, String] a class that has included the 46 | # PageObject module or a string containing the name of the class 47 | # @param Hash values that is pass through to page class a 48 | # available in the @params instance variable. 49 | # @param an optional block to be called 50 | # @return [PageObject] the newly created page object 51 | # 52 | def visit_page(page_class, params={:using_params => {}}, &block) 53 | on_page page_class, params, true, &block 54 | end 55 | 56 | # Support 'visit' for readability of usage 57 | alias_method :visit, :visit_page 58 | 59 | # 60 | # Create a page object. 61 | # 62 | # @param [PageObject, String] a class that has included the PageObject module or a string containing the name of the class 63 | # @param Hash values that is pass through to page class a 64 | # available in the @params instance variable. 65 | # @param [Boolean] a boolean indicating if the page should be visited? default is false. 66 | # @param [block] an optional block to be called 67 | # @return [PageObject] the newly created page object 68 | # 69 | def on_page(page_class, params={:using_params => {}}, visit=false, &block) 70 | page_class = class_from_string(page_class) if page_class.is_a? String 71 | return super(page_class, params, visit, &block) unless page_class.ancestors.include? PageObject 72 | merged = page_class.params.merge(params[:using_params]) 73 | page_class.instance_variable_set("@merged_params", merged) unless merged.empty? 74 | @current_page = page_class.new(@browser, visit) 75 | block.call @current_page if block 76 | @current_page 77 | end 78 | 79 | # Support 'on' for readability of usage 80 | alias_method :on, :on_page 81 | 82 | # 83 | # Create a page object if and only if the current page is the same page to be created 84 | # 85 | # @param [PageObject, String] a class that has included the PageObject module or a string containing the name of the class 86 | # @param Hash values that is pass through to page class a 87 | # available in the @params instance variable. 88 | # @param [block] an optional block to be called 89 | # @return [PageObject] the newly created page object 90 | # 91 | def if_page(page_class, params={:using_params => {}},&block) 92 | page_class = class_from_string(page_class) if page_class.is_a? String 93 | return @current_page unless @current_page.class == page_class 94 | on_page(page_class, params, false, &block) 95 | end 96 | 97 | # Support 'if' for readability of usage 98 | alias_method :if, :if_page 99 | 100 | private 101 | 102 | def class_from_string(str) 103 | str.split('::').inject(Object) do |mod, class_name| 104 | mod.const_get(class_name) 105 | end 106 | end 107 | end 108 | end 109 | --------------------------------------------------------------------------------