Class: MiniAutobot::PageObjects::Base

Inherits:
Object
  • Object
show all
Extended by:
ElementContainer
Includes:
Utils::Castable, Utils::Loggable, Utils::OverlayAndWidgetHelper, Utils::PageObjectHelper, Minitest::Assertions
Defined in:
lib/mini_autobot/page_objects/base.rb

Overview

The base page object. All page objects should be a subclass of this. Every subclass must implement the following class methods:

expected_path

All methods added here will be available to all subclasses, so do so sparingly. This class has access to assertions, which should only be used to validate the page.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ElementContainer

add_to_mapped_items, element, elements

Methods included from Utils::OverlayAndWidgetHelper

#get_overlay!, #get_widgets!

Methods included from Utils::PageObjectHelper

#connector_is_saucelabs?, #current_page, #is_element_present?, #is_element_present_and_displayed?, #json_save_to_ever_failed, #page, #print_sauce_link, #put_value, #read_yml, #retry_with_count, #take_screenshot, #teardown, #update_sauce_session_name, #update_sauce_session_status, #wait_for_attribute_to_have_value, #wait_for_element_to_be_present, #wait_for_element_to_display, #with_url_change_wait

Methods included from Utils::Loggable

#logger

Methods included from Utils::Castable

#cast, #cast_any, included

Constructor Details

#initialize(driver) ⇒ Base

Initializes a new page object from the driver. When a page is initialized, no validation occurs. As such, do not call this method directly. Rather, use PageObjectHelper#page in a test case, or #cast in another page object.

Parameters:



41
42
43
44
45
46
# File 'lib/mini_autobot/page_objects/base.rb', line 41

def initialize(driver)
  @driver = driver

  @assertions = 0
  @failures   = []
end

Instance Attribute Details

#assertionsObject

Returns the value of attribute assertions.



22
23
24
# File 'lib/mini_autobot/page_objects/base.rb', line 22

def assertions
  @assertions
end

#driverObject (readonly)

Returns the value of attribute driver.



24
25
26
# File 'lib/mini_autobot/page_objects/base.rb', line 24

def driver
  @driver
end

#failuresObject

Returns the value of attribute failures.



23
24
25
# File 'lib/mini_autobot/page_objects/base.rb', line 23

def failures
  @failures
end

Class Method Details

.expected_path(*args) ⇒ String

Given a set of arguments (no arguments by default), return the expected path to the page, which must only have file path and query-string.

Parameters:

  • args (String)

    one or more arguments to be used in calculating the expected path, if any.

Returns:

  • (String)

    the expected path.

Raises:

  • (NotImplementedError)


32
33
34
# File 'lib/mini_autobot/page_objects/base.rb', line 32

def self.expected_path(*args)
  raise NotImplementedError, "expected_path is not defined for #{self}"
end

Instance Method Details

#click_on_link!(link_text, page_name) ⇒ Object

click on a link on any page, cast a new page and return it



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

def click_on_link!(link_text, page_name)
  driver.find_element(:link, link_text).location_once_scrolled_into_view
  driver.find_element(:link, link_text).click
  # check user angent, if it's on IE, wait 2sec for the title change
  sleep 2 if driver.browser == :ie # todo remove this if every page has wait for title change in validate!
  #sleep 5 #wait for 5 secs
  logger.debug "click_on_link '#{link_text}'"
  cast(page_name)
end

#current_pathString

Returns the current path loaded in the driver.

Returns:

  • (String)

    The current path, without hostname.



59
60
61
# File 'lib/mini_autobot/page_objects/base.rb', line 59

def current_path
  current_url.path
end

#current_urlString

Returns the current URL loaded in the driver.

Returns:

  • (String)

    The current URL, including hostname.



66
67
68
# File 'lib/mini_autobot/page_objects/base.rb', line 66

def current_url
  URI.parse(driver.current_url)
end

#find_all(how, what) ⇒ Object



52
53
54
# File 'lib/mini_autobot/page_objects/base.rb', line 52

def find_all(how, what)
  driver.all(how, what)
end

#find_first(how, what) ⇒ Object



48
49
50
# File 'lib/mini_autobot/page_objects/base.rb', line 48

def find_first(how, what)
  driver.find_element(how, what)
end

#go!(*args) ⇒ Object

Instructs the driver to visit the expected_path.

Parameters:

  • args (*Object)

    optional parameters to pass into expected_path.



78
79
80
# File 'lib/mini_autobot/page_objects/base.rb', line 78

def go!(*args)
  driver.get(driver.url_for(self.class.expected_path(*args)))
end

#go_to_page!(url, page_type = :base) ⇒ Object



248
249
250
251
# File 'lib/mini_autobot/page_objects/base.rb', line 248

def go_to_page!(url, page_type = :base)
  driver.navigate.to(url)
  cast(page_type)
end

#go_to_subpage!(url_path, page_type = :base) ⇒ Object



253
254
255
256
# File 'lib/mini_autobot/page_objects/base.rb', line 253

def go_to_subpage!(url_path, page_type = :base)
  driver.navigate.to(driver.url_for(url_path))
  cast(page_type)
end

#headlineObject



99
100
101
# File 'lib/mini_autobot/page_objects/base.rb', line 99

def headline
  driver.find_element(:css, 'body div.site-content h1').text
end

#include?(value) ⇒ Boolean

Check that the page includes a certain string.

Parameters:

  • value (String)

    the string to search

Returns:

  • (Boolean)


86
87
88
# File 'lib/mini_autobot/page_objects/base.rb', line 86

def include?(value)
  driver.page_source.include?(value)
end

#metaObject

Retrieves all META tags with a ‘name` attribute on the current page.



91
92
93
94
95
96
97
# File 'lib/mini_autobot/page_objects/base.rb', line 91

def meta
  tags = driver.all(:css, 'meta[name]')
  tags.inject(Hash.new) do |vals, tag|
    vals[tag.attribute(:name)] = tag.attribute(:content) if tag.attribute(:name)
    vals
  end
end

#page_objectObject

interface for Overlay And Widget Helper version of get_widgets! and get_overlay!



71
72
73
# File 'lib/mini_autobot/page_objects/base.rb', line 71

def page_object
  self
end

#page_sourceObject

returns the all the page source of a page, useful for debugging



180
181
182
# File 'lib/mini_autobot/page_objects/base.rb', line 180

def page_source
  driver.page_source
end

#raise_on_error_pageObject

PageObject validate! helper. Raises RuntimeError if one of our error pages is displaying. This can prevent a test from taking the entire implicit_wait before announcing error. [~jacord]



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/mini_autobot/page_objects/base.rb', line 187

def raise_on_error_page
  logger.debug "raise_on_error_page"
  title = ''
  begin
    title = driver.title
  rescue ReadTimeout
    logger.debug 'ReadTimeout exception was thrown while trying to execute driver.title'
    logger.debug 'ignore exception and proceed'
  end
  title = driver.title
  logger.debug "Page Title: '#{title}'"
  raise "HTTP 500 Error" if %r/Internal Server Error/ =~ title
  raise "HTTP 503 Error" if %r/503 Service Temporarily Unavailable/ =~ title
  raise "HTTP 404 Error" if %r/Error 404: Page Not Found/ =~ title

  header = driver.find_element('body h1') rescue nil

  unless header.nil?
    raise "HTTP 500 Error" if header.text == 'Internal Server Error'
  end

end

#titleObject

Get page title from any page



104
105
106
# File 'lib/mini_autobot/page_objects/base.rb', line 104

def title
  driver.title
end

#validate!Object

By default, any driver state is accepted for any page. This method should be overridden in subclasses.



110
111
112
# File 'lib/mini_autobot/page_objects/base.rb', line 110

def validate!
  true
end

#wait(opts = {}) ⇒ Object

Explicitly wait for a certain condition to be true:

wait.until { driver.find_element(:css, 'body.tmpl-srp') }

when timeout is not specified, default timeout 5 sec will be used



141
142
143
# File 'lib/mini_autobot/page_objects/base.rb', line 141

def wait(opts = {})
  Selenium::WebDriver::Wait.new(opts)
end

#wait_for_ajax(timeout = 15) ⇒ Object

Wait on all AJAX requests to finish



132
133
134
135
136
# File 'lib/mini_autobot/page_objects/base.rb', line 132

def wait_for_ajax(timeout = 15)
  wait(timeout: timeout, msg: "Timeout after waiting #{timeout} for all ajax requests to finish").until do
    driver.execute_script 'return window.jQuery != undefined && jQuery.active == 0'
  end
end

#wait_for_dom(timeout = 15) ⇒ Object

Wait for all dom events to load



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/mini_autobot/page_objects/base.rb', line 115

def wait_for_dom(timeout = 15)
  uuid = SecureRandom.uuid
  # make sure body is loaded before appending anything to it
  wait(timeout: timeout, msg: "Timeout after waiting #{timeout} for body to load").until do
    is_element_present?(:css, 'body')
  end
  driver.execute_script <<-EOS
      _.defer(function() {
      $('body').append("<div id='#{uuid}'></div>");
      });
  EOS
  wait(timeout: timeout, msg: "Timeout after waiting #{timeout} for all dom events to finish").until do
    is_element_present?(:css, "div[id='#{uuid}']")
  end
end


229
230
231
232
233
234
235
236
# File 'lib/mini_autobot/page_objects/base.rb', line 229

def wait_for_link(link_text)
  message = "waited 15 sec, can't find link #{link_text} on page"
  wait(timeout: 15, message: message).until{ driver.find_element(:link, link_text) }

  unless driver.find_element(:link, link_text).displayed?
    driver.navigate.refresh
  end
end

#wait_for_title_change(title) ⇒ Object



221
222
223
224
225
226
227
# File 'lib/mini_autobot/page_objects/base.rb', line 221

def wait_for_title_change(title)
  title = driver.title if title.nil?
  logger.debug("Waiting for title change from '#{title}'")
  wait(timeout: 15, message: "Waited 15 sec for page transition")
    .until { driver.title != title }
  logger.debug("Arrived at #{driver.title}")
end

#wait_for_url_change(original_url) ⇒ Object

example usage: original_url = driver.current_url driver.find_element(*LINK_REGISTER).click # do some action that should cause url to change wait_for_url_change(original_url)



242
243
244
245
246
# File 'lib/mini_autobot/page_objects/base.rb', line 242

def wait_for_url_change(original_url)
  time = 15
  message = "waited #{time} sec, url is still #{original_url}, was expecting it to change"
  wait(timeout: time, message: message).until { driver.current_url != original_url }
end

#with_page_title_wait(&blk) ⇒ Object

Wrap an action, wait for page title change. This function eliminates some error-prone boilerplate around fetching page titles



172
173
174
175
176
# File 'lib/mini_autobot/page_objects/base.rb', line 172

def with_page_title_wait(&blk)
  title = driver.title
  yield
  wait_for_title_change(title)
end

#with_rescue(lbl, &blk) ⇒ Object

Wrap blocks acting on Selenium elements and catch errors they raise. This probably qualifies as a Dumb LISPer Trick. If there’s a better Ruby-ish way to do this, I welcome it. [~jacord]



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/mini_autobot/page_objects/base.rb', line 148

def with_rescue(lbl, &blk)
  yield ## run the block
  ## rescue errors. Rerunning may help, but we can also test for specific
  ## problems.
rescue Selenium::WebDriver::Error::ElementNotVisibleError => e
  ## The element is in the DOM but e.visible? is 'false'. Retry may help.
  logger.debug "Retrying #{lbl}: #{e.class}"
  yield
rescue Selenium::WebDriver::Error::StaleElementReferenceError => e
  ## The page has changed and invalidated your element. Retry may help.
  logger.debug "Retrying #{lbl}: #{e.class}"
  yield
rescue Selenium::WebDriver::Error::NoSuchElementError => e
  ## Raised by get_element(s). Retry MAY help, but check first for HTTP
  ## 500, which may be best handled higher up the stack.
  logger.debug "Recovering from NoSuchElementError during #{lbl}"
  raise_on_error_page
  ## If we got past the above, retry the block.
  logger.debug "Retrying #{lbl}: #{e.class}"
  yield
end