centric_page_object 2.3.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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +8 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +17 -0
  8. data/ChangeLog +931 -0
  9. data/Gemfile +12 -0
  10. data/Guardfile +20 -0
  11. data/LICENSE +20 -0
  12. data/README.md +114 -0
  13. data/Rakefile +29 -0
  14. data/centric_page_object.gemspec +31 -0
  15. data/cucumber.yml +8 -0
  16. data/lib/page-object/accessors.rb +1201 -0
  17. data/lib/page-object/element_locators.rb +21 -0
  18. data/lib/page-object/elements/area.rb +9 -0
  19. data/lib/page-object/elements/audio.rb +9 -0
  20. data/lib/page-object/elements/bold.rb +9 -0
  21. data/lib/page-object/elements/button.rb +12 -0
  22. data/lib/page-object/elements/canvas.rb +10 -0
  23. data/lib/page-object/elements/check_box.rb +9 -0
  24. data/lib/page-object/elements/date_field.rb +10 -0
  25. data/lib/page-object/elements/div.rb +9 -0
  26. data/lib/page-object/elements/element.rb +212 -0
  27. data/lib/page-object/elements/file_field.rb +9 -0
  28. data/lib/page-object/elements/form.rb +9 -0
  29. data/lib/page-object/elements/heading.rb +14 -0
  30. data/lib/page-object/elements/hidden_field.rb +9 -0
  31. data/lib/page-object/elements/image.rb +10 -0
  32. data/lib/page-object/elements/italic.rb +9 -0
  33. data/lib/page-object/elements/label.rb +9 -0
  34. data/lib/page-object/elements/link.rb +9 -0
  35. data/lib/page-object/elements/list_item.rb +9 -0
  36. data/lib/page-object/elements/media.rb +11 -0
  37. data/lib/page-object/elements/option.rb +9 -0
  38. data/lib/page-object/elements/ordered_list.rb +43 -0
  39. data/lib/page-object/elements/paragraph.rb +9 -0
  40. data/lib/page-object/elements/radio_button.rb +9 -0
  41. data/lib/page-object/elements/select_list.rb +42 -0
  42. data/lib/page-object/elements/span.rb +9 -0
  43. data/lib/page-object/elements/table.rb +85 -0
  44. data/lib/page-object/elements/table_cell.rb +10 -0
  45. data/lib/page-object/elements/table_row.rb +52 -0
  46. data/lib/page-object/elements/text_area.rb +9 -0
  47. data/lib/page-object/elements/text_field.rb +10 -0
  48. data/lib/page-object/elements/unordered_list.rb +42 -0
  49. data/lib/page-object/elements/video.rb +9 -0
  50. data/lib/page-object/elements.rb +62 -0
  51. data/lib/page-object/indexed_properties.rb +41 -0
  52. data/lib/page-object/javascript/angularjs.rb +14 -0
  53. data/lib/page-object/javascript/jquery.rb +14 -0
  54. data/lib/page-object/javascript/prototype.rb +14 -0
  55. data/lib/page-object/javascript/yui.rb +19 -0
  56. data/lib/page-object/javascript_framework_facade.rb +80 -0
  57. data/lib/page-object/locator_generator.rb +183 -0
  58. data/lib/page-object/nested_elements.rb +17 -0
  59. data/lib/page-object/page_factory.rb +108 -0
  60. data/lib/page-object/page_populator.rb +105 -0
  61. data/lib/page-object/platforms/watir/page_object.rb +1155 -0
  62. data/lib/page-object/platforms/watir.rb +50 -0
  63. data/lib/page-object/section_collection.rb +16 -0
  64. data/lib/page-object/version.rb +4 -0
  65. data/lib/page-object/widgets.rb +98 -0
  66. data/lib/page-object.rb +431 -0
  67. data/pageobject.gems +1 -0
  68. metadata +239 -0
@@ -0,0 +1,50 @@
1
+ module PageObject
2
+ module Platforms
3
+ module Watir
4
+
5
+ def self.create_page_object(browser)
6
+ browser = selenium_browser(browser) unless watir?(browser)
7
+ return Watir::PageObject.new(browser)
8
+ end
9
+
10
+ def self.is_for?(browser)
11
+ require 'watir'
12
+ watir?(browser) || selenium?(browser)
13
+ end
14
+
15
+ def self.browser_for root
16
+ return watir_browser(root) if watir?(root)
17
+ return selenium_browser(root) if selenium?(root)
18
+ nil
19
+ end
20
+
21
+ def self.root_element_for root
22
+ Elements::Element.new(root) if is_for?(root)
23
+ end
24
+
25
+ def self.browser_root_for browser
26
+ browser.element
27
+ end
28
+
29
+ private
30
+
31
+ def self.watir_browser(root)
32
+ return root if root.is_a?(::Watir::Browser)
33
+ root.browser
34
+ end
35
+
36
+ def self.selenium_browser(root)
37
+ return ::Watir::Browser.new(root) if root.is_a?(::Selenium::WebDriver::Driver)
38
+ ::Watir::Browser.new(Selenium::WebDriver::Driver.new(root.send(:bridge)))
39
+ end
40
+
41
+ def self.watir?(browser)
42
+ browser.is_a?(::Watir::Browser) || browser.is_a?(::Watir::HTMLElement)
43
+ end
44
+
45
+ def self.selenium?(browser)
46
+ browser.is_a?(::Selenium::WebDriver::Driver) || browser.is_a?(::Selenium::WebDriver::Element)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,16 @@
1
+ module PageObject
2
+ class SectionCollection < Array
3
+ def find_by(values_hash)
4
+ find do |section|
5
+ values_hash.all? { |key, value| value === section.public_send(key) }
6
+ end
7
+ end
8
+
9
+ def select_by(values_hash)
10
+ matches = select do |section|
11
+ values_hash.all? { |key, value| value === section.public_send(key) }
12
+ end
13
+ self.class[*matches]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ module PageObject
2
+ # @private
3
+ VERSION = '2.3.1'
4
+ end
@@ -0,0 +1,98 @@
1
+ require 'page-object/elements'
2
+ require 'page-object/platforms/watir/page_object'
3
+
4
+ module PageObject
5
+ module Widgets
6
+
7
+ #
8
+ # Module that allows for the registration of widget classes which extend the functionality of PageObject
9
+ # Allows any classes which extend PageObject::Element to be used as PageObject elements.
10
+ # This allows such widgets to be created using the defined tags.
11
+ #
12
+ # @param [Symbol] defines the symbol which will be used as an accessor name.
13
+ # @param [Class] the widget class extending PageObject::Elements::Element
14
+ # @param [Symbol] the symbol of the html element used when searching for this widget.
15
+ #
16
+ #
17
+ def self.register_widget(widget_tag, widget_class, base_element_tag)
18
+ if widget_class.ancestors.include? Elements::Element
19
+ define_accessors(Accessors, widget_tag, widget_class)
20
+ define_nested_elements(Elements::Element, widget_tag)
21
+ define_locators(PageObject, widget_tag)
22
+
23
+ PageObject::Platforms::Watir::PageObject.define_widget_accessors(widget_tag, widget_class, base_element_tag)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def self.define_accessors(base, widget_tag, widget_class)
30
+ accessors_module = Module.new do
31
+ define_method widget_tag do |name, *identifier_args, &block|
32
+
33
+ identifier = identifier_args.first
34
+ identifier = {:index => 0} if identifier.nil?
35
+
36
+ define_method("#{name}_element") do
37
+ return call_block(&block) if block
38
+ platform.send("#{widget_tag}_for", identifier.clone)
39
+ end
40
+ define_method("#{name}?") do
41
+ return call_block(&block).exists? if block
42
+ platform.send("#{widget_tag}_for", identifier.clone).exists?
43
+ end
44
+ if widget_class.respond_to? :accessor_methods
45
+ widget_class.accessor_methods(self, name)
46
+ end
47
+ end
48
+ define_method widget_class.plural_form do |name, *identifier_args, &block|
49
+ define_method("#{name}_elements") do
50
+ return call_block(&block) unless block.nil?
51
+ platform_method = "#{widget_tag.to_s}s_for"
52
+ platform.send platform_method, (identifier_args.first ? identifier_args.first.clone : {})
53
+ end
54
+ end
55
+ end
56
+
57
+ base.send(:include, accessors_module)
58
+ end
59
+
60
+ def self.define_nested_elements(base, widget_tag)
61
+ define_singular_nested_accessor(base, widget_tag)
62
+ define_multiple_nested_accessor(base, widget_tag)
63
+ end
64
+
65
+ def self.define_multiple_nested_accessor(base, widget_tag)
66
+ base.send(:define_method, "#{widget_tag}_elements") do |*args|
67
+ identifier = args[0] ? args[0] : {}
68
+ @platform.send("#{widget_tag}s_for", identifier.clone)
69
+ end
70
+ end
71
+
72
+ def self.define_singular_nested_accessor(base, widget_tag)
73
+ base.send(:define_method, "#{widget_tag}_element") do |*args|
74
+ identifier = args[0] ? args[0] : {:index => 0}
75
+ @platform.send("#{widget_tag}_for", identifier.clone)
76
+ end
77
+ end
78
+
79
+ def self.define_locators(base, widget_tag)
80
+ define_singular_locator(base, widget_tag)
81
+ define_multiple_locator(base, widget_tag)
82
+ end
83
+
84
+ def self.define_multiple_locator(base, widget_tag)
85
+ base.send(:define_method, "#{widget_tag}_elements") do |*args|
86
+ identifier = args[0] ? args[0] : {}
87
+ platform.send("#{widget_tag}s_for", identifier.clone)
88
+ end
89
+ end
90
+
91
+ def self.define_singular_locator(base, widget_tag)
92
+ base.send(:define_method, "#{widget_tag}_element") do |*args|
93
+ identifier = args[0] ? args[0] : {:index => 0}
94
+ platform.send("#{widget_tag}_for", identifier.clone)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,431 @@
1
+ require 'watir'
2
+ require 'page-object/version'
3
+ require 'page-object/accessors'
4
+ require 'page-object/element_locators'
5
+ require 'page-object/nested_elements'
6
+ require 'page-object/page_factory'
7
+ require 'page-object/page_populator'
8
+ require 'page-object/javascript_framework_facade'
9
+ require 'page-object/indexed_properties'
10
+ require 'page-object/section_collection'
11
+ require 'page-object/widgets'
12
+
13
+ require 'page-object/platforms/watir'
14
+ require 'page-object/platforms/watir/page_object'
15
+
16
+ #
17
+ # Module that when included adds functionality to a page object. This module
18
+ # will add numerous class and instance methods that you use to define and
19
+ # interact with web pages.
20
+ #
21
+ # If we have a login page with a username and password textfield and a login
22
+ # button we might define our page like the one below. We can then interact with
23
+ # the object using the generated methods.
24
+ #
25
+ # @example Login page example
26
+ # class LoginPage
27
+ # include PageObject
28
+ #
29
+ # text_field(:username, :id => 'user')
30
+ # text_field(:password, :id => 'pass')
31
+ # button(:login, :value => 'Login')
32
+ # end
33
+ #
34
+ # ...
35
+ #
36
+ # browser = Watir::Browser.new :firefox
37
+ # login_page = LoginPage.new(browser)
38
+ # login_page.username = 'cheezy'
39
+ # login_page.password = 'secret'
40
+ # login_page.login
41
+ #
42
+ # @see PageObject::Accessors to see what class level methods are added to this module at runtime.
43
+ #
44
+ module PageObject
45
+ include ElementLocators
46
+ include PagePopulator
47
+
48
+ def method_missing(method, *args, &block)
49
+ if @root_element.respond_to?(method)
50
+ @root_element.send(method, *args, &block)
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ def respond_to_missing?(method, include_all = false)
57
+ @root_element && @root_element.respond_to?(method) || super
58
+ end
59
+
60
+ # @return the platform browser passed to the constructor
61
+ attr_reader :browser
62
+ # @return [PageObject::WatirPageObject] the platform page object
63
+ attr_reader :platform
64
+
65
+ #
66
+ # Construct a new page object. Prior to browser initialization it will call
67
+ # a method named initialize_accessors if it exists. Upon initialization of
68
+ # the page it will call a method named initialize_page if it exists.
69
+ #
70
+ # @param [Watir::Browser, Watir::HTMLElement or Selenium::WebDriver::Driver, Selenium::WebDriver::Element] the platform browser/element to use
71
+ # @param [bool] open the page if page_url is set
72
+ #
73
+ def initialize(root, visit=false)
74
+ initialize_accessors if respond_to?(:initialize_accessors)
75
+ initialize_browser(root)
76
+ goto if visit && self.class.instance_methods(false).include?(:goto)
77
+ initialize_page if respond_to?(:initialize_page)
78
+ end
79
+
80
+ def initialize_browser(root)
81
+ @root_element = PageObject::Platforms::Watir.root_element_for root
82
+ @browser = root
83
+ @platform = PageObject::Platforms::Watir.create_page_object @browser
84
+ end
85
+
86
+ # @private
87
+ def self.included(cls)
88
+ cls.extend PageObject::Accessors
89
+ end
90
+
91
+ #
92
+ # Set the default timeout for page level waits
93
+ #
94
+ def self.default_page_wait=(timeout)
95
+ @page_wait = timeout
96
+ end
97
+
98
+ #
99
+ # Returns the default timeout for page lavel waits
100
+ #
101
+ def self.default_page_wait
102
+ @page_wait ||= 30
103
+ end
104
+
105
+ #
106
+ # Sets the default timeout for element level waits
107
+ #
108
+ def self.default_element_wait=(timeout)
109
+ @element_wait = timeout
110
+ end
111
+
112
+ #
113
+ # Returns the default timeout for element level waits
114
+ #
115
+ def self.default_element_wait
116
+ @element_wait ||= 5
117
+ end
118
+
119
+ #
120
+ # Set the javascript framework to use when determining number of
121
+ # ajax requests. Valid frameworks are :jquery, :prototype, :yui,
122
+ # and :angularjs
123
+ #
124
+ def self.javascript_framework=(framework)
125
+ PageObject::JavascriptFrameworkFacade.framework = framework
126
+ end
127
+
128
+ #
129
+ # Add a new javascript framework to page-object. The module passed
130
+ # in must adhere to the same prototype as the JQuery and Prototype
131
+ # modules.
132
+ #
133
+ # @param [Symbol] the name used to reference the framework in
134
+ # subsequent calls
135
+ # @param [Module] a module that has the necessary methods to perform
136
+ # the required actions.
137
+ #
138
+ def self.add_framework(key, framework)
139
+ PageObject::JavascriptFrameworkFacade.add_framework(key, framework)
140
+ end
141
+
142
+ #
143
+ # get the current page url
144
+ #
145
+ def current_url
146
+ platform.current_url
147
+ end
148
+
149
+ #
150
+ # navigate to the provided url
151
+ #
152
+ # @param [String] the full url to navigate to
153
+ #
154
+ def navigate_to(url)
155
+ platform.navigate_to(url)
156
+ end
157
+
158
+ #
159
+ # Returns the text of the current page
160
+ #
161
+ def text
162
+ platform.text
163
+ end
164
+
165
+ #
166
+ # Returns the html of the current page
167
+ #
168
+ def html
169
+ platform.html
170
+ end
171
+
172
+ #
173
+ # Returns the title of the current page
174
+ #
175
+ def title
176
+ platform.title
177
+ end
178
+
179
+ #
180
+ # Wait until the block returns true or times out
181
+ #
182
+ # @example
183
+ # @page.wait_until(5, 'Success not found on page') do
184
+ # @page.text.include? 'Success'
185
+ # end
186
+ #
187
+ # @param [Numeric] the amount of time to wait for the block to return true.
188
+ # @param [String] the message to include with the error if we exceed the timeout duration.
189
+ # @param block the block to execute. It should return true when successful.
190
+ #
191
+ def wait_until(timeout = PageObject.default_page_wait, message = nil, &block)
192
+ platform.wait_until(timeout, message, &block)
193
+ end
194
+
195
+
196
+ #
197
+ # Wait until there are no pending ajax requests. This requires you
198
+ # to set the javascript framework in advance.
199
+ #
200
+ # @param [Numeric] the amount of time to wait for the block to return true.
201
+ # @param [String] the message to include with the error if we exceed
202
+ # the timeout duration.
203
+ #
204
+ def wait_for_ajax(timeout = 30, message = nil)
205
+ end_time = ::Time.now + timeout
206
+ until ::Time.now > end_time
207
+ return if browser.execute_script(::PageObject::JavascriptFrameworkFacade.pending_requests) == 0
208
+ sleep 0.5
209
+ end
210
+ message = "Timed out waiting for ajax requests to complete" unless message
211
+ raise message
212
+ end
213
+
214
+ #
215
+ # Override the normal alert popup so it does not occur.
216
+ #
217
+ # @example
218
+ # message = @page.alert do
219
+ # @page.button_that_causes_alert
220
+ # end
221
+ #
222
+ # @param frame optional parameter used when alert is nested within a frame
223
+ # @param block a block that has the call that will cause the alert to display
224
+ # @return [String] the message that was contained in the alert
225
+ #
226
+ def alert(frame=nil, &block)
227
+ platform.alert(frame, &block)
228
+ end
229
+
230
+ #
231
+ # Override the normal confirm popup so it does not occur.
232
+ #
233
+ # @example
234
+ # message = @popup.confirm(true) do
235
+ # @page.button_that_causes_confirm
236
+ # end
237
+ #
238
+ # @param [bool] what response you want to return back from the confirm popup
239
+ # @param frame optional parameter used when the confirm is nested within a frame
240
+ # @param block a block that has the call that will cause the confirm to display
241
+ # @return [String] the message that was prompted in the confirm
242
+ #
243
+ def confirm(response, frame=nil, &block)
244
+ platform.confirm(response, frame, &block)
245
+ end
246
+
247
+ #
248
+ # Override the normal prompt popup so it does not occur.
249
+ #
250
+ # @example
251
+ # message = @popup.prompt("Some Value") do
252
+ # @page.button_that_causes_prompt
253
+ # end
254
+ #
255
+ # @param [string] the value returned to the caller of the prompt
256
+ # @param frame optional parameter used with the prompt is nested within a frame
257
+ # @param block a block that has the call that will cause the prompt to display
258
+ # @return [Hash] A has containing two keys - :message contains the prompt message and
259
+ # :default_value contains the default value for the prompt if provided
260
+ #
261
+ def prompt(answer, frame=nil, &block)
262
+ platform.prompt(answer, frame, &block)
263
+ end
264
+
265
+ #
266
+ # Execute javascript on the browser
267
+ #
268
+ # @example Get inner HTML of element
269
+ # span = @page.span_element
270
+ # @page.execute_script "return arguments[0].innerHTML", span
271
+ # #=> "Span innerHTML"
272
+ #
273
+ def execute_script(script, *args)
274
+ args.map! { |e| e.kind_of?(PageObject::Elements::Element) ? e.element : e }
275
+ platform.execute_script(script, *args)
276
+ end
277
+
278
+ #
279
+ # Identify an element as existing within a frame. A frame parameter
280
+ # is passed to the block and must be passed to the other calls to PageObject.
281
+ # You can nest calls to in_frame by passing the frame to the next level.
282
+ #
283
+ # @example
284
+ # in_frame(:id => 'frame_id') do |frame|
285
+ # text_field_element(:id => 'fname', :frame => frame)
286
+ # end
287
+ #
288
+ # @param [Hash] identifier how we find the frame. The valid keys are:
289
+ # * :id
290
+ # * :index
291
+ # * :name
292
+ # * :class
293
+ # @param frame passed from a previous call to in_frame. Used to nest calls
294
+ # @param block that contains the calls to elements that exist inside the frame.
295
+ #
296
+ def in_frame(identifier, frame=nil, &block)
297
+ platform.in_frame(identifier, frame, &block)
298
+ end
299
+
300
+ # Identify an element as existing within an iframe. A frame parameter
301
+ # is passed to the block and must be passed to the other calls to PageObject.
302
+ # You can nest calls to in_iframe by passing the frame to the next level.
303
+ #
304
+ # @example
305
+ # in_iframe(:id => 'iframe_id') do |iframe|
306
+ # text_field_element(:id => 'ifname', :frame => iframe)
307
+ # end
308
+ #
309
+ # @param [Hash] identifier how we find the iframe. The valid keys are:
310
+ # * :id
311
+ # * :index
312
+ # * :name
313
+ # * :class
314
+ # @param frame passed from a previous call to in_iframe. Used to nest calls
315
+ # @param block that contains the calls to elements that exist inside the iframe.
316
+ #
317
+ def in_iframe(identifier, frame=nil, &block)
318
+ platform.in_iframe(identifier, frame, &block)
319
+ end
320
+
321
+ #
322
+ # Override the normal showModalDialog call is it opens a window instead
323
+ # of a dialog. You will need to attach to the new window in order to
324
+ # continue.
325
+ #
326
+ # @example
327
+ # @page.modal_dialog do
328
+ # @page.action_that_spawns_the_modal
329
+ # end
330
+ #
331
+ # @param block a block that contains the call that will cause the modal dialog.
332
+ #
333
+ def modal_dialog(&block)
334
+ script =
335
+ %Q{
336
+ window.showModalDialog = function(sURL, vArguments, sFeatures) {
337
+ window.dialogArguments = vArguments;
338
+ modalWin = window.open(sURL, 'modal', sFeatures);
339
+ return modalWin;
340
+ }
341
+ }
342
+ browser.execute_script script
343
+ yield if block_given?
344
+ end
345
+
346
+ #
347
+ # Attach to a running window. You can locate the window using either
348
+ # the window's title or url. If it fails to connect to a window it will
349
+ # pause for 1 second and try again.
350
+ #
351
+ # @example
352
+ # page.attach_to_window(:title => "other window's title")
353
+ #
354
+ # @param [Hash] either :title or :url of the other window. The url does not need to
355
+ # be the entire url - it can just be the page name like index.html
356
+ # @param block if present the block is executed and then execution is returned to the
357
+ # calling window
358
+ #
359
+ def attach_to_window(identifier, &block)
360
+ begin
361
+ platform.attach_to_window(identifier, &block)
362
+ rescue
363
+ sleep 1
364
+ platform.attach_to_window(identifier, &block)
365
+ end
366
+ end
367
+
368
+ #
369
+ # Find the element that has focus on the page
370
+ #
371
+ def element_with_focus
372
+ platform.element_with_focus
373
+ end
374
+
375
+ #
376
+ # Refresh to current page
377
+ #
378
+ def refresh
379
+ platform.refresh
380
+ end
381
+
382
+ #
383
+ # Go back to the previous page
384
+ #
385
+ def back
386
+ platform.back
387
+ end
388
+
389
+ #
390
+ # Go forward to the next page
391
+ #
392
+ def forward
393
+ platform.forward
394
+ end
395
+
396
+ #
397
+ # Clear the cookies from the browser
398
+ #
399
+ def clear_cookies
400
+ platform.clear_cookies
401
+ end
402
+
403
+ #
404
+ # Save the current screenshot to the provided url. File
405
+ # is saved as a png file.
406
+ #
407
+ def save_screenshot(file_name)
408
+ platform.save_screenshot file_name
409
+ end
410
+
411
+ #
412
+ # Check if root element exists and is visible
413
+ #
414
+ def present?
415
+ root.present?
416
+ end
417
+
418
+ def self.register_widget(widget_tag, widget_class, base_element_tag)
419
+ Widgets.register_widget(widget_tag, widget_class, base_element_tag)
420
+ end
421
+
422
+ private
423
+
424
+ def root
425
+ @root_element || PageObject::Platforms::Watir.browser_root_for(browser)
426
+ end
427
+
428
+ def call_block(&block)
429
+ block.arity == 1 ? block.call(self) : self.instance_eval(&block)
430
+ end
431
+ end
data/pageobject.gems ADDED
@@ -0,0 +1 @@
1
+ bundler