Class: Gless::BasePage
- Inherits:
-
Object
- Object
- Gless::BasePage
- Includes:
- RSpec::Matchers
- Defined in:
- lib/gless/base_page.rb
Overview
This class is intended to be the base class for all page classes used by the session object to represent individual pages on a website. In fact, if you *don’t* subclass all your page classes from this one, something is likely to break.
Class Level Methods
This class defines a bunch of class-level behaviour, so that we can have things like
element :email_field, :text_field, :id => 'email'
in the class definition itself.
However, this is too early to do much of the initialization, which leads to some complexity in the real init method to basically make up for deferred computation.
Calling Back To The Session
The session object needs to know all of the page object classes. This is accomplished by having an inherited
method on this (the BasePage
) class that calls add_page_class
on the Session class; this only stores the subclass, it does no further processing, since complicated processing at class creation time tends to hit snags. When a session object is actually instantiated, the list of page classes is walked, and a page class instance is created for each for future use.
Class Attribute Summary collapse
-
.elements ⇒ Array
Just sets up a default (to wit, []) for elements.
-
.entry_url ⇒ String
A URL that can be used to come to this page directly, if that can be known at compile time; has no sensible default.
-
.url_patterns ⇒ Array
Just sets up a default (to wit, []) for url_patterns.
-
.validator_blocks ⇒ Array
Just sets up a default (to wit, []) for validator_blocks.
-
.validator_elements ⇒ Array
Just sets up a default (to wit, []) for validator_elements.
Instance Attribute Summary collapse
-
#application ⇒ Object
The main application object.
- #browser ⇒ Watir::Browser
-
#cached_elements ⇒ Hash
A hash of cached
WrapWatir
elements indexed by the symbol name. -
#session ⇒ Gless::Session
The session object that uses/created this page.
Class Method Summary collapse
-
.add_validator(&blk) ⇒ Object
Adds the given block to the list of validators to this page, which is run to ensure that the page is loaded.
-
.element(basename, type, opts = {}) ⇒ Object
Specifies an element that might appear on the page.
-
.expected_title(expected_title) ⇒ Object
Specifies the title that this page is expected to have.
-
.inherited(klass) ⇒ Object
Calls back to Gless::Session.
-
.set_entry_url(url) ⇒ Object
Set this page’s entry url.
-
.url(url) ⇒ Rexexp, String
:base_url
is replaced with the output of @application.base_url.
Instance Method Summary collapse
-
#arrived? ⇒ Boolean
Make sure that we’ve actually gotten to this page, after clicking a button or whatever; used by Session.
-
#enter ⇒ Object
Go to the page from who-cares-where, and make sure we’re there.
-
#initialize(browser, session, application) ⇒ BasePage
constructor
A new instance of BasePage.
-
#match_url(url) ⇒ Object
Return true if the given url matches this page’s patterns.
-
#method_missing(sym, *args, &block) ⇒ Object
Pass through anything we don’t understand to the browser, just in case.
-
#substitute(str) ⇒ Object
Perform special variable substitution; used for url match patterns and entry urls.
Constructor Details
#initialize(browser, session, application) ⇒ BasePage
Returns a new instance of BasePage.
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
# File 'lib/gless/base_page.rb', line 349 def initialize browser, session, application # @session.log.debug "In GenericBasePage, for #{self.class.name}, init: #{browser}, #{session}, #{application}" @browser = browser @session = session @application = application # Couldn't do this any earlier, needed the application if self.class.entry_url self.class.entry_url = substitute self.class.entry_url end # Fake inheritance time self.class.elements += self.class.ancestors.map { |x| x.respond_to?( :elements ) ? x.elements : nil } self.class.elements = self.class.elements.flatten.compact.uniq self.class.validator_elements += self.class.ancestors.map { |x| x.respond_to?( :validator_elements ) ? x.validator_elements : nil } self.class.validator_elements = self.class.validator_elements.flatten.compact.uniq self.class.url_patterns.map! { |x| substitute x } @session.log.debug "In GenericBasePage, for #{self.class.name}, init: class vars: #{self.class.entry_url}, #{self.class.url_patterns}, #{self.class.elements}, #{self.class.validator_elements}" end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(sym, *args, &block) ⇒ Object
Pass through anything we don’t understand to the browser, just in case.
384 385 386 |
# File 'lib/gless/base_page.rb', line 384 def method_missing sym, *args, &block @browser.send sym, *args, &block end |
Class Attribute Details
.elements ⇒ Array
Returns Just sets up a default (to wit, []) for elements.
68 69 70 |
# File 'lib/gless/base_page.rb', line 68 def elements @elements ||= [] end |
.entry_url ⇒ String
Returns A URL that can be used to come to this page directly, if that can be known at compile time; has no sensible default.
45 46 47 |
# File 'lib/gless/base_page.rb', line 45 def entry_url @entry_url end |
.url_patterns ⇒ Array
Returns Just sets up a default (to wit, []) for url_patterns.
52 53 54 |
# File 'lib/gless/base_page.rb', line 52 def url_patterns @url_patterns ||= [] end |
.validator_blocks ⇒ Array
Returns Just sets up a default (to wit, []) for validator_blocks.
92 93 94 |
# File 'lib/gless/base_page.rb', line 92 def validator_blocks @validator_blocks ||= [] end |
.validator_elements ⇒ Array
Returns Just sets up a default (to wit, []) for validator_elements.
81 82 83 |
# File 'lib/gless/base_page.rb', line 81 def validator_elements @validator_elements ||= [] end |
Instance Attribute Details
#application ⇒ Object
The main application object. See the README for specifics.
331 332 333 |
# File 'lib/gless/base_page.rb', line 331 def application @application end |
#browser ⇒ Watir::Browser
328 329 330 |
# File 'lib/gless/base_page.rb', line 328 def browser @browser end |
#cached_elements ⇒ Hash
Returns A hash of cached WrapWatir
elements indexed by the symbol name. This hash is cleared whenever the page changes.
484 485 486 |
# File 'lib/gless/base_page.rb', line 484 def cached_elements @cached_elements ||= {} end |
#session ⇒ Gless::Session
Returns The session object that uses/created this page.
335 336 337 |
# File 'lib/gless/base_page.rb', line 335 def session @session end |
Class Method Details
.add_validator(&blk) ⇒ Object
Adds the given block to the list of validators to this page, which is run to ensure that the page is loaded. This provides a low-level version of validator elements, which has more flexibility in determining whether the page is loaded. The block is given two arguments: the browser, and the session. The block is expected to return true if the validation succeeded; i.e., the page is currently loaded according to the validator’s test; and otherwise false.
293 294 295 |
# File 'lib/gless/base_page.rb', line 293 def add_validator &blk validator_blocks << blk end |
.element(basename, type, opts = {}) ⇒ Object
Specifies an element that might appear on the page. The goal is to be easy for users of this library to use, so there’s some real complexity here so that the end user can just do stuff like:
element :deleted_application , :div , :text => /Your application. \S+ has been deleted./
and it comes out feeling very natural.
A longer example:
element :new_application_button , :element , :id => 'new_application' , :validator => true , :click_destination => :ApplicationNewPage
That’s about as complicated as it gets.
The first two arguments (name and type) are required. The rest is a hash. Six options (see below) have special meaning: :validator
, :click_destination
, :parent
, :child
:proc
, :cache
, and :unique
(see below) have special meaning.
Anything else is taken to be a Watir selector. If no selector is forthcoming, the name is taken to be the element id.
The element can also be a collection of elements with the appropriate element type (e.g. lis
, plural of li
); however, if it is restricted by non-watir selectors (e.g. with :child), the collection is returned as an Array
, since watir-webdriver does not support arbitrarily filtering elements from an ElementCollection
. For reliability, the user can either ensure that the element is only used after being coerced into an array with .to_a
to ensure that the collection ends up as an Array in each case (unless the method used is supported by both element collections and arrays), or use a low-level :proc
to bypass gless’s element finding procedure.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/gless/base_page.rb', line 227 def element basename, type, opts = {} # No class-compile-time logging; it's way too much work, as this runs at *rake* time # $master_logger.debug "In GenericBasePage for #{self.name}: element: initial opts: #{opts}" # Promote various other things into selectors; do this before # we add in the default below non_selector_opts = [ :validator, :click_destination, :parent, :cache, :unique, :child ] if ! opts[:selector] opts[:selector] = {} if ! opts.keys.empty? opts.keys.each do |key| if (! non_selector_opts.member?(key)) && (key != :selector) opts[:selector][key] = opts[key] opts.delete(key) end end end opts = { :selector => { :id => basename.to_s }, :validator => false, :click_destination => nil }.merge(opts) # No class-compile-time logging; it's way too much work, as this runs at *rake* time # $master_logger.debug "In GenericBasePage for #{self.name}: element: final opts: #{opts}" selector = opts[:selector] click_destination = opts[:click_destination] validator = opts[:validator] parent = opts[:parent] child = opts[:child] if child.nil? # No child child = [] elsif child.kind_of? Symbol # Single child child = [[child]] elsif (child.kind_of? Array) && (!child.empty?) && (child[0].kind_of? Symbol) # Multiple children w/out arguments child.map! {|s| [s]} end cache = opts[:cache] unique = opts[:unique] methname = basename.to_s.tr('-', '_').to_sym elements << methname if validator # No class-compile-time logging; it's way too much work, as this runs at *rake* time # $master_logger.debug "In GenericBasePage, for #{self.name}, element: #{basename} is a validator" validator_elements << methname end if click_destination # No class-compile-time logging; it's way too much work, as this runs at *rake* time # $master_logger.debug "In GenericBasePage, for #{self.name}, element: #{basename} has a special destination when clicked, #{click_destination}" end define_method methname do |*args| cached_elements[[methname, *args]] ||= Gless::WrapWatir.new(methname, @browser, @session, self, type, selector, click_destination, parent, child, cache, unique, *args) end end |
.expected_title(expected_title) ⇒ Object
Specifies the title that this page is expected to have.
99 100 101 102 103 104 |
# File 'lib/gless/base_page.rb', line 99 def expected_title expected_title define_method 'has_expected_title?' do @session.log.debug "In GenericBasePage, for #{self.class.name}, has_expected_title?: current is #{@browser.title}, expected is #{expected_title}" expected_title.kind_of?(Regexp) ? @browser.title.should =~ expected_title : @browser.title.should == expected_title end end |
.inherited(klass) ⇒ Object
Calls back to Gless::Session. See overview documentation for Gless::BasePage
58 59 60 |
# File 'lib/gless/base_page.rb', line 58 def inherited(klass) Gless::Session.add_page_class klass end |
.set_entry_url(url) ⇒ Object
Set this page’s entry url.
316 317 318 |
# File 'lib/gless/base_page.rb', line 316 def set_entry_url( url ) @entry_url = url end |
.url(url) ⇒ Rexexp, String
:base_url
is replaced with the output of @application.base_url
303 304 305 306 307 308 309 310 311 |
# File 'lib/gless/base_page.rb', line 303 def url( url ) if url.is_a?(String) url_patterns << Regexp.new(Regexp.escape(url)) elsif url.is_a?(Regexp) url_patterns << url else puts "INVALID URL class "+url.class.name+" for #{url.inspect}" end end |
Instance Method Details
#arrived? ⇒ Boolean
Make sure that we’ve actually gotten to this page, after clicking a button or whatever; used by Session
Takes an optional block; if that block exists, it’s run before the per-loop validation attempt.
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 |
# File 'lib/gless/base_page.rb', line 404 def arrived? all_validate = true 6.times do if ! match_url( @browser.url ) yield if block_given? end self.class.validator_elements.each do |x| begin if self.send(x).wait_until_present(5) @session.log.debug "In GenericBasePage, for #{self.class.name}, arrived?: validator element #{x} found." else # Probably never reached @session.log.warn "In GenericBasePage, for #{self.class.name}, arrived?: validator element #{x} NOT found." end rescue Watir::Wait::TimeoutError => e @session.log.warn "In GenericBasePage, for #{self.class.name}, arrived?: validator element #{x} NOT found." all_validate = false end end self.class.validator_blocks.each do |x| if ! x.call @browser, @session @session.log.warn "In GenericBasePage, for #{self.class.name}, arrived?: a validator block failed." all_validate = false end end if all_validate if match_url( @browser.url ) @session.log.debug "In GenericBasePage, for #{self.class.name}, arrived?: all validator elements found." break else @session.log.warn "In GenericBasePage, for #{self.class.name}, arrived?: all validator elements found, but the current URL (#{@browser.url}) doesn't match the expected URL(s) (#{self.class.url_patterns}); trying again." end else @session.log.warn "In GenericBasePage, for #{self.class.name}, arrived?: not all validator elements found, trying again." end end if ! all_validate @session.log.warn "In GenericBasePage, for #{self.class.name}, arrived?: not all validator elements found, continuing, but this it's unlikely to go well." end begin if respond_to? :has_expected_title? has_expected_title?.should be_truthy end match_url( @browser.url ).should be_truthy # We don't use all_validate here because we want to alert on the # element with the problem self.class.validator_elements.each do |x| self.send(x).wait_until_present(5).should be_truthy end @session.log.debug "In GenericBasePage, for #{self.class.name}, arrived?: completed successfully." return true rescue StandardError => e if @session.get_config :global, :debug @session.log.debug "GenericBasePage, for #{self.class.name}, arrived?: something doesn't match (url or title or expected elements), exception information follows, then giving you a debugger" @session.log.debug "Gless::BasePage: Had an exception in debug mode: #{e.inspect}" @session.log.debug "Gless::BasePage: Had an exception in debug mode: #{e.}" @session.log.debug "Gless::BasePage: Had an exception in debug mode: #{e.backtrace.join("\n")}" debugger else @session.log.warn "In GenericBasePage, for #{self.class.name}, arrived?: failed to validate the page." raise e end end end |
#enter ⇒ Object
Go to the page from who-cares-where, and make sure we’re there
389 390 391 392 393 394 395 396 397 398 |
# File 'lib/gless/base_page.rb', line 389 def enter @session.log.debug "#{self.class.name}: enter" raise "#{self.class.name}.enter: no entry_url has been set" if self.class.entry_url.nil? arrived? do @session.log.info "#{self.class.name}: about to goto #{self.class.entry_url} from #{@browser.url}" @browser.goto self.class.entry_url end end |
#match_url(url) ⇒ Object
Return true if the given url matches this page’s patterns
372 373 374 375 376 377 378 379 380 |
# File 'lib/gless/base_page.rb', line 372 def match_url( url ) self.class.url_patterns.each do |pattern| if url =~ pattern return true end end return false end |
#substitute(str) ⇒ Object
Perform special variable substitution; used for url match patterns and entry urls.
339 340 341 342 343 344 345 346 347 |
# File 'lib/gless/base_page.rb', line 339 def substitute str if str.kind_of?(Regexp) reg = str.source reg.gsub!(/\:base_url/,@application.base_url) return Regexp.new(reg) else return str.gsub(/\:base_url/,@application.base_url) end end |