centric_page_object 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
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