Class: MiniAutobot::PageObjects::Base

Inherits:
Object
  • Object
show all
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 Utils::OverlayAndWidgetHelper

#get_overlay!, #get_widgets!

Methods included from Utils::PageObjectHelper

#connector_is_saucelabs?, #is_element_present?, #is_element_present_and_displayed?, #json_save_to_ever_failed, #page, #print_sauce_link, #put_value, #read_yml, #retry_with_count, #save_to_ever_failed, #take_screenshot, #teardown, #update_sauce_session

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:

  • driver (Selenium::WebDriver)

    The WebDriver instance.



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

def initialize(driver)
  @driver = driver

  @assertions = 0
  @failures   = []
end

Instance Attribute Details

#assertionsObject

Returns the value of attribute assertions.



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

def assertions
  @assertions
end

#driverObject (readonly)

Returns the value of attribute driver.



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

def driver
  @driver
end

#failuresObject

Returns the value of attribute failures.



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

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)


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

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



208
209
210
211
212
213
214
215
216
# File 'lib/mini_autobot/page_objects/base.rb', line 208

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.



50
51
52
# File 'lib/mini_autobot/page_objects/base.rb', line 50

def current_path
  current_url.path
end

#current_urlString

Returns the current URL loaded in the driver.

Returns:

  • (String)

    The current URL, including hostname.



57
58
59
# File 'lib/mini_autobot/page_objects/base.rb', line 57

def current_url
  URI.parse(driver.current_url)
end

#go!(*args) ⇒ Object

Instructs the driver to visit the expected_path.

Parameters:

  • args (*Object)

    optional parameters to pass into expected_path.



69
70
71
# File 'lib/mini_autobot/page_objects/base.rb', line 69

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

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



245
246
247
248
# File 'lib/mini_autobot/page_objects/base.rb', line 245

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



250
251
252
253
# File 'lib/mini_autobot/page_objects/base.rb', line 250

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

#headlineObject



90
91
92
# File 'lib/mini_autobot/page_objects/base.rb', line 90

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)


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

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

#metaObject

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



82
83
84
85
86
87
88
# File 'lib/mini_autobot/page_objects/base.rb', line 82

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!



62
63
64
# File 'lib/mini_autobot/page_objects/base.rb', line 62

def page_object
  self
end

#page_sourceObject

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



177
178
179
# File 'lib/mini_autobot/page_objects/base.rb', line 177

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]



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

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



95
96
97
# File 'lib/mini_autobot/page_objects/base.rb', line 95

def title
  driver.title
end

#validate!Object

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



101
102
103
# File 'lib/mini_autobot/page_objects/base.rb', line 101

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 when timeout is larger than 15, max timeout 15 sec will be used



133
134
135
136
137
138
139
140
# File 'lib/mini_autobot/page_objects/base.rb', line 133

def wait(opts = {})
  if !opts[:timeout].nil? && opts[:timeout] > 15
    puts "WARNING: #{opts[:timeout]} sec timeout is NOT supported by wait method,
          max timeout 15 sec will be used instead"
    opts[:timeout] = 15
  end
  Selenium::WebDriver::Wait.new(opts)
end

#wait_for_ajax(timeout = 15) ⇒ Object

Wait on all AJAX requests to finish



123
124
125
126
127
# File 'lib/mini_autobot/page_objects/base.rb', line 123

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



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/mini_autobot/page_objects/base.rb', line 106

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 "      _.defer(function() {\n      $('body').append(\"<div id='\#{uuid}'></div>\");\n      });\n  EOS\n  wait(timeout: timeout, msg: \"Timeout after waiting \#{timeout} for all dom events to finish\").until do\n    is_element_present?(:css, \"div[id='\#{uuid}']\")\n  end\nend\n"


226
227
228
229
230
231
232
233
# File 'lib/mini_autobot/page_objects/base.rb', line 226

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



218
219
220
221
222
223
224
# File 'lib/mini_autobot/page_objects/base.rb', line 218

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)



239
240
241
242
243
# File 'lib/mini_autobot/page_objects/base.rb', line 239

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



169
170
171
172
173
# File 'lib/mini_autobot/page_objects/base.rb', line 169

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]



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

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