require 'test/unit' Capybara::Node::Element.class_eval do def click_at(x, y) right = x - (native.size.width / 2) top = y - (native.size.height / 2) driver.browser.action.move_to(native).move_by(right.to_i, top.to_i).click.perform end def get_width native.size.width end def get_height native.size.height end def get_x native.location.x end def get_y native.location.y end def displayed? native.displayed? end end module TestCentricity class UIElement include Capybara::DSL include Test::Unit::Assertions attr_reader :parent, :locator, :context, :type, :name attr_accessor :alt_locator, :locator_type XPATH_SELECTORS = ['//', '[@', '[contains(@'] CSS_SELECTORS = ['#', ':nth-child(', ':nth-of-type(', '^=', '$=', '*='] def initialize(name, parent, locator, context) @name = name @parent = parent @locator = locator @context = context @type = nil @alt_locator = nil set_locator_type end def set_locator_type(locator = nil) locator = @locator if locator.nil? is_xpath = XPATH_SELECTORS.any? { |selector| locator.include?(selector) } is_css = CSS_SELECTORS.any? { |selector| locator.include?(selector) } if is_xpath && !is_css @locator_type = :xpath elsif is_css && !is_xpath @locator_type = :css elsif !is_css && !is_xpath @locator_type = :css else raise "Cannot determine type of locator for UIElement '#{@name}' - locator = #{locator}" end end def get_object_type if @type @type elsif obj.tag_name obj.tag_name elsif obj.native.attribute('type') obj.native.attribute('type') end end def get_locator @locator end def get_name @name end def set_alt_locator(temp_locator) @alt_locator = temp_locator end def clear_alt_locator @alt_locator = nil end # Click on an object # # @example # basket_link.click # def click obj, type = find_element object_not_found_exception(obj, type) begin obj.click rescue obj.click_at(10, 10) unless Capybara.current_driver == :poltergeist end end # Double-click on an object # # @example # file_image.double_click # def double_click obj, type = find_element object_not_found_exception(obj, type) page.driver.browser.action.double_click(obj.native).perform end # Right-click on an object # # @example # basket_item_image.right_click # def right_click obj, type = find_element object_not_found_exception(obj, type) page.driver.browser.action.context_click(obj.native).perform end # Click at a specific location within an object # # @param x [Integer] X offset # @param y [Integer] Y offset # @example # basket_item_image.click_at(10, 10) # def click_at(x, y) obj, = find_element raise "UI #{object_ref_message} not found" unless obj obj.click_at(x, y) end def set(value) obj, type = find_element object_not_found_exception(obj, type) obj.set(value) end # Send keystrokes to this object. # # @param keys [String] keys # @example # comment_field.send_keys(:enter) # def send_keys(*keys) obj, type = find_element object_not_found_exception(obj, type) obj.send_keys(*keys) end # Does UI object exists? # # @return [Boolean] # @example # basket_link.exists? # def exists?(visible = true) obj, = find_object(visible) obj != nil end # Is UI object visible? # # @return [Boolean] # @example # remember_me_checkbox.visible? # def visible? obj, type = find_object exists = obj invisible = false if type == :css Capybara.using_wait_time 0.1 do # is object itself hidden with .ui-helper-hidden class? self_hidden = page.has_css?("#{@locator}.ui-helper-hidden") # is parent of object hidden, thus hiding the object? parent_hidden = page.has_css?(".ui-helper-hidden > #{@locator}") # is grandparent of object, or any other ancestor, hidden? other_ancestor_hidden = page.has_css?(".ui-helper-hidden * #{@locator}") # if any of the above conditions are true, then object is invisible invisible = self_hidden || parent_hidden || other_ancestor_hidden end else invisible = !obj.visible? if exists end # the object is visible if it exists and it is not invisible if exists && !invisible true else false end end # Is UI object hidden (not visible)? # # @return [Boolean] # @example # remember_me_checkbox.hidden? # def hidden? !visible? end # Is UI object enabled? # # @return [Boolean] # @example # login_button.enabled? # def enabled? !disabled? end # Is UI object disabled (not enabled)? # # @return [Boolean] # @example # login_button.disabled? # def disabled? obj, type = find_element object_not_found_exception(obj, type) obj.disabled? end # Wait until the object exists, or until the specified wait time has expired. # # @param seconds [Integer or Float] wait time in seconds # @example # run_button.wait_until_exists(0.5) # def wait_until_exists(seconds = nil) timeout = seconds.nil? ? Capybara.default_max_wait_time : seconds wait = Selenium::WebDriver::Wait.new(timeout: timeout) wait.until { exists? } rescue raise "Could not find UI #{object_ref_message} after #{timeout} seconds" unless exists? end # Wait until the object no longer exists, or until the specified wait time has expired. # # @param seconds [Integer or Float] wait time in seconds # @example # logout_button.wait_until_gone(5) # def wait_until_gone(seconds = nil) timeout = seconds.nil? ? Capybara.default_max_wait_time : seconds wait = Selenium::WebDriver::Wait.new(timeout: timeout) wait.until { !exists? } rescue raise "UI #{object_ref_message} remained visible after #{timeout} seconds" if exists? end # Wait until the object is visible, or until the specified wait time has expired. # # @param seconds [Integer or Float] wait time in seconds # @example # run_button.wait_until_visible(0.5) # def wait_until_visible(seconds = nil) timeout = seconds.nil? ? Capybara.default_max_wait_time : seconds wait = Selenium::WebDriver::Wait.new(timeout: timeout) wait.until { visible? } rescue raise "Could not find UI #{object_ref_message} after #{timeout} seconds" unless visible? end # Wait until the object is hidden, or until the specified wait time has expired. # # @param seconds [Integer or Float] wait time in seconds # @example # run_button.wait_until_hidden(10) # def wait_until_hidden(seconds = nil) timeout = seconds.nil? ? Capybara.default_max_wait_time : seconds wait = Selenium::WebDriver::Wait.new(timeout: timeout) wait.until { hidden? } rescue raise "UI #{object_ref_message} remained visible after #{timeout} seconds" if visible? end # Wait until the object's value equals the specified value, or until the specified wait time has expired. # # @param seconds [Integer or Float] wait time in seconds # @example # card_authorized_label.wait_until_value_is(5, 'Card authorized') # def wait_until_value_is(value, seconds = nil) timeout = seconds.nil? ? Capybara.default_max_wait_time : seconds wait = Selenium::WebDriver::Wait.new(timeout: timeout) wait.until { get_value == value } rescue raise "Value of UI #{object_ref_message} failed to equal '#{value}' after #{timeout} seconds" unless get_value == value end # Wait until the object's value changes to a different value, or until the specified wait time has expired. # # @param seconds [Integer or Float] wait time in seconds # @example # basket_grand_total_label.wait_until_value_changes(5) # def wait_until_value_changes(seconds = nil) value = get_value timeout = seconds.nil? ? Capybara.default_max_wait_time : seconds wait = Selenium::WebDriver::Wait.new(timeout: timeout) wait.until { get_value != value } rescue raise "Value of UI #{object_ref_message} failed to change from '#{value}' after #{timeout} seconds" if get_value == value end # Return width of object. # # @return [Integer] # @example # button_width = my_button.width # def width obj, type = find_element(false) object_not_found_exception(obj, type) obj.get_width end # Return height of object. # # @return [Integer] # @example # button_height = my_button.height # def height obj, type = find_element(false) object_not_found_exception(obj, type) obj.get_height end # Return x coordinate of object's location. # # @return [Integer] # @example # button_x = my_button.x # def x obj, type = find_element(false) object_not_found_exception(obj, type) obj.get_x end # Return y coordinate of object's location. # # @return [Integer] # @example # button_y = my_button.y # def y obj, type = find_element(false) object_not_found_exception(obj, type) obj.get_y end # Is UI object displayed in browser window? # # @return [Boolean] # @example # basket_link.displayed?? # def displayed? obj, type = find_element(false) object_not_found_exception(obj, type) obj.displayed? end def get_value(visible = true) obj, type = find_element(visible) object_not_found_exception(obj, type) case obj.tag_name.downcase when 'input', 'select', 'textarea' obj.value else obj.text end end alias get_caption get_value def verify_value(expected, enqueue = false) actual = get_value enqueue ? ExceptionQueue.enqueue_assert_equal(expected.strip, actual.strip, "Expected UI #{object_ref_message}") : assert_equal(expected.strip, actual.strip, "Expected UI #{object_ref_message} to display '#{expected}' but found '#{actual}'") end alias verify_caption verify_value # Hover the cursor over an object # # @example # basket_link.hover # def hover obj, type = find_element object_not_found_exception(obj, type) obj.hover end def drag_by(right_offset, down_offset) obj, type = find_element object_not_found_exception(obj, type) page.driver.browser.action.click_and_hold(obj.native).perform sleep(1) obj.drag_by(right_offset, down_offset) end def drag_and_drop(target, right_offset = nil, down_offset = nil) source, type = find_element object_not_found_exception(source, type) page.driver.browser.action.click_and_hold(source.native).perform sleep(1) target_drop, = target.find_element page.driver.browser.action.move_to(target_drop.native, right_offset.to_i, down_offset.to_i).release.perform end def get_attribute(attrib) obj, type = find_element(false) object_not_found_exception(obj, type) obj[attrib] end def get_native_attribute(attrib) obj, type = find_element(false) object_not_found_exception(obj, type) obj.native.attribute(attrib) end private def find_element(visible = true) wait = Selenium::WebDriver::Wait.new(timeout: Capybara.default_max_wait_time) wait.until { find_object(visible) } end def find_object(visible = true) @alt_locator.nil? ? obj_locator = @locator : obj_locator = @alt_locator parent_section = @context == :section && !@parent.get_locator.nil? parent_section ? tries ||= 2 : tries ||= 1 if parent_section && tries > 1 parent_locator = @parent.get_locator parent_locator = parent_locator.gsub('|', ' ') parent_locator_type = @parent.get_locator_type obj = page.find(parent_locator_type, parent_locator, :wait => 0.01).find(@locator_type, obj_locator, :wait => 0.01, :visible => visible) else obj = page.find(@locator_type, obj_locator, :wait => 0.01, :visible => visible) end [obj, @locator_type] rescue retry if (tries -= 1) > 0 [nil, nil] end def object_not_found_exception(obj, obj_type) @alt_locator.nil? ? locator = @locator : locator = @alt_locator obj_type.nil? ? object_type = 'Object' : object_type = obj_type raise "#{object_type} named '#{@name}' (#{locator}) not found" unless obj end def invalid_object_type_exception(obj, obj_type) unless obj.tag_name == obj_type || obj.native.attribute('type') == obj_type @alt_locator.nil? ? locator = @locator : locator = @alt_locator raise "#{locator} is not a #{obj_type} element" end end def invoke_siebel_popup obj, = find_element object_not_found_exception(obj, 'Siebel object') trigger_name = obj.native.attribute('aria-describedby').strip trigger = "span##{trigger_name}" trigger = "#{@parent.get_locator} #{trigger}" if @context == :section && !@parent.get_locator.nil? first(trigger).click end def object_ref_message "object '#{get_name}' (#{get_locator})" end end end