Class: Element

Inherits:
Object
  • Object
show all
Defined in:
lib/element.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, by, locator, opts = {}) ⇒ Element

Returns a new instance of Element.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/element.rb', line 9

def initialize(name, by, locator, opts = {})
  @name = name
  @by = by
  @locator = locator
  @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

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_sym, *arguments, &block) ⇒ Object



350
351
352
353
354
355
356
357
# File 'lib/element.rb', line 350

def method_missing(method_sym, *arguments, &block)
  Log.debug("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

Instance Attribute Details

#byObject (readonly)

Returns the value of attribute by.



6
7
8
# File 'lib/element.rb', line 6

def by
  @by
end

#locatorObject (readonly)

Returns the value of attribute locator.



6
7
8
# File 'lib/element.rb', line 6

def locator
  @locator
end

#nameObject (readonly)

Returns the value of attribute name.



6
7
8
# File 'lib/element.rb', line 6

def name
  @name
end

Instance Method Details

#append_keys(*args) ⇒ Object

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



145
146
147
148
149
150
151
152
153
154
# File 'lib/element.rb', line 145

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) ⇒ Object



92
93
94
# File 'lib/element.rb', line 92

def attribute(name)
  element.attribute(name)
end

#clearObject



120
121
122
123
# File 'lib/element.rb', line 120

def clear
  element.clear
  sleep @text_padding_time
end

#clickObject



129
130
131
132
133
134
135
136
137
138
# File 'lib/element.rb', line 129

def click
  Log.debug("Clicking on #{self}")
  if element.enabled?
    ElementExtensions.highlight(self) if Gridium.config.highlight_verifications
    $verification_passes += 1
    element.click
  else
    Log.error('Cannot click on element.  Element is not present.')
  end
end

#compare_element_screenshot(base_image_path) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/element.rb', line 311

def compare_element_screenshot(base_image_path)
  #Returns TRUE if there are no differences, FALSE if there are
  begin
    Log.debug("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("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("Pixels total:    #{images.first.pixels.length}")
    Log.debug("Pixels changed:  #{diff.length}")
    Log.debug("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("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 Exception => e
    Log.error("There was a problem comparing element images. #{e.to_s}")
  end
end

#css_value(name) ⇒ Object



96
97
98
# File 'lib/element.rb', line 96

def css_value(name)
  element.css_value(name)
end

#displayed?Boolean

Returns:

  • (Boolean)


109
110
111
112
113
114
# File 'lib/element.rb', line 109

def displayed?
  return element.displayed?
rescue StandardError => error
  Log.debug("element.displayed? is false because this error was rescued: #{error}")
  return false
end

#displayed_elementObject



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/element.rb', line 49

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 "found #{elements.length} element(s) via #{@by} and #{@locator} and 0 are displayed"
    end
  rescue StandardError => error
    Log.debug("element.displayed_element rescued: #{error}")
    if found_element
      Log.warn("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.to_s}")
    else
      Log.warn("Could not find Element: #{self.to_s}")
    end
  end

  found_element
end

#elementObject



32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/element.rb', line 32

def element
  if stale?
    wait = Selenium::WebDriver::Wait.new :timeout => Gridium.config.element_timeout, :interval => 1
    if Gridium.config.visible_elements_only
      wait.until { @element = displayed_element }
    else
      wait.until { @element = @parent.find_element(@by, @locator); Log.debug("Finding element #{self}..."); @element.enabled? }
    end

  end
  @element
end

#element=(e) ⇒ Object



45
46
47
# File 'lib/element.rb', line 45

def element=(e)
  @element = e
end

#enabled?Boolean

Returns:

  • (Boolean)


116
117
118
# File 'lib/element.rb', line 116

def enabled?
  element.enabled?
end

#find_element(by, locator) ⇒ Element

Search for an element within this element

Parameters:

  • by (Symbol)

    (:css or :xpath)

  • locator (String)

Returns:



269
270
271
272
# File 'lib/element.rb', line 269

def find_element(by, locator)
  Log.debug('Finding element...')
  Element.new("Child of #{@name}", by, locator, parent: @element)
end

#find_elements(by, locator) ⇒ Array

Search for an elements within this element

Parameters:

  • by (Symbol)

    (:css or :xpath)

  • locator (String)

Returns:

  • (Array)

    elements



282
283
284
285
# File 'lib/element.rb', line 282

def find_elements(by, locator)
  elements = element.find_elements(by, locator)
  elements.map {|_| Element.new("Child of #{@name}", by, locator, parent: @element)}
end

#hover_awayObject



200
201
202
203
204
205
206
207
208
# File 'lib/element.rb', line 200

def hover_away
  Log.debug("Hovering away from element (#{self.to_s})...")
  if element.enabled?
    $verification_passes += 1
    ElementExtensions.hover_away(self) # Javascript workaround to above issue
  else
    Log.error('Cannot hover away from element.  Element is not present.')
  end
end

#hover_overObject



187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/element.rb', line 187

def hover_over
  Log.debug("Hovering over element (#{self.to_s})...")
  # @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('Cannot hover over element.  Element is not present.')
  end
end

#locationObject



179
180
181
# File 'lib/element.rb', line 179

def location
  element.location
end

#location_once_scrolled_into_viewObject



183
184
185
# File 'lib/element.rb', line 183

def location_once_scrolled_into_view
  element.location_once_scrolled_into_view
end

#mouse_overObject

Raw webdriver mouse over



211
212
213
214
215
216
217
218
219
# File 'lib/element.rb', line 211

def mouse_over
  Log.debug("Triggering mouse over for (#{self.to_s})...")
  if element.enabled?
    $verification_passes += 1
    ElementExtensions.mouse_over(self)
  else
    Log.error('Cannot mouse over.  Element is not present.')
  end
end

#present?Boolean

Returns:

  • (Boolean)


102
103
104
105
106
107
# File 'lib/element.rb', line 102

def present?
  return element.enabled?
rescue StandardError => error
  Log.debug("element.present? is false because this error was rescued: #{error}")
  return false
end

#save_element_screenshotObject



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/element.rb', line 287

def save_element_screenshot
  Log.debug ("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.to_s)
  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_viewObject



221
222
223
224
225
226
227
228
# File 'lib/element.rb', line 221

def scroll_into_view
  if element.enabled?
    $verification_passes += 1
    ElementExtensions.scroll_to(self)
  else
    Log.error('Cannot scroll element into view.  Element is not present.')
  end
end

#selected?Boolean

Returns:

  • (Boolean)


244
245
246
# File 'lib/element.rb', line 244

def selected?
  element.selected?
end

#send_keys(*args) ⇒ Object Also known as: text=

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



161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/element.rb', line 161

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

#sizeObject



240
241
242
# File 'lib/element.rb', line 240

def size
  element.size
end

#submitObject



252
253
254
# File 'lib/element.rb', line 252

def submit
  element.submit
end

#tag_nameObject



248
249
250
# File 'lib/element.rb', line 248

def tag_name
  element.tag_name
end

#textObject



256
257
258
259
# File 'lib/element.rb', line 256

def text
  #this is used for text based elements
  element.text
end

#to_sObject



28
29
30
# File 'lib/element.rb', line 28

def to_s
  "'#{@name}' (By:#{@by} => '#{@locator}')"
end

#trigger_onblurObject



230
231
232
233
234
235
236
237
238
# File 'lib/element.rb', line 230

def trigger_onblur
  Log.debug("Triggering onblur for (#{self.to_s})...")
  if element.enabled?
    $verification_passes += 1
    ElementExtensions.trigger_onblur(self)
  else
    Log.error('Cannot trigger onblur.  Element is not present.')
  end
end

#valueObject



125
126
127
# File 'lib/element.rb', line 125

def value
  element.attribute "value"
end

#verify(timeout: nil) ⇒ Object

soft failure, will not kill test immediately



79
80
81
82
83
# File 'lib/element.rb', line 79

def verify(timeout: nil)
  Log.debug('Verifying new element...')
  timeout = Gridium.config.element_timeout if timeout.nil?
  ElementVerification.new(self, timeout)
end

#wait_until(timeout: nil) ⇒ Object

hard failure, will kill test immediately



86
87
88
89
90
# File 'lib/element.rb', line 86

def wait_until(timeout: nil)
  Log.debug('Waiting for new element...')
  timeout = Gridium.config.element_timeout if timeout.nil?
  ElementVerification.new(self, timeout, fail_test: true)
end