site-object 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 300dba6834ffdc9679787bcc0a044eec07ba4578
4
- data.tar.gz: 294f6bd2a9b0a11bf6ee4b9c9aebe95158890f3b
3
+ metadata.gz: dcc551b0162213d441310f6c959457d62b70eaa0
4
+ data.tar.gz: 6fa848b3b71572a6ccdfc18190009de1a1fa5d0c
5
5
  SHA512:
6
- metadata.gz: 4ba1cafa9a61574ddcf61e1d22aac4f28b4776e85c2d7dfb5750b089435a032ca5078b38ceb59766e3b490b9e6044b3925e681965de947ca32a54070ce8d2be4
7
- data.tar.gz: 0ca164228e971c736627f0d45c9fa0af320048ea978d8ce71930d1c96ee8a1a2091f7510510af5a5c635b91fa0b99b89b304921a6bc25effa612e53520193ca9
6
+ metadata.gz: 5f0031ea59e20d8bff920e95995dc87c0e7d268989d746ec36ec1ce5915db4735b76cd64b32360875255f7e467558900a62253f9ee38e6ba2f7794b4e36742df
7
+ data.tar.gz: df0e8793178c932d4a91efc8f06265931d0dd42b624404625cefcd265b89e742d9d39b1ba4fd076b35009c7e1f319857b100e1c35570d62428c4903407546944
@@ -3,8 +3,10 @@ class ElementContainer
3
3
 
4
4
  def initialize(element)
5
5
  @element = element
6
+ binding.pry
7
+ @page = parent.site.page
6
8
  end
7
-
9
+
8
10
  def method_missing(sym, *args, &block)
9
11
  @element.send(sym, *args, &block)
10
12
  end
@@ -2,6 +2,9 @@ module SiteObjectExceptions
2
2
  class BrowserLibraryNotSupportedError < RuntimeError
3
3
  end
4
4
 
5
+ class PageConfigError < RuntimeError
6
+ end
7
+
5
8
  class PageInitError < RuntimeError
6
9
  end
7
10
 
@@ -1,14 +1,14 @@
1
1
  # Page objects are containers for all of the functionality of a page that you want to expose for testing
2
- # purposes. When you create a page object you define a URL to access it, elements for all of the page
2
+ # purposes. When you create a page object you define a URL to access it, elements for all of the page
3
3
  # elements that you want to work with as well as higher level methods that use those elements to perform
4
4
  # page operations.
5
5
  #
6
6
  # Here's a very simple account edit page example that has two fields and one button and assumes
7
7
  # that you've defined a site object called 'ExampleSite.'
8
8
  #
9
- # class AccountDetailsEditPage < ExampleSite::Page
9
+ # class AccountDetailsEditPage < ExampleSite::Page
10
10
  # set_url "/accounts/{account_code}/edit" # Parameterized URL.
11
- #
11
+ #
12
12
  # element(:first_name) {|b| b.text_field(:id, 'fname') } # text_field is a Watir method.
13
13
  # element(:last_name) {|b| b.text_field(:id, 'fname') } # text_field is a Watir method.
14
14
  # element(:save) {|b| b.button(:id, 'fname') } # text_field is a Watir method.
@@ -20,26 +20,26 @@
20
20
  # end
21
21
  # end
22
22
  #
23
- # The URL defined in the example above is "parameterized" ({account_code} is a placeholder.)
23
+ # The URL defined in the example above is "parameterized" ({account_code} is a placeholder.)
24
24
  # You don't need to specify parameters for a URL, but if you do you need to call the page with a hash
25
25
  # argument. To use the page after initializing an instance of the site object:
26
26
  #
27
- # site.account_details_edit_page(account_code: 12345)
27
+ # site.account_details_edit_page(account_code: 12345)
28
28
  #
29
- # Pages only take arguments if the URL is parameterized.
29
+ # Pages only take arguments if the URL is parameterized.
30
30
  #
31
- # Note that in the example above that there's no explicit navigation call. This is because the site will
31
+ # Note that in the example above that there's no explicit navigation call. This is because the site will
32
32
  #look at its current URL and automatically navigate to the page if it's not already on it.
33
33
  #
34
- # Here's a simple page object for the rubygems.org search page. Note that no page URL is
34
+ # Here's a simple page object for the rubygems.org search page. Note that no page URL is
35
35
  # defined using the PageObject#set_url method. This is because the page URL for the landing page is
36
- # the same as the base URL for the site. When a page URL isn't explicitly defined the base URL is used
36
+ # the same as the base URL for the site. When a page URL isn't explicitly defined the base URL is used
37
37
  # in its place:
38
38
  #
39
39
  # class LandingPage < RubyGems::Page
40
40
  # element(:search_field) { |b| b.browser.text_field(:id, 'home_query') }
41
41
  # element(:search_submit) { |b| b.browser.input(:id, 'search_submit') }
42
- #
42
+ #
43
43
  # def search(criteria)
44
44
  # search_field.set('rails')
45
45
  # search_submit.click
@@ -48,8 +48,8 @@
48
48
  # end
49
49
  #
50
50
  # Page objects aren't initialized outside of the context of a site object. When a site object is initialized
51
- # it creates accessor methods for each page object that inherits from the site's page class. In the
52
- # example above, the LandingPage class inherits from the RubyGems site object's page class so you'd
51
+ # it creates accessor methods for each page object that inherits from the site's page class. In the
52
+ # example above, the LandingPage class inherits from the RubyGems site object's page class so you'd
53
53
  # be able to use it once you've initialized a RubyGems site:
54
54
  #
55
55
  # site.landing_page.search("rails") # Returns an instance of the landing page after performing a search.
@@ -70,9 +70,9 @@ module PageObject
70
70
  ObjectSpace.each_object(Class).select { |klass| klass < self }
71
71
  end
72
72
 
73
- # This method can be used to disable page navigation when defining a page class (it sets an
73
+ # This method can be used to disable page navigation when defining a page class (it sets an
74
74
  # instance variable called @navigation during initialization.) The use case for this is a page
75
- # that can't be accessed directly and requires some level of browser interaction to reach.
75
+ # that can't be accessed directly and requires some level of browser interaction to reach.
76
76
  # To disable navigation:
77
77
  #
78
78
  # class SomePage < SomeSite::Page
@@ -80,16 +80,16 @@ module PageObject
80
80
  # end
81
81
  #
82
82
  # When navigation is disabled there will be no automatic navigation when the page is called.
83
- # If the current page is not the page that you want a SiteObject::WrongPageError will
83
+ # If the current page is not the page that you want a SiteObject::WrongPageError will
84
84
  # be raised.
85
- # If the visit method is called on the page a SiteObject::PageNavigationNotAllowedError
86
- # will be raised.
85
+ # If the visit method is called on the page a SiteObject::PageNavigationNotAllowedError
86
+ # will be raised.
87
87
  def disable_automatic_navigation
88
88
  @navigation_disabled = true
89
89
  end
90
90
 
91
91
  # Used to define access to a single HTML element on a page. This method takes two arguments:
92
- # * A symbol representing the element you are defining. This symbol is used to create an accessor
92
+ # * A symbol representing the element you are defining. This symbol is used to create an accessor
93
93
  # method on the page object.
94
94
  # * A block where access to the HTML element gets defined.
95
95
  #
@@ -97,8 +97,8 @@ module PageObject
97
97
  #
98
98
  # element(:first_name) { |b| b.text_field(:id 'signup-first-name') }
99
99
  #
100
- # In the example above, the block argument 'b' is the browser object that will get passed down from
101
- # the site to the page and used when the page needs to access the element. You can actually use any
100
+ # In the example above, the block argument 'b' is the browser object that will get passed down from
101
+ # the site to the page and used when the page needs to access the element. You can actually use any
102
102
  # label for the block argument but it's recommended that you use something like 'b' or 'browser'
103
103
  # consistently here because it's always going to be some sort of browser object.
104
104
  #
@@ -110,7 +110,7 @@ module PageObject
110
110
  # el(:first_name) { |b| b.text_field(:id 'signup-first-name') }
111
111
  def element(name, &block)
112
112
  @page_elements ||= []
113
- @page_elements << name.to_sym
113
+ @page_elements << name.to_sym
114
114
  define_method(name) do
115
115
  block.call(@browser)
116
116
  end
@@ -122,26 +122,26 @@ module PageObject
122
122
  @arguments ||= @url_template.keys.map { |k| k.to_sym }
123
123
  end
124
124
 
125
- # Used to define the full or relative URL to the page. Typically, you will *almost* *always* want to use
126
- # this method when defining a page object (but see notes below.) The URL can be defined in a number
125
+ # Used to define the full or relative URL to the page. Typically, you will *almost* *always* want to use
126
+ # this method when defining a page object (but see notes below.) The URL can be defined in a number
127
127
  # of different ways. Here are some examples using Google News:
128
128
  #
129
129
  # *Relative* *URL*
130
130
  #
131
131
  # set_url "/nwshp?hl=en"
132
132
  #
133
- # Relative URLs are most commonly used when defining page objects. The idea here is that you can
134
- # change the base_url when calling the site object, which allows you to use the same code across
133
+ # Relative URLs are most commonly used when defining page objects. The idea here is that you can
134
+ # change the base_url when calling the site object, which allows you to use the same code across
135
135
  # multiple test environments by changing the base_url as you initialize a site object.
136
136
  #
137
137
  # *Relative* *URL* *with* *URL* *Templating*
138
138
  # set_url "/nwshp?hl={language}"
139
139
  #
140
140
  # This takes the relative URL example one step further, allowing you to set the page's parameters.
141
- # Note that the the language specified in the first relative URL example ('en') was replaced by
142
- # '{language}' in this one. Siteobject uses the Addressable library, which supports this kind of
143
- # templating. When you template a value in the URL, the page object will allow you to specify the
144
- # templated value when it's being initialized. Here's an example of how this works using a news site.
141
+ # Note that the the language specified in the first relative URL example ('en') was replaced by
142
+ # '{language}' in this one. Siteobject uses the Addressable library, which supports this kind of
143
+ # templating. When you template a value in the URL, the page object will allow you to specify the
144
+ # templated value when it's being initialized. Here's an example of how this works using a news site.
145
145
  # Here's the base site object class:
146
146
  #
147
147
  # class NewsSite
@@ -154,14 +154,14 @@ module PageObject
154
154
  # set_url "/news?l={language}"
155
155
  # end
156
156
  #
157
- # After you've initialized the site object you can load the Spanish or French versions of the
157
+ # After you've initialized the site object you can load the Spanish or French versions of the
158
158
  # page by changing the hash argument used to call the page from the site object:
159
159
  #
160
160
  # site = NewsSite.new(base_url: "http://news.somesite.com")
161
161
  # site.news_page(language: 'es')
162
162
  # site.news_page(language: 'fr')
163
163
  #
164
- # In addition to providing a hash of templated values when initializing a page you can also use
164
+ # In addition to providing a hash of templated values when initializing a page you can also use
165
165
  # an object, as long as that object responds to all of the templated arguments in the page's
166
166
  # URL definition. Here's a simple class that has a language method that we can use for the news
167
167
  # page described above:
@@ -175,8 +175,8 @@ module PageObject
175
175
  # end
176
176
  #
177
177
  # In the example below, the Country class is used to create a new new country object called 'c'.
178
- # This object has been initialized with a Spanish language code and the news page
179
- # will load the spanish version of the page when it's called with the country object.
178
+ # This object has been initialized with a Spanish language code and the news page
179
+ # will load the spanish version of the page when it's called with the country object.
180
180
  #
181
181
  # site = NewsSite.new(base_url: "http://news.somesite.com")
182
182
  # c = Country.new('es')
@@ -188,7 +188,7 @@ module PageObject
188
188
  #
189
189
  # If one or more URL parameters are missing when the page is getting initialized then the page
190
190
  # will look at the hash arguments used to initialize the site. If the argument the page needs is
191
- # defined in the site's initialization arguments it will use that. For example, if the site
191
+ # defined in the site's initialization arguments it will use that. For example, if the site
192
192
  # object is initialized with a port, subdomain, or any other argument you can use those values
193
193
  # when defining a page URL. Example:
194
194
  #
@@ -199,7 +199,7 @@ module PageObject
199
199
  # site = MySite.new(subdomain: 'foo')
200
200
  # => <MySite:0x005434546511>
201
201
  # site.configuration_page # No need to provide a subdomain here as long as the site object has it.
202
- # => <ConfigPage:0x705434546541>
202
+ # => <ConfigPage:0x705434546541>
203
203
  #
204
204
  # *Full* *URL*
205
205
  # set_url "http://news.google.com/nwshp?hl=en"
@@ -208,18 +208,18 @@ module PageObject
208
208
  # to do that. Just define a complete URL for that page object and that's what will get used; the
209
209
  # base_url will be ignored.
210
210
  #
211
- # *No* *URL*
211
+ # *No* *URL*
212
212
  #
213
- # The set_url method is not mandatory. when defining a page. If you don't use set_url in the page
214
- # definition then the page will defined the base_url as the page's URL.
213
+ # The set_url method is not mandatory. when defining a page. If you don't use set_url in the page
214
+ # definition then the page will defined the base_url as the page's URL.
215
215
  def set_url(url)
216
216
  url ? @page_url = url : nil
217
- end
217
+ end
218
218
 
219
- def set_url_template(base_url)
219
+ def set_url_template(base_url)
220
220
  begin
221
221
  case @page_url.to_s
222
- when '' # There's no page URL so just assume the base URL
222
+ when '' # There's no page URL so just assume the base URL
223
223
  @url_template = Addressable::Template.new(base_url)
224
224
  when /(http:\/\/|https:\/\/)/i
225
225
  @url_template = Addressable::Template.new(@page_url)
@@ -227,15 +227,15 @@ module PageObject
227
227
  @url_template = Addressable::Template.new(Addressable::URI.parse("#{base_url}#{@page_url}"))
228
228
  end
229
229
  rescue Addressable::URI::InvalidURIError => e
230
- raise SiteObject::PageInitError, "Unable to initialize #{self.class} because there's no base_url defined for the site and the page object URL that was defined was a URL fragment (#{@page_url})\n\n#{caller.join("\n")}"
230
+ raise SiteObject::PageInitError, "Unable to initialize #{self.class} because there's no base_url defined for the site and the page object URL that was defined was a URL fragment (#{@page_url})\n\n#{caller.join("\n")}"
231
231
  end
232
232
  end
233
233
 
234
234
  # Optional. Allows you to specify a fallback mechanism for checking to see if the correct page is
235
235
  # being displayed. This only gets used in cases where the primary mechanism for checking a page
236
- # (the URL template defined by Page#set_url) fails to match the current browser URL. When that
236
+ # (the URL template defined by Page#set_url) fails to match the current browser URL. When that
237
237
  # happens the regular expression defined here will be applied and the navigation check will pass
238
- # if the regular expression matches the current browser URL.
238
+ # if the regular expression matches the current browser URL.
239
239
  #
240
240
  # In most cases, you won't need to define a URL matcher and should just rely on the default page
241
241
  # matching that uses the page's URL template. The default matching should work fine for most cases.
@@ -246,7 +246,7 @@ module PageObject
246
246
  # Used to import page features for use within the page. Example:
247
247
  #
248
248
  # class ConfigPage < MySite::Page
249
- # use_features :footer, :sidebar
249
+ # use_features :footer, :sidebar
250
250
  # end
251
251
  #
252
252
  # Then, once the page object has been initialized:
@@ -264,27 +264,27 @@ module PageObject
264
264
 
265
265
  # Takes the name of a page class. If the current page is of that class then it returns a page
266
266
  # object for the page. Raises a SiteObject::WrongPageError if that's not the case.
267
- # It's generally not a good idea to put error checking inside a page object. This should only be
267
+ # It's generally not a good idea to put error checking inside a page object. This should only be
268
268
  # used in cases where there is a page transition and that transition is always expected to work.
269
269
  def expect_page(page)
270
270
  @site.expect_page(page)
271
271
  end
272
272
 
273
- # There's no need to ever call this directly. Initializes a page object within the context of a
274
- # site object. Takes a site object and a hash of configuration arguments. The site object will
275
- # handle all of this for you.
273
+ # There's no need to ever call this directly. Initializes a page object within the context of a
274
+ # site object. Takes a site object and a hash of configuration arguments. The site object will
275
+ # handle all of this for you.
276
276
  def initialize(site, args={})
277
- @browser = site.browser
277
+ @browser = site.browser
278
278
  @navigation_disabled = self.class.navigation_disabled
279
279
  @page_url = self.class.page_url
280
280
  @page_elements = self.class.page_elements
281
281
  @page_features = self.class.page_features
282
282
  @required_arguments = self.class.required_arguments
283
283
  @site = site
284
- @url_matcher = self.class.url_matcher
285
- @url_template = self.class.url_template
284
+ @url_matcher = self.class.url_matcher
285
+ @url_template = self.class.url_template
286
286
 
287
- # Try to expand the URL template if the URL has parameters.
287
+ # Try to expand the URL template if the URL has parameters.
288
288
  @arguments = {}.with_indifferent_access # Stores the param list that will expand the url_template after examining the arguments used to initialize the page.
289
289
  if @required_arguments.length > 0
290
290
  @required_arguments.each do |arg| # Try to extract each URL argument from the hash or object provided, OR from the site object.
@@ -336,7 +336,7 @@ module PageObject
336
336
  end
337
337
  end
338
338
  end
339
-
339
+
340
340
  @site.most_recent_page = self
341
341
  unless on_page?
342
342
  if @navigation_disabled
@@ -345,8 +345,8 @@ module PageObject
345
345
  else
346
346
  raise SiteObject::PageNavigationNotAllowedError, "The #{self.class.name} page could not be accessed. Navigation is intentionally disabled for this page and the page that the browser was displaying could not be recognized.\n\nPAGE URL:\n------------\n#{@site.browser.url}\n\nPAGE TEXT:\n------------\n#{@site.browser.text}\n\n#{caller.join("\n")}"
347
347
  end
348
- end
349
- visit
348
+ end
349
+ visit
350
350
  end
351
351
  end
352
352
 
@@ -354,32 +354,31 @@ module PageObject
354
354
  def inspect
355
355
  "#<#{self.class.name}:#{object_id} @url_template=#{@url_template.inspect}>"
356
356
  end
357
-
357
+
358
358
  # Returns true if the page defined by the page object is currently being displayed in the browser,
359
359
  # false if not. It does this in two different ways, which are described below.
360
360
  #
361
361
  # A page always has a URL defined for it. This is typically done by using the Page.set_url method
362
362
  # to specify a URL when defining the page. You can skip using the set_url method but in that case
363
- # the page URL defaults to the base URL defined for the site object.
363
+ # the page URL defaults to the base URL defined for the site object.
364
364
  #
365
365
  # The default approach for determining if the page is being displayed relies on the URL defined for
366
366
  # the page. This method first does a general match against the current browser URL page's URL template.
367
367
  # If a match occurs here, and there are no required arguments for the page the method returns true.
368
- # If the page's URL template does require arguments the method performs an additional check to
369
- # verify that each of the arguments defined for the page match what's in the current browser URL.
370
- # If all of the arguments match then the method will return true.
368
+ # If the page's URL template does require arguments the method performs an additional check to
369
+ # verify that each of the arguments defined for the page match what's in the current browser URL.
370
+ # If all of the arguments match then the method will return true.
371
371
  #
372
372
  # This should work for most cases but may not always be enough. For example, there may be a redirect
373
- # and the URL used to navigate to the page may not be the final page URL. There's a fallback
373
+ # and the URL used to navigate to the page may not be the final page URL. There's a fallback
374
374
  # mechanism for these sorts of situations. You can use the Page.set_url_matcher method to define a
375
375
  # regular expression that the method will use in place of the URL template. If the regular expression
376
- # matches, then the method will return true even if the URL wouldn't match the URL template.
376
+ # matches, then the method will return true even if the URL wouldn't match the URL template.
377
377
  #
378
378
  # It's better to use the default URL matching if possible. But if for some reason it's not feasible
379
379
  # you can use the alternate method to specify how to match the page.
380
380
  def on_page?
381
381
  url = @browser.url
382
-
383
382
  if @url_matcher && @url_matcher =~ url
384
383
  return true
385
384
  elsif @url_template.match(url)
@@ -395,9 +394,9 @@ module PageObject
395
394
  false
396
395
  end
397
396
 
398
- # Refreshes the page.
397
+ # Refreshes the page.
399
398
  def refresh # TODO: Isolate browser library-specific code so that the adding new browser
400
- if @browser.is_a?(Watir::Browser)
399
+ if @browser.is_a?(Watir::Browser)
401
400
  @browser.refresh
402
401
  elsif @browser.is_a?(Selenium::WebDriver::Driver)
403
402
  @browser.navigate.refresh
@@ -407,28 +406,28 @@ module PageObject
407
406
  self
408
407
  end
409
408
 
410
- # Navigates to the page that it's called on. Raises a SiteObject::PageNavigationNotAllowedError when
411
- # navigation has been disabled for the page. Raises a SiteObject::WrongPageError if the
409
+ # Navigates to the page that it's called on. Raises a SiteObject::PageNavigationNotAllowedError when
410
+ # navigation has been disabled for the page. Raises a SiteObject::WrongPageError if the
412
411
  # specified page isn't getting displayed after navigation.
413
412
  def visit
414
413
  if @navigation_disabled
415
414
  raise SiteObject::PageNavigationNotAllowedError, "Navigation has been disabled for the #{self.class.name} page. This was done when defining the page class and usually means that the page can't be reached directly through a URL and requires some additional work to access."
416
- end
415
+ end
417
416
  if @browser.is_a?(Watir::Browser)
418
417
  @browser.goto(@url)
419
418
  elsif @browser.is_a?(Selenium::WebDriver::Driver)
420
419
  @browser.get(@url)
421
420
  else
422
421
  raise SiteObject::BrowserLibraryNotSupportedError, "Only Watir-Webdriver and Selenium Webdriver are currently supported. Class of browser object: #{@browser.class.name}"
423
- end
424
-
422
+ end
423
+
425
424
  if @url_matcher
426
425
  raise SiteObject::WrongPageError, "Navigation check failed after attempting to access the #{self.class.name} page. Current URL #{@browser.url} did not match #{@url_template.pattern}. A URL matcher was also defined for the page and the secondary check against the URL matcher also failed. URL matcher: #{@url_matcher}" unless on_page?
427
426
  else
428
427
  raise SiteObject::WrongPageError, "Navigation check failed after attempting to access the #{self.class.name} page. Current URL #{@browser.url} did not match #{@url_template.pattern}" unless on_page?
429
428
  end
430
429
 
431
- @site.most_recent_page = self
430
+ @site.most_recent_page = self
432
431
  self
433
432
  end
434
433
  end
@@ -83,6 +83,12 @@ module SiteObject
83
83
  @pages.each do |current_page|
84
84
  current_page.set_url_template(@base_url)
85
85
 
86
+ if current_page.url_matcher
87
+ unless current_page.url_matcher.is_a? Regexp
88
+ raise SiteObject::PageConfigError, "A url_matcher was defined for the #{current_page} page but it was not a regular expression. Check the value provided to the set_url_matcher method in the class definition for this page. Object provided was a #{current_page.url_matcher.class.name}"
89
+ end
90
+ end
91
+
86
92
  self.class.class_eval do
87
93
  define_method(current_page.to_s.underscore) do |args={}, block=nil|
88
94
  current_page.new(self, args)
@@ -1,3 +1,3 @@
1
1
  module SiteObject
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: site-object
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Fitisoff
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-21 00:00:00.000000000 Z
11
+ date: 2015-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,11 +80,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
80
  version: '0'
81
81
  requirements: []
82
82
  rubyforge_project: site-object
83
- rubygems_version: 2.2.3
83
+ rubygems_version: 2.2.2
84
84
  signing_key:
85
85
  specification_version: 4
86
86
  summary: Wraps page objects up into a site object, which provides some introspection
87
87
  and navigation capabilities that page objects don't provide. Works with Watir and
88
88
  Selenium.
89
89
  test_files: []
90
- has_rdoc: