lib/howitzer/utils/locator_store.rb
require 'howitzer/exceptions'
#The following are locator aliases:
#
#1) locator
#Type: :css(by default), :xpath
#Method example: find, all, first
#
#2) link_locator
#Type: id, text
#Method example: click_link, find_link
#
#3) button_locator
#Type: id, name, value
#Method example: click_button, find_button
#
#4) field_locator
#Type: name, id, text
#Method example: find_field, fill_in
module LocatorStore
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
LOCATOR_TYPES = [:base, :link, :field, :button]
##
#
# Adds css or xpath locator into LocatorStore.
# Also locator can be set by lambda expression. For example: lambda{|name|{xpath: ".//a[@id='#{name}']"}}
#
# *Parameters:*
# * +name+ - Locator name
# * +params+ - String for css locator or hash with :xpath key and string value for xpath locator
#
def add_locator(name, params)
add_locator_by_type(:base, name, params)
end
##
#
# Adds locator for link into LocatorStore. Link can be found by: id, text
# Capybara methods that can work with this locator type are: click_link, find_link
#
# *Parameters:*
# * +name+ - Locator name
# * +params+ - ID or text of link
#
def add_link_locator(name, params)
add_locator_by_type(:link, name, params)
end
##
#
# Adds locator for field into LocatorStore. Field can be found by: name, id, text
# Capybara methods that can work with this locator type are: find_field, fill_in
#
# *Parameters:*
# * +name+ - Locator name
# * +params+ - Name, ID or text of field
#
def add_field_locator(name, params)
add_locator_by_type(:field, name, params)
end
##
#
# Add locator for button into LocatorStore. Button can be found by: id, name, value
# Capybara methods that can work with this locator type are: click_button, find_button
#
# *Parameters:*
# * +name+ - Locator name
# * +params+ - Name, ID or value
#
def add_button_locator(name, params)
add_locator_by_type(:button, name, params)
end
##
#
# Takes css or xpath locator from LocatorStore by name
#
# *Parameters:*
# * +name+ - Locator name
#
def locator(name)
locator_by_type(:base, name)
end
##
#
# Take link locator from LocatorStore by name
#
# *Parameters:*
# * +name+ - Locator name
#
def link_locator(name)
locator_by_type(:link, name)
end
##
#
# Take field locator from LocatorStore by name
#
# *Parameters:*
# * +name+ - Locator name
#
def field_locator(name)
locator_by_type(:field, name)
end
##
#
# Take button locator from LocatorStore be name
#
# *Parameters:*
# * +name+ - Locator name
#
def button_locator(name)
locator_by_type(:button, name)
end
##
#
# Get locator set by lambda.
# For example: find(apply(locator(:locator_name), 'register')).click
#
# *Parameters:*
# * +locator+ - Locator set with lambda expression
# * +values+ - Arguments that should be matched lambda expression params
#
def apply(locator, *values)
locator.call(*values).to_a.flatten
end
def find_element(name)
type, locator = find_locator(name)
if type == :base
send :find, locator
else
send "find_#{type}", locator
end
end
def first_element(name)
type, locator = find_locator(name)
if type == :base
send :first, locator
else
send :first, type, locator
end
end
protected
def find_locator(name)
name = name.to_s.to_sym
LOCATOR_TYPES.each do|type|
return [type, locator_by_type(type, name)] if (@locators || {}).fetch(self.name, {}).fetch(type, {})[name]
end
log.error(Howitzer::LocatorNotDefinedError, name)
end
# looks up locator in current and all super classes
def parent_locator(type, name)
if !@locators.nil? && @locators.key?(self.name) && @locators[self.name].key?(type) && @locators[self.name][type].key?(name)
@locators[self.name][type][name]
else
self.superclass.parent_locator(type, name) unless self.superclass == Object
end
end
private
def locator_by_type(type, name)
locator = parent_locator(type, name)
log.error(Howitzer::LocatorNotDefinedError, name) if locator.nil?
locator
end
def add_locator_by_type(type, name, params)
@locators ||= {}
@locators[self.name] ||= {}
@locators[self.name][type] ||= {}
log.error Howitzer::BadLocatorParamsError, args.inspect if params.nil? || (!params.is_a?(Proc) && params.empty?)
case params.class.name
when 'Hash'
@locators[self.name][type][name] = [params.keys.first.to_s.to_sym, params.values.first.to_s]
when 'Proc'
@locators[self.name][type][name] = params
else
@locators[self.name][type][name] = params.to_s
end
end
end
#delegate class methods to instance
ClassMethods.public_instance_methods.each do |name|
define_method(name) do |*args|
self.class.send(name, *args)
end
end
end