Create page object framework using cucumber watir
My first ruby code was with Capybara and cucumber and I’m still coding on it with the help of site_prism to have a page object structure. Last couple of weeks I’ve spent some time with watir just from curiosity and because it uses Ruby, a full-featured modern scripting language, rather than a proprietary vendorscript. Here I will try to show to you guys how to get started with Cucumber Watir using page object model.
Cucumber watir setup:
**Install**
install ruby version manager ([RVM](http://rvm.io/)) before all other
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
\curl -sSL https://get.rvm.io | bash -s stable
To see all available ruby versions to install
rvm list known
Install appropriate ruby version from .ruby-version file if not installed yet
rvm install ruby-2.4.0
To see all installed rubies with showing of default and current version
rvm list rubies
Install bundler and app dependencies
gem install bundler
bundle install
**Running cucumber tests**
cucumber
To run a specific feature in a specific file specify the feature file and line number:
cucumber features/listings.feature:11
Or you can run the feature by name:
cucumber features --name "I am able to search on google"
Install the gem from rubygems.org:
$ gem install watir-webdriver
Building native extensions. This could take a while...
Successfully installed json_pure-1.4.6
Successfully installed rubyzip-0.9.4
Successfully installed ffi-0.6.3
Successfully installed childprocess-0.0.7
Successfully installed selenium-webdriver-0.0.29
Successfully installed watir-webdriver-0.1.1
6 gems installed
$
Cucumber watir project structure:
cucumber watir
Cucumber watir page object model code example:
Lets see what’s inside model >> page_object.rb
class PageObject
@@mapping = {}
# Little bit of Meta programming
def self.container(selector)
@@mapping[name] ||= {}
@@mapping[name][:container] = selector
end
def self.element(element_name, selector)
@@mapping[name] ||= {}
@@mapping[name][:elements] ||= {}
@@mapping[name][:elements][element_name] = selector
end
# Get container selector
# @return [String]
def self.container_selector
mapping[:container]
end
# Get element selector
# @return [String]
def self.element_selector(element_name)
mapping[:elements][element_name.downcase]
end
# Get mapping hash:
# full for PageObject class, and self for other
# @return [Hash]
def self.mapping
name == 'PageObject' ? @@mapping : @@mapping[name]
end
end
Lets create our object inside model >> object.rb
class Object
# Get component web element representation
def component(name)
Component.by_name(name)
end
# Get component's web element representation
def element(element_name, component_name = nil)
Component.by_element(element_name, component_name)
end
# Check if string is kind of xpath selector style
def xpath_selector?
is_a?(String) && (start_with?('//') || start_with?('.//'))
end
end
Lets handle our components :
class Component
def self.by_name(name)
component_selector = Object.const_get(name).container_selector
$b.element(css: component_selector)
end
def self.by_element(element_name, component_name = nil)
# Find correct element selector if component name not specified
if component_name.nil?
mapping = PageObject.mapping
.find_all { |_k, v| v[:elements][element_name.downcase] }
raise_exception(element_name) if mapping.empty?
# ToDo: For now it is used first element found in mapping
# What to do if found at least two appropriate values???
component_name = mapping[0][0]
end
element_selector = Object.const_get(component_name).element_selector(element_name.downcase)
# Check selector type: css or xpath
if element_selector.xpath_selector?
by_name(component_name).element(xpath: element_selector)
else
by_name(component_name).element(css: element_selector)
end
end
private
def self.raise_exception(element_name, component_name = nil)
raise "No elements found with name: '#{element_name}' & component: #{component_name}"
end
end
Your first cucumber watir page inside mapping >> pages >> main_page.rb
Your first cucumber waitr from inside mapping >> forms >> search_form.rb
Your first cucumber watir component class inside mapping >> components >> header_menu.rb
Create your first cucumber watir feature file inside features :
@examples
Feature: This is feature for framework testing purposes & examples
@ex1
Scenario: I am able to navigate to google
Given I navigate to the url google.com
And I click on the search button
Inside support folder we are going to initialise the project setup , environment variables , selenium driver , and provide the possibility to take screenshots
support >> _init.rb
require 'watir'
require 'fileutils'
require 'rspec/expectations'
# Require model
page_object_dir = File.expand_path('../../../model', __FILE__)
Dir.glob File.join(page_object_dir, 'model', '*.rb'), &method(:require)
# Require mapping classes
Dir.glob File.join(page_object_dir, 'mapping', '**', '*.rb'), &method(:require)
support >> browser.rb
$b = Watir::Browser.new ENV['BROWSER'].to_sym
# close the browser using ruby global hook
at_exit do
$b.close
end
support >> env.rb
# default browser to run
ENV['BROWSER'] = 'firefox'
ENV['PATH'] = ENV['PATH'] + ':' + File.expand_path('../../../bin', __FILE__)
support >> screenshot_maker.rb
SCREENSHOT_PATH = 'output/screenshots'
FileUtils.mkdir_p SCREENSHOT_PATH
After do |scenario|
if scenario.failed?
screenshot_name = "#{Time.now}_#{scenario.name}_#{scenario}".gsub(/\W/, '_') + '.png'
$b.screenshot.save "#{SCREENSHOT_PATH}/#{screenshot_name}"
# embed it in html report output
embed "#{SCREENSHOT_PATH}/#{screenshot_name}", 'image/png'
end
end
And we are almost done lets create the step definition inside features >> step_definitions for our cucumber watir project
Given /^I navigate to the url (.*)$/ do |url|
$b.goto url
end
Actions step definitions example :
##### Action steps context #####
Given /^I click on the (.+)$/ do |element_name|
element(element_name).click
end
Given /^I click on the (.+) for (.+)$/ do |element_name, component_name|
element(element_name, component_name).click
end
Given /^I enter "(.+)" on the (.+)$/ do |entered_text, element_name|
element(element_name).send_keys entered_text
end
Given /^I enter "(.+)" on the (.+) for (.+)$/ do |entered_text, element_name, component_name|
element(element_name, component_name).send_keys entered_text
end
Assertions context example :
##### Assertions context #####
Then /^Fail assert$/ do
expect(1).to eq 2
end
Then /^Success assert$/ do
expect(1).to eq 1
end
Here we go , you just managed to setup your first cucumber watir project and your are ready to extend it with as many page objects and components you want.
What I like about this structure is because gives you full reusability of the cucumber steps. I don’t like the fact that complex logic need to be handled separated of the reusable actions .
If you need more informations about setup you can find it here on the official watir page.
How to initialise the cucumber ruby project you can fallow the cucumber documentation .
Happy testing!
The post Create a page object framework with cucumber watir appeared first on TestingRepository.