class Element

Attributes

by[R]
locator[R]
name[R]

Public Class Methods

new(name, by, locator, opts = {}) click to toggle source
# File lib/element.rb, line 10
def initialize(name, by, locator, opts = {})
  @name = name
  @by = by
  @locator = locator
  @timeout = opts[:timeout] || Gridium.config.element_timeout #don't set this to zero
  @element_screenshot = nil #used to store the path of element screenshots for comparison

  # wrapped driver
  @driver = Driver.driver

  # selenium web element
  @element = nil

  # should always be driver unless getting an element's child
  @parent ||= (opts[:parent] || @driver)

  #how long to wait between clearing an input and sending keys to it
  @text_padding_time = 0.15
end

Public Instance Methods

append_keys(*args) click to toggle source

add to what's already in the text field for cases when you don't want to stomp what's already in the text field

# File lib/element.rb, line 184
def append_keys(*args)
  ElementExtensions.highlight(self) if Gridium.config.highlight_verifications
  $verification_passes += 1
  unless element.enabled?
    raise "Browser Error: tried to enter #{args} but the input is disabled"
  end
  element.send_keys(*args)
  sleep @text_padding_time
  # when it's possible to validate for more than non-empty outcomes, do that here
end
attribute(name) click to toggle source
# File lib/element.rb, line 93
def attribute(name)
  element.attribute(name)
end
clear() click to toggle source
# File lib/element.rb, line 142
def clear
  element.clear
  sleep @text_padding_time
end
click() click to toggle source
# File lib/element.rb, line 155
def click
  Log.debug("[GRIDIUM::Element] Clicking on #{self}")
  click_retry = 2
  if element.enabled?
    ElementExtensions.highlight(self) if Gridium.config.highlight_verifications
    $verification_passes += 1
    begin
      element.click
    rescue StandardError => e
      Log.warn("[GRIDIUM::Element] Click Exception retrying...")
      sleep 1
      click_retry -= 1
      if click_retry >= 0
        retry
      else
        Log.error("[GRIDIUM::Element] Click Exception #{e}")
        fail
      end
    end
  else
    Log.error('[GRIDIUM::Element] Cannot click on element.  Element is not present.')
  end
end
compare_element_screenshot(base_image_path) click to toggle source
# File lib/element.rb, line 370
def compare_element_screenshot(base_image_path)
  #Returns TRUE if there are no differences, FALSE if there are
  begin
    Log.debug("[GRIDIUM::Element] Loading Images for Comparison...")
    images = [
        ChunkyPNG::Image.from_file(base_image_path),
        ChunkyPNG::Image.from_file(@element_screenshot)
    ]
    #used to store image x,y diff
    diff = []
    Log.debug("[GRIDIUM::Element] Comparing Images...")
    images.first.height.times do |y|
      images.first.row(y).each_with_index do |pixel, x|
        diff << [x,y] unless pixel == images.last[x,y]
      end
    end

    Log.debug("[GRIDIUM::Element] Pixels total:    #{images.first.pixels.length}")
    Log.debug("[GRIDIUM::Element] Pixels changed:  #{diff.length}")
    Log.debug("[GRIDIUM::Element] Pixels changed:  #{(diff.length.to_f / images.first.pixels.length) * 100}%")

    x, y = diff.map{|xy| xy[0]}, diff.map{|xy| xy[1]}

    if x.any? && y.any?
      Log.debug("[GRIDIUM::Element] Differences Detected! Writing Diff Image...")
      name = self.name.gsub(' ', '_')
      #timestamp = Time.now.strftime("%Y_%m_%d__%H_%M_%S")
      element_screenshot_path = File.join($current_run_dir, "#{name}__diff_.png")
      images.last.rect(x.min, y.min, x.max, y.max, ChunkyPNG::Color(0,255,0))
      images.last.save(element_screenshot_path)
      return false
    else
      return true
    end
  rescue StandardError => e
    Log.error("There was a problem comparing element images. #{e.message}")
  end
end
css_value(name) click to toggle source
# File lib/element.rb, line 116
def css_value(name)
  element.css_value(name)
end
disabled?() click to toggle source
# File lib/element.rb, line 138
def disabled?
  !enabled?
end
displayed?(opts = {}) click to toggle source
# File lib/element.rb, line 127
def displayed?(opts = {})
  return element(opts).displayed?
rescue StandardError => error
  Log.debug("[GRIDIUM::Element] element.displayed? is false because this error was rescued: #{error}")
  return false
end
displayed_element() click to toggle source
# File lib/element.rb, line 52
def displayed_element
  found_element = nil
  #Found an issue where the element would go stale after it's found
  begin
    elements = @parent.find_elements(@by, @locator)
    elements.each do |element|
      if element.displayed? #removed check for element.enabled
        found_element = element; #this will always return the last displayed element
      end
    end
    if found_element.nil?
      Log.debug "[GRIDIUM::Element] found #{elements.length} element(s) via #{@by} and #{@locator} and 0 are displayed"
    end
  rescue StandardError => error
    Log.debug("[GRIDIUM::Element] element.displayed_element rescued: #{error}")
    if found_element
      Log.warn("[GRIDIUM::Element] An element was found, but it was not displayed on the page. Gridium.config.visible_elements_only set to: #{Gridium.config.visible_elements_only} Element: #{self}")
    else
      Log.warn("[GRIDIUM::Element] Could not find Element: #{self}")
    end
  end

  found_element
end
drag_to(target) click to toggle source

HTML5 Drag n drop @param [Element] target element to drag to

# File lib/element.rb, line 262
def drag_to(target)
  raise Gridium::InvalidTypeError, "source element selector must be ':css'" unless self.by == :css
  raise Gridium::InvalidTypeError, "target element selector must be ':css'" unless target.by == :css

  Log.debug("[GRIDIUM::Element] Dragging (#{self}) to (#{target})")
  ElementExtensions.drag_to(self, target)
end
element(opts = {}) click to toggle source
# File lib/element.rb, line 34
def element(opts = {})
  timeout = opts[:timeout] || @timeout
  if stale?
    wait = Selenium::WebDriver::Wait.new :timeout => timeout, :interval => 1
    if Gridium.config.visible_elements_only
      human_readable_error { wait.until { @element = displayed_element } }
    else
      human_readable_error { wait.until { @element = @parent.find_element(@by, @locator); Log.debug("[GRIDIUM::Element] Finding element #{self}..."); @element.enabled? } }
    end

  end
  @element
end
element=(e) click to toggle source
# File lib/element.rb, line 48
def element=(e)
  @element = e
end
enabled?() click to toggle source
# File lib/element.rb, line 134
def enabled?
  element.enabled?
end
find_element(by, locator) click to toggle source

Search for an element within this element

@param [Symbol] by (:css or :xpath) @param [String] locator

@return [Element] element

# File lib/element.rb, line 328
def find_element(by, locator)
  Log.debug('[GRIDIUM::Element] Finding element...')
  Element.new("Child of #{@name}", by, locator, parent: @element)
end
find_elements(by, locator) click to toggle source

Search for an elements within this element

@param [Symbol] by (:css or :xpath) @param [String] locator

@return [Array] elements

# File lib/element.rb, line 341
def find_elements(by, locator)
  elements = element.find_elements(by, locator)
  elements.map {|_| Element.new("Child of #{@name}", by, locator, parent: @element)}
end
hover_away() click to toggle source
# File lib/element.rb, line 239
def hover_away
  Log.debug("[GRIDIUM::Element] Hovering away from element (#{self})...")
  if element.enabled?
    $verification_passes += 1
    ElementExtensions.hover_away(self) # Javascript workaround to above issue
  else
    Log.error('[GRIDIUM::Element] Cannot hover away from element.  Element is not present.')
  end
end
hover_over() click to toggle source
# File lib/element.rb, line 226
def hover_over
  Log.debug("[GRIDIUM::Element] Hovering over element (#{self})...")
  # @driver.mouse.move_to(element)            # Note: Doesn't work with Selenium 2.42 bindings for Firefox v31
  # @driver.action.move_to(element).perform
  # @driver.mouse_over(@locator)
  if element.enabled?
    $verification_passes += 1
    ElementExtensions.hover_over(self) # Javascript workaround to above issue
  else
    Log.error('[GRIDIUM::Element] Cannot hover over element.  Element is not present.')
  end
end
innerHTML() click to toggle source
# File lib/element.rb, line 151
def innerHTML
  attribute "innerHTML"
end
jquery_click() click to toggle source
# File lib/element.rb, line 289
def jquery_click
  Log.debug("[GRIDIUM::Element] JQuery clickin (#{self})...")
  if element.enabled?
    $verification_passes += 1
    ElementExtensions.jquery_click(self)
  else
    Log.error('[GRIDIUM::Element] Cannot jquery_click.  Element is not present.')
  end
end
location() click to toggle source
# File lib/element.rb, line 218
def location
  element.location
end
location_once_scrolled_into_view() click to toggle source
# File lib/element.rb, line 222
def location_once_scrolled_into_view
  element.location_once_scrolled_into_view
end
method_missing(method_sym, *arguments, &block) click to toggle source
Calls superclass method
# File lib/element.rb, line 409
def method_missing(method_sym, *arguments, &block)
  Log.debug("[GRIDIUM::Element] called #{method_sym} on element #{@locator} by #{@by_type}")
  if @element.respond_to?(method_sym)
    @element.method(method_sym).call(*arguments, &block)
  else
    super
  end
end
mouse_over(x: 1, y: 1) click to toggle source

Raw webdriver mouse over

# File lib/element.rb, line 250
def mouse_over(x: 1, y: 1)
  Log.debug("[GRIDIUM::Element] Triggering mouse over for (#{self})...")
  if element.enabled?
    $verification_passes += 1
    ElementExtensions.mouse_over(self, x: x, y: y)
  else
    Log.error('[GRIDIUM::Element] Cannot mouse over.  Element is not present.')
  end
end
present?() click to toggle source
# File lib/element.rb, line 120
def present?
  return element.enabled?
rescue StandardError => error
  Log.debug("[GRIDIUM::Element] element.present? is false because this error was rescued: #{error}")
  return false
end
save_element_screenshot() click to toggle source
# File lib/element.rb, line 346
def save_element_screenshot
  Log.debug ("[GRIDIUM::Element] Capturing screenshot of element...")
  self.scroll_into_view

  timestamp = Time.now.strftime("%Y_%m_%d__%H_%M_%S")
  name = self.name.gsub(' ', '_')
  screenshot_path = File.join($current_run_dir, "#{name}__#{timestamp}.png")
  @driver.save_screenshot(screenshot_path)

  location_x = self.location.x
  location_y = self.location.y
  element_width = self.size.width
  element_height = self.size.height

  # ChunkyPNG commands tap into oily_png (performance-enhanced version of chunky_png)
  image = ChunkyPNG::Image.from_file(screenshot_path)
  image1 = image.crop(location_x, location_y, element_width, element_height)
  image2 = image1.to_image
  element_screenshot_path = File.join($current_run_dir, "#{name}__#{timestamp}.png")
  image2.save(element_screenshot_path)
  @element_screenshot = element_screenshot_path
  SpecData.screenshots_captured.push("#{name}__#{timestamp}.png")
end
scroll_into_view() click to toggle source
# File lib/element.rb, line 270
def scroll_into_view
  if element.enabled?
    $verification_passes += 1
    ElementExtensions.scroll_to(self)
  else
    Log.error('[GRIDIUM::Element] Cannot scroll element into view.  Element is not present.')
  end
end
selected?() click to toggle source
# File lib/element.rb, line 303
def selected?
  element.selected?
end
send_keys(*args) click to toggle source

overwrite to what's already in the text field and validate afterward

# File lib/element.rb, line 200
def send_keys(*args)
  ElementExtensions.highlight(self) if Gridium.config.highlight_verifications
  $verification_passes += 1
  unless element.enabled?
    raise "Browser Error: tried to enter #{args} but the input is disabled"
  end
  if only_symbols?(*args)
    append_keys(*args)
  else
    _stomp_input_text(*args)
    field_empty_afterward?(*args)
  end
end
Also aliased as: text=
set_attribute(name, value) click to toggle source

Requires element to have an unique css @param [String] name - attribute name @param [String] value - attribute value to set

# File lib/element.rb, line 100
def set_attribute(name, value)
  Log.debug("[GRIDIUM::Element] setting element attribute '#{name}' to '#{value}'")

  if self.by == :css
    ElementExtensions.set_attribute(self.locator, name, value)
  else
    # see if the element has an id to work with
    id = self.attribute('id')
    if id.nil? || id.empty?
      Log.warn("[GRIDIUM::Element] #{self} does not have an 'id'. Consider adding one.")
    else
      ElementExtensions.set_attribute("[id=#{id}", name, value)
    end
  end
end
size() click to toggle source
# File lib/element.rb, line 299
def size
  element.size
end
submit() click to toggle source
# File lib/element.rb, line 311
def submit
  element.submit
end
tag_name() click to toggle source
# File lib/element.rb, line 307
def tag_name
  element.tag_name
end
text() click to toggle source
# File lib/element.rb, line 315
def text
  #this is used for text based elements
  element.text
end
text=(*args)
Alias for: send_keys
to_s() click to toggle source
# File lib/element.rb, line 30
def to_s
  "'#{@name}' (By:#{@by} => '#{@locator}')"
end
trigger_onblur() click to toggle source
# File lib/element.rb, line 279
def trigger_onblur
  Log.debug("[GRIDIUM::Element] Triggering onblur for (#{self})...")
  if element.enabled?
    $verification_passes += 1
    ElementExtensions.trigger_onblur(self)
  else
    Log.error('[GRIDIUM::Element] Cannot trigger onblur.  Element is not present.')
  end
end
value() click to toggle source
# File lib/element.rb, line 147
def value
  attribute "value"
end
verify(timeout: @timeout) click to toggle source

soft failure, will not kill test immediately

# File lib/element.rb, line 82
def verify(timeout: @timeout)
  Log.debug('[GRIDIUM::Element] Verifying new element...')
  ElementVerification.new(self, timeout)
end
wait_until(timeout: @timeout) click to toggle source

hard failure, will kill test immediately

# File lib/element.rb, line 88
def wait_until(timeout: @timeout)
  Log.debug('[GRIDIUM::Element] Waiting for new element...')
  ElementVerification.new(self, timeout, fail_test: true)
end

Private Instance Methods

_stomp_input_text(*args) click to toggle source

helper to clear input and put new text in

# File lib/element.rb, line 433
def _stomp_input_text(*args)
  Log.debug("[GRIDIUM::Element] Clearing \"#{value}\" from element: (#{self})")
  element.clear
  sleep @text_padding_time
  Log.debug("[GRIDIUM::Element] Typing: #{args} into element: (#{self}).")
  element.send_keys(*args)
  sleep @text_padding_time
end
field_empty?() click to toggle source
# File lib/element.rb, line 457
def field_empty?
  if !value.nil?
    value.empty?
  elsif !innerHTML.nil?
    innerHTML.empty?
  else
    raise "Element Error: Supported #{__method__} text attributes are nil"
  end
end
field_empty_afterward?(*args) click to toggle source

raise error if the field is empty after we sent it values TODO: verify if text correct afterward, but need to be able to handle cases of symbols like :space and :enter correctly

# File lib/element.rb, line 448
def field_empty_afterward?(*args)
  Log.debug("[GRIDIUM::Element] Checking the field after sending #{args}, to see if it's empty")
  check_again = has_characters?(*args) && no_symbols?(*args)
  field_is_empty_but_should_not_be = check_again && field_empty?
  if field_is_empty_but_should_not_be
    raise "Browser Error: tried to input #{args} but found an empty string afterward: #{value}"
  end
end
has_characters?(*args) click to toggle source

helper to check if *args is not empty but contains only empty string(s)/symbol(s) if so, don't bother trying to validate the text afterward

# File lib/element.rb, line 498
def has_characters?(*args)
  characters = args.select { |_| not _.is_a? Symbol }.join('')
  if characters.empty?
    return false
  end
  true
end
human_readable_error(&block) click to toggle source
# File lib/element.rb, line 506
def human_readable_error(&block)
  block.call
rescue StandardError => e
  raise e.exception.class, "#{self} : #{e.message}"
end
no_symbols?(*args) click to toggle source

helper to check if *args to send_keys has any symbols if so, don't bother trying to validate the text afterward

# File lib/element.rb, line 472
def no_symbols?(*args)
  symbols = args.select { |_| _.is_a? Symbol }
  if symbols.length > 0
    return false
  end
  true
end
only_symbols?(*args) click to toggle source

helper to check if *args to send_keys has only symbols if so, don't bother clearing the field first

# File lib/element.rb, line 485
def only_symbols?(*args)
  symbols = args.select { |_| _.is_a? Symbol }
  if symbols.length == args.length
    return true
  end
  false
end
stale?() click to toggle source
# File lib/element.rb, line 420
def stale?
  return true if @element.nil?
  @element.displayed?
rescue StandardError => error
  Log.debug("[GRIDIUM::Element] element.stale? is true because this error was rescued: #{error}")
  Log.warn("[GRIDIUM::Element] Stale element detected.... #{self}")
  return true
end