page-object 2.2.6 → 2.3.0

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -1
  3. data/.gitignore +8 -8
  4. data/.rspec +2 -2
  5. data/.ruby-gemset +1 -1
  6. data/.ruby-version +1 -1
  7. data/.travis.yml +17 -17
  8. data/ChangeLog +923 -916
  9. data/Gemfile +13 -13
  10. data/Guardfile +20 -20
  11. data/LICENSE +20 -20
  12. data/README.md +114 -114
  13. data/Rakefile +29 -29
  14. data/cucumber.yml +8 -8
  15. data/lib/page-object.rb +431 -420
  16. data/lib/page-object/accessors.rb +1201 -1175
  17. data/lib/page-object/element_locators.rb +21 -21
  18. data/lib/page-object/elements.rb +62 -61
  19. data/lib/page-object/elements/area.rb +9 -9
  20. data/lib/page-object/elements/audio.rb +9 -9
  21. data/lib/page-object/elements/bold.rb +9 -9
  22. data/lib/page-object/elements/button.rb +12 -12
  23. data/lib/page-object/elements/canvas.rb +10 -10
  24. data/lib/page-object/elements/check_box.rb +9 -9
  25. data/lib/page-object/elements/date_field.rb +10 -0
  26. data/lib/page-object/elements/div.rb +9 -9
  27. data/lib/page-object/elements/element.rb +212 -209
  28. data/lib/page-object/elements/file_field.rb +9 -9
  29. data/lib/page-object/elements/form.rb +9 -9
  30. data/lib/page-object/elements/heading.rb +14 -14
  31. data/lib/page-object/elements/hidden_field.rb +9 -9
  32. data/lib/page-object/elements/image.rb +10 -10
  33. data/lib/page-object/elements/italic.rb +9 -9
  34. data/lib/page-object/elements/label.rb +9 -9
  35. data/lib/page-object/elements/link.rb +9 -9
  36. data/lib/page-object/elements/list_item.rb +9 -9
  37. data/lib/page-object/elements/media.rb +11 -11
  38. data/lib/page-object/elements/option.rb +9 -9
  39. data/lib/page-object/elements/ordered_list.rb +43 -43
  40. data/lib/page-object/elements/paragraph.rb +9 -9
  41. data/lib/page-object/elements/radio_button.rb +9 -9
  42. data/lib/page-object/elements/select_list.rb +42 -42
  43. data/lib/page-object/elements/span.rb +9 -9
  44. data/lib/page-object/elements/table.rb +85 -85
  45. data/lib/page-object/elements/table_cell.rb +10 -10
  46. data/lib/page-object/elements/table_row.rb +52 -52
  47. data/lib/page-object/elements/text_area.rb +9 -9
  48. data/lib/page-object/elements/text_field.rb +10 -10
  49. data/lib/page-object/elements/unordered_list.rb +42 -42
  50. data/lib/page-object/elements/video.rb +9 -9
  51. data/lib/page-object/indexed_properties.rb +41 -41
  52. data/lib/page-object/javascript/angularjs.rb +14 -14
  53. data/lib/page-object/javascript/jquery.rb +14 -14
  54. data/lib/page-object/javascript/prototype.rb +14 -14
  55. data/lib/page-object/javascript/yui.rb +18 -18
  56. data/lib/page-object/javascript_framework_facade.rb +80 -80
  57. data/lib/page-object/locator_generator.rb +183 -182
  58. data/lib/page-object/nested_elements.rb +17 -17
  59. data/lib/page-object/page_factory.rb +108 -108
  60. data/lib/page-object/page_populator.rb +105 -105
  61. data/lib/page-object/platforms/watir.rb +50 -50
  62. data/lib/page-object/platforms/watir/page_object.rb +1155 -1124
  63. data/lib/page-object/section_collection.rb +16 -16
  64. data/lib/page-object/version.rb +4 -4
  65. data/lib/page-object/widgets.rb +98 -98
  66. data/page-object.gemspec +32 -32
  67. metadata +8 -7
@@ -1,1175 +1,1201 @@
1
- require 'erb'
2
- require 'page-object/locator_generator'
3
-
4
- module PageObject
5
- #
6
- # Contains the class level methods that are inserted into your page objects
7
- # when you include the PageObject module. These methods will generate another
8
- # set of methods that provide access to the elements on the web pages.
9
- #
10
- module Accessors
11
-
12
- #
13
- # Set some values that can be used within the class. This is
14
- # typically used to provide values that help build dynamic urls in
15
- # the page_url method
16
- #
17
- # @param [Hash] the value to set the params
18
- #
19
- def params=(the_params)
20
- @params = the_params
21
- end
22
-
23
- #
24
- # Return the params that exist on this page class
25
- #
26
- def params
27
- @params ||= {}
28
- end
29
-
30
- #
31
- # Specify the url for the page. A call to this method will generate a
32
- # 'goto' method to take you to the page.
33
- #
34
- # @param [String] the url for the page.
35
- # @param [Symbol] a method name to call to get the url
36
- #
37
- def page_url(url)
38
- define_method("goto") do
39
- platform.navigate_to self.page_url_value
40
- end
41
-
42
- define_method('page_url_value') do
43
- lookup = url.kind_of?(Symbol) ? self.send(url) : url
44
- erb = ERB.new(%Q{#{lookup}})
45
- merged_params = self.class.instance_variable_get("@merged_params")
46
- params = merged_params ? merged_params : self.class.params
47
- erb.result(binding)
48
- end
49
- end
50
- alias_method :direct_url, :page_url
51
-
52
- #
53
- # Creates a method that waits the expected_title of a page to match the actual.
54
- # @param [String] expected_title the literal expected title for the page
55
- # @param [Regexp] expected_title the expected title pattern for the page
56
- # @param [optional, Integer] timeout default value is nil - do not wait
57
- # @return [boolean]
58
- # @raise An exception if expected_title does not match actual title
59
- #
60
- # @example Specify 'Google' as the expected title of a page
61
- # expected_title "Google"
62
- # page.has_expected_title?
63
- #
64
- def wait_for_expected_title(expected_title, timeout=::PageObject.default_element_wait)
65
- define_method("wait_for_expected_title?") do
66
- error_message = lambda { "Expected title '#{expected_title}' instead of '#{title}'" }
67
-
68
- has_expected_title = (expected_title === title)
69
- wait_until(timeout, error_message.call) do
70
- has_expected_title = (expected_title === title)
71
- end unless has_expected_title
72
-
73
- raise error_message.call unless has_expected_title
74
- has_expected_title
75
- end
76
- end
77
-
78
- #
79
- # Creates a method that compares the expected_title of a page against the actual.
80
- # @param [String] expected_title the literal expected title for the page
81
- # @param [Regexp] expected_title the expected title pattern for the page
82
- # @return [boolean]
83
- # @raise An exception if expected_title does not match actual title
84
- #
85
- # @example Specify 'Google' as the expected title of a page
86
- # expected_title "Google"
87
- # page.has_expected_title?
88
- #
89
- def expected_title(expected_title)
90
- define_method("has_expected_title?") do
91
- page_title = title
92
- has_expected_title = (expected_title === page_title)
93
- raise "Expected title '#{expected_title}' instead of '#{page_title}'" unless has_expected_title
94
- has_expected_title
95
- end
96
- end
97
-
98
- #
99
- # Creates a method that provides a way to initialize a page based upon an expected element.
100
- # This is useful for pages that load dynamic content.
101
- # @param [Symbol] the name given to the element in the declaration
102
- # @param [optional, Integer] timeout default value is 5 seconds
103
- # @return [boolean]
104
- #
105
- # @example Specify a text box named :address expected on the page within 10 seconds
106
- # expected_element(:address, 10)
107
- # page.has_expected_element?
108
- #
109
- def expected_element(element_name, timeout=::PageObject.default_element_wait)
110
- define_method("has_expected_element?") do
111
- self.respond_to? "#{element_name}_element" and self.send("#{element_name}_element").when_present timeout
112
- end
113
- end
114
-
115
- #
116
- # Creates a method that provides a way to initialize a page based upon an expected element to become visible.
117
- # This is useful for pages that load dynamic content and might have hidden elements that are not shown.
118
- # @param [Symbol] the name given to the element in the declaration
119
- # @param [optional, Integer] timeout default value is 5 seconds
120
- # @param [optional, boolean] also check that element to be visible if set to true
121
- # @return [boolean]
122
- #
123
- # @example Specify a text box named :address expected on the page within 10 seconds
124
- # expected_element_visible(:address, 10)
125
- # page.has_expected_element_visible?
126
- #
127
- def expected_element_visible(element_name, timeout=::PageObject.default_element_wait, check_visible=false)
128
- define_method("has_expected_element_visible?") do
129
- self.respond_to? "#{element_name}_element" and self.send("#{element_name}_element").when_present timeout
130
- self.respond_to? "#{element_name}_element" and self.send("#{element_name}_element").when_visible timeout
131
- end
132
- end
133
-
134
- #
135
- # Identify an element as existing within a frame . A frame parameter
136
- # is passed to the block and must be passed to the other calls to PageObject.
137
- # You can nest calls to in_frame by passing the frame to the next level.
138
- #
139
- # @example
140
- # in_frame(:id => 'frame_id') do |frame|
141
- # text_field(:first_name, :id => 'fname', :frame => frame)
142
- # end
143
- #
144
- # @param [Hash] identifier how we find the frame. The valid keys are:
145
- # * :id
146
- # * :index
147
- # * :name
148
- # * :regexp
149
- # @param frame passed from a previous call to in_frame. Used to nest calls
150
- # @param block that contains the calls to elements that exist inside the frame.
151
- #
152
- def in_frame(identifier, frame=nil, &block)
153
- frame = frame.nil? ? [] : frame.dup
154
- frame << {frame: identifier}
155
- block.call(frame)
156
- end
157
-
158
- #
159
- # Identify an element as existing within an iframe. A frame parameter
160
- # is passed to the block and must be passed to the other calls to PageObject.
161
- # You can nest calls to in_frame by passing the frame to the next level.
162
- #
163
- # @example
164
- # in_iframe(:id => 'frame_id') do |frame|
165
- # text_field(:first_name, :id => 'fname', :frame => frame)
166
- # end
167
- #
168
- # @param [Hash] identifier how we find the frame. The valid keys are:
169
- # * :id
170
- # * :index
171
- # * :name
172
- # * :regexp
173
- # @param frame passed from a previous call to in_iframe. Used to nest calls
174
- # @param block that contains the calls to elements that exist inside the iframe.
175
- #
176
- def in_iframe(identifier, frame=nil, &block)
177
- frame = frame.nil? ? [] : frame.dup
178
- frame << {iframe: identifier}
179
- block.call(frame)
180
- end
181
-
182
- #
183
- # adds four methods to the page object - one to set text in a text field,
184
- # another to retrieve text from a text field, another to return the text
185
- # field element, another to check the text field's existence.
186
- #
187
- # @example
188
- # text_field(:first_name, :id => "first_name")
189
- # # will generate 'first_name', 'first_name=', 'first_name_element',
190
- # # 'first_name?' methods
191
- #
192
- # @param [String] the name used for the generated methods
193
- # @param [Hash] identifier how we find a text field.
194
- # @param optional block to be invoked when element method is called
195
- #
196
- def text_field(name, identifier={:index => 0}, &block)
197
- standard_methods(name, identifier, 'text_field_for', &block)
198
- define_method(name) do
199
- return platform.text_field_value_for identifier.clone unless block_given?
200
- self.send("#{name}_element").value
201
- end
202
- define_method("#{name}=") do |value|
203
- return platform.text_field_value_set(identifier.clone, value) unless block_given?
204
- self.send("#{name}_element").value = value
205
- end
206
- end
207
-
208
- #
209
- # adds three methods to the page object - one to get the text from a hidden field,
210
- # another to retrieve the hidden field element, and another to check the hidden
211
- # field's existence.
212
- #
213
- # @example
214
- # hidden_field(:user_id, :id => "user_identity")
215
- # # will generate 'user_id', 'user_id_element' and 'user_id?' methods
216
- #
217
- # @param [String] the name used for the generated methods
218
- # @param [Hash] identifier how we find a hidden field.
219
- # @param optional block to be invoked when element method is called
220
- #
221
- def hidden_field(name, identifier={:index => 0}, &block)
222
- standard_methods(name, identifier, 'hidden_field_for', &block)
223
- define_method(name) do
224
- return platform.hidden_field_value_for identifier.clone unless block_given?
225
- self.send("#{name}_element").value
226
- end
227
- end
228
- alias_method :hidden, :hidden_field
229
-
230
- #
231
- # adds four methods to the page object - one to set text in a text area,
232
- # another to retrieve text from a text area, another to return the text
233
- # area element, and another to check the text area's existence.
234
- #
235
- # @example
236
- # text_area(:address, :id => "address")
237
- # # will generate 'address', 'address=', 'address_element',
238
- # # 'address?' methods
239
- #
240
- # @param [String] the name used for the generated methods
241
- # @param [Hash] identifier how we find a text area.
242
- # @param optional block to be invoked when element method is called
243
- #
244
- def text_area(name, identifier={:index => 0}, &block)
245
- standard_methods(name, identifier, 'text_area_for', &block)
246
- define_method(name) do
247
- return platform.text_area_value_for identifier.clone unless block_given?
248
- self.send("#{name}_element").value
249
- end
250
- define_method("#{name}=") do |value|
251
- return platform.text_area_value_set(identifier.clone, value) unless block_given?
252
- self.send("#{name}_element").value = value
253
- end
254
- end
255
- alias_method :textarea, :text_area
256
-
257
- #
258
- # adds five methods - one to select an item in a drop-down,
259
- # another to fetch the currently selected item text, another
260
- # to retrieve the select list element, another to check the
261
- # drop down's existence and another to get all the available options
262
- # to select from.
263
- #
264
- # @example
265
- # select_list(:state, :id => "state")
266
- # # will generate 'state', 'state=', 'state_element', 'state?', "state_options" methods
267
- #
268
- # @param [Symbol] the name used for the generated methods
269
- # @param [Hash] identifier how we find a select list.
270
- # @param optional block to be invoked when element method is called
271
- #
272
- def select_list(name, identifier={:index => 0}, &block)
273
- standard_methods(name, identifier, 'select_list_for', &block)
274
- define_method(name) do
275
- return platform.select_list_value_for identifier.clone unless block_given?
276
- self.send("#{name}_element").value
277
- end
278
- define_method("#{name}=") do |value|
279
- return platform.select_list_value_set(identifier.clone, value) unless block_given?
280
- self.send("#{name}_element").select(value)
281
- end
282
- define_method("#{name}_options") do
283
- element = self.send("#{name}_element")
284
- (element && element.options) ? element.options.collect(&:text) : []
285
- end
286
- end
287
- alias_method :select, :select_list
288
-
289
- #
290
- # adds three methods - one to select a link, another
291
- # to return a PageObject::Elements::Link object representing
292
- # the link, and another that checks the link's existence.
293
- #
294
- # @example
295
- # link(:add_to_cart, :text => "Add to Cart")
296
- # # will generate 'add_to_cart', 'add_to_cart_element', and 'add_to_cart?' methods
297
- #
298
- # @param [Symbol] the name used for the generated methods
299
- # @param [Hash] identifier how we find a link.
300
- # @param optional block to be invoked when element method is called
301
- #
302
- def link(name, identifier={:index => 0}, &block)
303
- standard_methods(name, identifier, 'link_for', &block)
304
- define_method(name) do
305
- return platform.click_link_for identifier.clone unless block_given?
306
- self.send("#{name}_element").click
307
- end
308
- end
309
- alias_method :a, :link
310
-
311
- #
312
- # adds five methods - one to check, another to uncheck, another
313
- # to return the state of a checkbox, another to return
314
- # a PageObject::Elements::CheckBox object representing the checkbox, and
315
- # a final method to check the checkbox's existence.
316
- #
317
- # @example
318
- # checkbox(:active, :name => "is_active")
319
- # # will generate 'check_active', 'uncheck_active', 'active_checked?',
320
- # # 'active_element', and 'active?' methods
321
- #
322
- # @param [Symbol] the name used for the generated methods
323
- # @param [Hash] identifier how we find a checkbox.
324
- # @param optional block to be invoked when element method is called
325
- #
326
- def checkbox(name, identifier={:index => 0}, &block)
327
- standard_methods(name, identifier, 'checkbox_for', &block)
328
- define_method("check_#{name}") do
329
- return platform.check_checkbox(identifier.clone) unless block_given?
330
- self.send("#{name}_element").check
331
- end
332
- define_method("uncheck_#{name}") do
333
- return platform.uncheck_checkbox(identifier.clone) unless block_given?
334
- self.send("#{name}_element").uncheck
335
- end
336
- define_method("#{name}_checked?") do
337
- return platform.checkbox_checked?(identifier.clone) unless block_given?
338
- self.send("#{name}_element").checked?
339
- end
340
- end
341
-
342
- #
343
- # adds four methods - one to select, another to return if a radio button
344
- # is selected, another method to return a PageObject::Elements::RadioButton
345
- # object representing the radio button element, and another to check
346
- # the radio button's existence.
347
- #
348
- # @example
349
- # radio_button(:north, :id => "north")
350
- # # will generate 'select_north', 'north_selected?',
351
- # # 'north_element', and 'north?' methods
352
- #
353
- # @param [Symbol] the name used for the generated methods
354
- # @param [Hash] identifier how we find a radio button.
355
- # @param optional block to be invoked when element method is called
356
- #
357
- def radio_button(name, identifier={:index => 0}, &block)
358
- standard_methods(name, identifier, 'radio_button_for', &block)
359
- define_method("select_#{name}") do
360
- return platform.select_radio(identifier.clone) unless block_given?
361
- self.send("#{name}_element").select
362
- end
363
- define_method("#{name}_selected?") do
364
- return platform.radio_selected?(identifier.clone) unless block_given?
365
- self.send("#{name}_element").selected?
366
- end
367
- end
368
- alias_method :radio, :radio_button
369
-
370
- #
371
- # adds five methods to help interact with a radio button group -
372
- # a method to select a radio button in the group by given value/text,
373
- # a method to return the values of all radio buttons in the group, a method
374
- # to return if a radio button in the group is selected (will return
375
- # the text of the selected radio button, if true), a method to return
376
- # an array of PageObject::Elements::RadioButton objects representing
377
- # the radio button group, and finally a method to check the existence
378
- # of the radio button group.
379
- #
380
- # @example
381
- # radio_button_group(:color, :name => "preferred_color")
382
- # will generate 'select_color', 'color_values', 'color_selected?',
383
- # 'color_elements', and 'color?' methods
384
- #
385
- # @param [Symbol] the name used for the generated methods
386
- # @param [Hash] shared identifier for the radio button group. Typically, a 'name' attribute.
387
- # The valid keys are:
388
- # * :name
389
- #
390
- def radio_button_group(name, identifier)
391
- define_method("select_#{name}") do |value|
392
- platform.radio_buttons_for(identifier.clone).each do |radio_elem|
393
- if radio_elem.value == value
394
- return radio_elem.select
395
- end
396
- end
397
- end
398
- define_method("#{name}_values") do
399
- result = []
400
- platform.radio_buttons_for(identifier.clone).each do |radio_elem|
401
- result << radio_elem.value
402
- end
403
- return result
404
- end
405
- define_method("#{name}_selected?") do
406
- platform.radio_buttons_for(identifier.clone).each do |radio_elem|
407
- return radio_elem.value if radio_elem.selected?
408
- end
409
- return false
410
- end
411
- define_method("#{name}_elements") do
412
- return platform.radio_buttons_for(identifier.clone)
413
- end
414
- define_method("#{name}?") do
415
- return platform.radio_buttons_for(identifier.clone).any?
416
- end
417
- end
418
- alias_method :radio_group, :radio_button_group
419
-
420
- #
421
- # adds three methods - one to click a button, another to
422
- # return the button element, and another to check the button's existence.
423
- #
424
- # @example
425
- # button(:purchase, :id => 'purchase')
426
- # # will generate 'purchase', 'purchase_element', and 'purchase?' methods
427
- #
428
- # @param [Symbol] the name used for the generated methods
429
- # @param [Hash] identifier how we find a button.
430
- # @param optional block to be invoked when element method is called
431
- #
432
- def button(name, identifier={:index => 0}, &block)
433
- standard_methods(name, identifier, 'button_for', &block)
434
- define_method(name) do
435
- return platform.click_button_for identifier.clone unless block_given?
436
- self.send("#{name}_element").click
437
- end
438
- end
439
-
440
- #
441
- # adds three methods - one to retrieve the text from a div,
442
- # another to return the div element, and another to check the div's existence.
443
- #
444
- # @example
445
- # div(:message, :id => 'message')
446
- # # will generate 'message', 'message_element', and 'message?' methods
447
- #
448
- # @param [Symbol] the name used for the generated methods
449
- # @param [Hash] identifier how we find a div.
450
- # @param optional block to be invoked when element method is called
451
- #
452
- def div(name, identifier={:index => 0}, &block)
453
- standard_methods(name, identifier, 'div_for', &block)
454
- define_method(name) do
455
- return platform.div_text_for identifier.clone unless block_given?
456
- self.send("#{name}_element").text
457
- end
458
- end
459
-
460
- #
461
- # adds three methods - one to retrieve the text from a span,
462
- # another to return the span element, and another to check the span's existence.
463
- #
464
- # @example
465
- # span(:alert, :id => 'alert')
466
- # # will generate 'alert', 'alert_element', and 'alert?' methods
467
- #
468
- # @param [Symbol] the name used for the generated methods
469
- # @param [Hash] identifier how we find a span.
470
- # @param optional block to be invoked when element method is called
471
- #
472
- def span(name, identifier={:index => 0}, &block)
473
- standard_methods(name, identifier, 'span_for', &block)
474
- define_method(name) do
475
- return platform.span_text_for identifier.clone unless block_given?
476
- self.send("#{name}_element").text
477
- end
478
- end
479
-
480
- #
481
- # adds three methods - one to return the text for the table, one
482
- # to retrieve the table element, and another to
483
- # check the table's existence.
484
- #
485
- # @example
486
- # table(:cart, :id => 'shopping_cart')
487
- # # will generate a 'cart', 'cart_element' and 'cart?' method
488
- #
489
- # @param [Symbol] the name used for the generated methods
490
- # @param [Hash] identifier how we find a table.
491
- # @param optional block to be invoked when element method is called
492
- #
493
- def table(name, identifier={:index => 0}, &block)
494
- standard_methods(name, identifier, 'table_for', &block)
495
- define_method(name) do
496
- return platform.table_text_for identifier.clone unless block_given?
497
- self.send("#{name}_element").text
498
- end
499
- end
500
-
501
- #
502
- # adds three methods - one to retrieve the text from a table cell,
503
- # another to return the table cell element, and another to check the cell's
504
- # existence.
505
- #
506
- # @example
507
- # cell(:total, :id => 'total_cell')
508
- # # will generate 'total', 'total_element', and 'total?' methods
509
- #
510
- # @param [Symbol] the name used for the generated methods
511
- # @param [Hash] identifier how we find a cell.
512
- # @param optional block to be invoked when element method is called
513
- #
514
- def cell(name, identifier={:index => 0}, &block)
515
- standard_methods(name, identifier, 'cell_for', &block)
516
- define_method("#{name}") do
517
- return platform.cell_text_for identifier.clone unless block_given?
518
- self.send("#{name}_element").text
519
- end
520
- end
521
- alias_method :td, :cell
522
-
523
-
524
- #
525
- # adds three methods - one to retrieve the text from a table row,
526
- # another to return the table row element, and another to check the row's
527
- # existence.
528
- #
529
- # @example
530
- # row(:sums, :id => 'sum_row')
531
- # # will generate 'sums', 'sums_element', and 'sums?' methods
532
- #
533
- # @param [Symbol] the name used for the generated methods
534
- # @param [Hash] identifier how we find a cell.
535
- # @param optional block to be invoked when element method is called
536
- #
537
- def row(name, identifier={:index => 0}, &block)
538
- standard_methods(name, identifier, 'row_for', &block)
539
- define_method("#{name}") do
540
- return platform.row_text_for identifier.clone unless block_given?
541
- self.send("#{name}_element").text
542
- end
543
- end
544
-
545
- #
546
- # adds three methods - one to retrieve the image element, another to
547
- # check the load status of the image, and another to check the
548
- # image's existence.
549
- #
550
- # @example
551
- # image(:logo, :id => 'logo')
552
- # # will generate 'logo_element', 'logo_loaded?', and 'logo?' methods
553
- #
554
- # @param [Symbol] the name used for the generated methods
555
- # @param [Hash] identifier how we find an image.
556
- # @param optional block to be invoked when element method is called
557
- #
558
- def image(name, identifier={:index => 0}, &block)
559
- standard_methods(name, identifier, 'image_for', &block)
560
- define_method("#{name}_loaded?") do
561
- return platform.image_loaded_for identifier.clone unless block_given?
562
- self.send("#{name}_element").loaded?
563
- end
564
- end
565
- alias_method :img, :image
566
-
567
- #
568
- # adds two methods - one to retrieve the form element, and another to
569
- # check the form's existence.
570
- #
571
- # @example
572
- # form(:login, :id => 'login')
573
- # # will generate 'login_element' and 'login?' methods
574
- #
575
- # @param [Symbol] the name used for the generated methods
576
- # @param [Hash] identifier how we find a form.
577
- # @param optional block to be invoked when element method is called
578
- #
579
- def form(name, identifier={:index => 0}, &block)
580
- standard_methods(name, identifier, 'form_for', &block)
581
- end
582
-
583
- #
584
- # adds three methods - one to retrieve the text from a list item,
585
- # another to return the list item element, and another to check the list item's
586
- # existence.
587
- #
588
- # @example
589
- # list_item(:item_one, :id => 'one')
590
- # # will generate 'item_one', 'item_one_element', and 'item_one?' methods
591
- #
592
- # @param [Symbol] the name used for the generated methods
593
- # @param [Hash] identifier how we find a list item.
594
- # @param optional block to be invoked when element method is called
595
- #
596
- def list_item(name, identifier={:index => 0}, &block)
597
- standard_methods(name, identifier, 'list_item_for', &block)
598
- define_method(name) do
599
- return platform.list_item_text_for identifier.clone unless block_given?
600
- self.send("#{name}_element").text
601
- end
602
- end
603
- alias_method :li, :list_item
604
-
605
- #
606
- # adds three methods - one to return the text within the unordered
607
- # list, one to retrieve the unordered list element, and another to
608
- # check it's existence.
609
- #
610
- # @example
611
- # unordered_list(:menu, :id => 'main_menu')
612
- # # will generate 'menu', 'menu_element' and 'menu?' methods
613
- #
614
- # @param [Symbol] the name used for the generated methods
615
- # @param [Hash] identifier how we find an unordered list.
616
- # @param optional block to be invoked when element method is called
617
- #
618
- def unordered_list(name, identifier={:index => 0}, &block)
619
- standard_methods(name, identifier, 'unordered_list_for', &block)
620
- define_method(name) do
621
- return platform.unordered_list_text_for identifier.clone unless block_given?
622
- self.send("#{name}_element").text
623
- end
624
- end
625
- alias_method :ul, :unordered_list
626
-
627
- #
628
- # adds three methods - one to return the text within the ordered
629
- # list, one to retrieve the ordered list element, and another to
630
- # test it's existence.
631
- #
632
- # @example
633
- # ordered_list(:top_five, :id => 'top')
634
- # # will generate 'top_five', 'top_five_element' and 'top_five?' methods
635
- #
636
- # @param [Symbol] the name used for the generated methods
637
- # @param [Hash] identifier how we find an ordered list.
638
- # @param optional block to be invoked when element method is called
639
- #
640
- def ordered_list(name, identifier={:index => 0}, &block)
641
- standard_methods(name, identifier, 'ordered_list_for', &block)
642
- define_method(name) do
643
- return platform.ordered_list_text_for identifier.clone unless block_given?
644
- self.send("#{name}_element").text
645
- end
646
- end
647
- alias_method :ol, :ordered_list
648
-
649
- #
650
- # adds three methods - one to retrieve the text of a h1 element, another to
651
- # retrieve a h1 element, and another to check for it's existence.
652
- #
653
- # @example
654
- # h1(:title, :id => 'title')
655
- # # will generate 'title', 'title_element', and 'title?' methods
656
- #
657
- # @param [Symbol] the name used for the generated methods
658
- # @param [Hash] identifier how we find a H1. You can use a multiple parameters
659
- # by combining of any of the following except xpath.
660
- # @param optional block to be invoked when element method is called
661
- #
662
- def h1(name, identifier={:index => 0}, &block)
663
- standard_methods(name, identifier,'h1_for', &block)
664
- define_method(name) do
665
- return platform.h1_text_for identifier.clone unless block_given?
666
- self.send("#{name}_element").text
667
- end
668
- end
669
-
670
- #
671
- # adds three methods - one to retrieve the text of a h2 element, another
672
- # to retrieve a h2 element, and another to check for it's existence.
673
- #
674
- # @example
675
- # h2(:title, :id => 'title')
676
- # # will generate 'title', 'title_element', and 'title?' methods
677
- #
678
- # @param [Symbol] the name used for the generated methods
679
- # @param [Hash] identifier how we find a H2.
680
- # @param optional block to be invoked when element method is called
681
- #
682
- def h2(name, identifier={:index => 0}, &block)
683
- standard_methods(name, identifier, 'h2_for', &block)
684
- define_method(name) do
685
- return platform.h2_text_for identifier.clone unless block_given?
686
- self.send("#{name}_element").text
687
- end
688
- end
689
-
690
- #
691
- # adds three methods - one to retrieve the text of a h3 element,
692
- # another to return a h3 element, and another to check for it's existence.
693
- #
694
- # @example
695
- # h3(:title, :id => 'title')
696
- # # will generate 'title', 'title_element', and 'title?' methods
697
- #
698
- # @param [Symbol] the name used for the generated methods
699
- # @param [Hash] identifier how we find a H3.
700
- # @param optional block to be invoked when element method is called
701
- #
702
- def h3(name, identifier={:index => 0}, &block)
703
- standard_methods(name, identifier, 'h3_for', &block)
704
- define_method(name) do
705
- return platform.h3_text_for identifier.clone unless block_given?
706
- self.send("#{name}_element").text
707
- end
708
- end
709
-
710
- #
711
- # adds three methods - one to retrieve the text of a h4 element,
712
- # another to return a h4 element, and another to check for it's existence.
713
- #
714
- # @example
715
- # h4(:title, :id => 'title')
716
- # # will generate 'title', 'title_element', and 'title?' methods
717
- #
718
- # @param [Symbol] the name used for the generated methods
719
- # @param [Hash] identifier how we find a H4.
720
- # @param optional block to be invoked when element method is called
721
- #
722
- def h4(name, identifier={:index => 0}, &block)
723
- standard_methods(name, identifier, 'h4_for', &block)
724
- define_method(name) do
725
- return platform.h4_text_for identifier.clone unless block_given?
726
- self.send("#{name}_element").text
727
- end
728
- end
729
-
730
- #
731
- # adds three methods - one to retrieve the text of a h5 element,
732
- # another to return a h5 element, and another to check for it's existence.
733
- #
734
- # @example
735
- # h5(:title, :id => 'title')
736
- # # will generate 'title', 'title_element', and 'title?' methods
737
- #
738
- # @param [Symbol] the name used for the generated methods
739
- # @param [Hash] identifier how we find a H5.
740
- # @param optional block to be invoked when element method is called
741
- #
742
- def h5(name, identifier={:index => 0}, &block)
743
- standard_methods(name, identifier, 'h5_for', &block)
744
- define_method(name) do
745
- return platform.h5_text_for identifier.clone unless block_given?
746
- self.send("#{name}_element").text
747
- end
748
- end
749
-
750
- #
751
- # adds three methods - one to retrieve the text of a h6 element,
752
- # another to return a h6 element, and another to check for it's existence.
753
- #
754
- # @example
755
- # h6(:title, :id => 'title')
756
- # # will generate 'title', 'title_element', and 'title?' methods
757
- #
758
- # @param [Symbol] the name used for the generated methods
759
- # @param [Hash] identifier how we find a H6.
760
- # @param optional block to be invoked when element method is called
761
- #
762
- def h6(name, identifier={:index => 0}, &block)
763
- standard_methods(name, identifier, 'h6_for', &block)
764
- define_method(name) do
765
- return platform.h6_text_for identifier.clone unless block_given?
766
- self.send("#{name}_element").text
767
- end
768
- end
769
-
770
- #
771
- # adds three methods - one to retrieve the text of a paragraph, another
772
- # to retrieve a paragraph element, and another to check the paragraph's existence.
773
- #
774
- # @example
775
- # paragraph(:title, :id => 'title')
776
- # # will generate 'title', 'title_element', and 'title?' methods
777
- #
778
- # @param [Symbol] the name used for the generated methods
779
- # @param [Hash] identifier how we find a paragraph.
780
- # @param optional block to be invoked when element method is called
781
- #
782
- def paragraph(name, identifier={:index => 0}, &block)
783
- standard_methods(name, identifier, 'paragraph_for', &block)
784
- define_method(name) do
785
- return platform.paragraph_text_for identifier.clone unless block_given?
786
- self.send("#{name}_element").text
787
- end
788
- end
789
- alias_method :p, :paragraph
790
-
791
- #
792
- # adds three methods - one to set the file for a file field, another to retrieve
793
- # the file field element, and another to check it's existence.
794
- #
795
- # @example
796
- # file_field(:the_file, :id => 'file_to_upload')
797
- # # will generate 'the_file=', 'the_file_element', and 'the_file?' methods
798
- #
799
- # @param [Symbol] the name used for the generated methods
800
- # @param [Hash] identifier how we find a file_field.
801
- # @param optional block to be invoked when element method is called
802
- #
803
- def file_field(name, identifier={:index => 0}, &block)
804
- standard_methods(name, identifier, 'file_field_for', &block)
805
- define_method("#{name}=") do |value|
806
- return platform.file_field_value_set(identifier.clone, value) unless block_given?
807
- self.send("#{name}_element").value = value
808
- end
809
- end
810
-
811
- #
812
- # adds three methods - one to retrieve the text from a label,
813
- # another to return the label element, and another to check the label's existence.
814
- #
815
- # @example
816
- # label(:message, :id => 'message')
817
- # # will generate 'message', 'message_element', and 'message?' methods
818
- #
819
- # @param [Symbol] the name used for the generated methods
820
- # @param [Hash] identifier how we find a label.
821
- # @param optional block to be invoked when element method is called
822
- #
823
- def label(name, identifier={:index => 0}, &block)
824
- standard_methods(name, identifier, 'label_for', &block)
825
- define_method(name) do
826
- return platform.label_text_for identifier.clone unless block_given?
827
- self.send("#{name}_element").text
828
- end
829
- end
830
-
831
- #
832
- # adds three methods - one to click the area,
833
- # another to return the area element, and another to check the area's existence.
834
- #
835
- # @example
836
- # area(:message, :id => 'message')
837
- # # will generate 'message', 'message_element', and 'message?' methods
838
- #
839
- # @param [Symbol] the name used for the generated methods
840
- # @param [Hash] identifier how we find an area.
841
- # @param optional block to be invoked when element method is called
842
- #
843
- def area(name, identifier={:index => 0}, &block)
844
- standard_methods(name, identifier, 'area_for', &block)
845
- define_method(name) do
846
- return platform.click_area_for identifier.clone unless block_given?
847
- self.send("#{name}_element").click
848
- end
849
- end
850
-
851
- #
852
- # adds two methods - one to return the canvas element and another to check
853
- # the canvas's existence.
854
- #
855
- # @example
856
- # canvas(:my_canvas, :id => 'canvas_id')
857
- # # will generate 'my_canvas_element' and 'my_canvas?' methods
858
- #
859
- # @param [Symbol] the name used for the generated methods
860
- # @param [Hash] identifier how we find a canvas.
861
- # @param optional block to be invoked when element method is called
862
- #
863
- def canvas(name, identifier={:index => 0}, &block)
864
- standard_methods(name, identifier, 'canvas_for', &block)
865
- end
866
-
867
- #
868
- # adds two methods - one to return the audio element and another to check
869
- # the audio's existence.
870
- #
871
- # @example
872
- # audio(:acdc, :id => 'audio_id')
873
- # # will generate 'acdc_element' and 'acdc?' methods
874
- #
875
- # @param [Symbol] the name used for the generated methods
876
- # @param [Hash] identifier how we find an audio element.
877
- # @param optional block to be invoked when element method is called
878
- #
879
- def audio(name, identifier={:index => 0}, &block)
880
- standard_methods(name, identifier, 'audio_for', &block)
881
- end
882
-
883
- #
884
- # adds two methods - one to return the video element and another to check
885
- # the video's existence.
886
- #
887
- # @example
888
- # video(:movie, :id => 'video_id')
889
- # # will generate 'movie_element' and 'movie?' methods
890
- #
891
- # @param [Symbol] the name used for the generated methods
892
- # @param [Hash] identifier how we find a video element.
893
- # @param optional block to be invoked when element method is called
894
- #
895
- def video(name, identifier={:index => 0}, &block)
896
- standard_methods(name, identifier, 'video_for', &block)
897
- end
898
-
899
- #
900
- # adds three methods - one to retrieve the text of a b element, another to
901
- # retrieve a b element, and another to check for it's existence.
902
- #
903
- # @example
904
- # b(:bold, :id => 'title')
905
- # # will generate 'bold', 'bold_element', and 'bold?' methods
906
- #
907
- # @param [Symbol] the name used for the generated methods
908
- # @param [Hash] identifier how we find a b.
909
- # @param optional block to be invoked when element method is called
910
- #
911
- def b(name, identifier={:index => 0}, &block)
912
- standard_methods(name, identifier,'b_for', &block)
913
- define_method(name) do
914
- return platform.b_text_for identifier.clone unless block_given?
915
- self.send("#{name}_element").text
916
- end
917
- end
918
-
919
- #
920
- # adds three methods - one to retrieve the text of a i element, another to
921
- # retrieve a i element, and another to check for it's existence.
922
- #
923
- # @example
924
- # i(:italic, :id => 'title')
925
- # # will generate 'italic', 'italic_element', and 'italic?' methods
926
- #
927
- # @param [Symbol] the name used for the generated methods
928
- # @param [Hash] identifier how we find a i.
929
- # @param optional block to be invoked when element method is called
930
- #
931
- def i(name, identifier={:index => 0}, &block)
932
- standard_methods(name, identifier,'i_for', &block)
933
- define_method(name) do
934
- return platform.i_text_for identifier.clone unless block_given?
935
- self.send("#{name}_element").text
936
- end
937
- end
938
- alias_method :icon, :i
939
-
940
- #
941
- # adds two methods - one to retrieve a svg, and another to check
942
- # the svg's existence.
943
- #
944
- # @example
945
- # svg(:circle, :id => 'circle')
946
- # # will generate 'circle_element', and 'circle?' methods
947
- #
948
- # @param [Symbol] the name used for the generated methods
949
- # @param [Hash] identifier how we find a svg.
950
- # @param optional block to be invoked when element method is called
951
- #
952
- def svg(name, identifier={:index => 0}, &block)
953
- standard_methods(name, identifier, 'svg_for', &block)
954
- end
955
-
956
-
957
- #
958
- # adds three methods - one to retrieve the text of an element, another
959
- # to retrieve an element, and another to check the element's existence.
960
- #
961
- # @example
962
- # element(:title, :header, :id => 'title')
963
- # # will generate 'title', 'title_element', and 'title?' methods
964
- #
965
- # @param [Symbol] the name used for the generated methods
966
- # @param [Symbol] the name of the tag for the element
967
- # @param [Hash] identifier how we find an element.
968
- # @param optional block to be invoked when element method is called
969
- #
970
- def element(name, tag=:element, identifier={ :index => 0 }, &block)
971
- #
972
- # sets tag as element if not defined
973
- #
974
- if tag.is_a?(Hash)
975
- identifier = tag
976
- tag = :element
977
- end
978
-
979
- standard_methods(name, identifier, 'element_for', &block)
980
-
981
- define_method("#{name}") do
982
- element = self.send("#{name}_element")
983
-
984
- %w(Button TextField Radio Hidden CheckBox FileField).each do |klass|
985
- next unless element.element.class.to_s == "Watir::#{klass}"
986
- self.class.send(klass.gsub(/(.)([A-Z])/,'\1_\2').downcase, name, identifier, &block)
987
- return self.send name
988
- end
989
- element.text
990
- end
991
- define_method("#{name}_element") do
992
- return call_block(&block) if block_given?
993
- platform.element_for(tag, identifier.clone)
994
- end
995
- define_method("#{name}?") do
996
- self.send("#{name}_element").exists?
997
- end
998
- define_method("#{name}=") do |value|
999
- element = self.send("#{name}_element")
1000
-
1001
- klass = case element.element
1002
- when Watir::TextField
1003
- 'text_field'
1004
- when Watir::TextArea
1005
- 'text_area'
1006
- when Watir::Select
1007
- 'select_list'
1008
- when Watir::FileField
1009
- 'file_field'
1010
- else
1011
- raise "Can not set a #{element.element} element with #="
1012
- end
1013
- self.class.send(klass, name, identifier, &block)
1014
- self.send("#{name}=", value)
1015
- end
1016
- end
1017
-
1018
- #
1019
- # adds a method to return a collection of generic Element objects
1020
- # for a specific tag.
1021
- #
1022
- # @example
1023
- # elements(:title, :header, :id => 'title')
1024
- # # will generate ''title_elements'
1025
- #
1026
- # @param [Symbol] the name used for the generated methods
1027
- # @param [Symbol] the name of the tag for the element
1028
- # @param [Hash] identifier how we find an element.
1029
- # @param optional block to be invoked when element method is called
1030
- #
1031
- def elements(name, tag=:element, identifier={:index => 0}, &block)
1032
- #
1033
- # sets tag as element if not defined
1034
- #
1035
- if tag.is_a?(Hash)
1036
- identifier = tag
1037
- tag = :element
1038
- end
1039
-
1040
- define_method("#{name}_elements") do
1041
- return call_block(&block) if block_given?
1042
- platform.elements_for(tag, identifier.clone)
1043
- end
1044
- end
1045
-
1046
- #
1047
- # adds a method to return a page object rooted at an element
1048
- #
1049
- # @example
1050
- # page_section(:navigation_bar, NavigationBar, :id => 'nav-bar')
1051
- # # will generate 'navigation_bar'
1052
- #
1053
- # @param [Symbol] the name used for the generated methods
1054
- # @param [Class] the class to instantiate for the element
1055
- # @param [Hash] identifier how we find an element.
1056
- #
1057
- def page_section(name, section_class, identifier)
1058
- define_method(name) do
1059
- platform.page_for(identifier, section_class)
1060
- end
1061
- end
1062
-
1063
- #
1064
- # adds a method to return a collection of page objects rooted at elements
1065
- #
1066
- # @example
1067
- # page_sections(:articles, Article, :class => 'article')
1068
- # # will generate 'articles'
1069
- #
1070
- # @param [Symbol] the name used for the generated method
1071
- # @param [Class] the class to instantiate for each element
1072
- # @param [Hash] identifier how we find an element.
1073
- #
1074
- def page_sections(name, section_class, identifier)
1075
- define_method(name) do
1076
- platform.pages_for(identifier, section_class)
1077
- end
1078
- end
1079
-
1080
- #
1081
- # methods to generate accessors for types that follow the same
1082
- # pattern as element
1083
- #
1084
- # @example
1085
- # article(:my_article, :id => "article_id")
1086
- # will generate 'my_article', 'my_article_element' and 'my_article?'
1087
- # articles(:my_article, :id => 'article_id')
1088
- # will generate 'my_article_elements'
1089
- #
1090
- # @param [Symbol] the name used for the generated methods
1091
- # @param [Symbol] the name of the tag for the element
1092
- # @param [Hash] identifier how we find an element.
1093
- # @param optional block to be invoked when element method is called
1094
- #
1095
- LocatorGenerator::BASIC_ELEMENTS.each do |tag|
1096
- define_method(tag) do |name, *identifier, &block|
1097
- identifier = identifier[0] ? identifier[0] : {:index => 0}
1098
- element(name, tag, identifier, &block)
1099
- end
1100
- define_method("#{tag}s") do |name, *identifier, &block|
1101
- identifier = identifier[0] ? identifier[0] : {:index => 0}
1102
- elements(name, tag, identifier, &block)
1103
- end unless tag == :param
1104
- end
1105
-
1106
- def standard_methods(name, identifier, method, &block)
1107
- define_method("#{name}_element") do
1108
- return call_block(&block) if block_given?
1109
- platform.send(method, identifier.clone)
1110
- end
1111
- define_method("#{name}?") do
1112
- return call_block(&block).exists? if block_given?
1113
- platform.send(method, identifier.clone).exists?
1114
- end
1115
- end
1116
-
1117
- #
1118
- # adds a method that will return an indexed property. The property will respond to
1119
- # the [] method with an object that has a set of normal page_object properties that
1120
- # correspond to the definitions included in the identifier_list parameter, with the
1121
- # "what" of the "how and what" substituted based on the index provided to the []
1122
- # method.
1123
- #
1124
- # @example
1125
- # indexed_property(:title, [
1126
- # [:text_field, :field_1, :id => 'table[%s].field_1'],
1127
- # [:button, :button_1, :id => 'table[%s].button_1'],
1128
- # [:text_field, :field_2, :name => 'table[%s].field_2']
1129
- # ])
1130
- # # will generate a title method that responds to []. title['foo'] will return an object
1131
- # # that responds to the normal methods expected for two text_fields and a button with the
1132
- # # given names, using the given how and what with 'foo' substituted for the %s. title[123]
1133
- # # will do the same, using the integer 123 instead.
1134
- #
1135
- # @param [Symbol] the name used for the generated method
1136
- # @param [Array] definitions an array of definitions to define on the indexed property. Each
1137
- # entry in the array should contain two symbols and a hash, corresponding to one of the standard
1138
- # page_object properties with a single substitution marker in each value in the hash,
1139
- # e.g. [:text_field, :field_1, :id => 'table[%s].field_1']
1140
- #
1141
- def indexed_property (name, identifier_list)
1142
- define_method("#{name}") do
1143
- IndexedProperties::TableOfElements.new(@browser, identifier_list)
1144
- end
1145
- end
1146
-
1147
- #
1148
- # methods to fetch multiple elements of the same type
1149
- #
1150
- # adds a method to the page object to return all of the matching elements
1151
- #
1152
- # @example
1153
- # text_fields(:first_name, :id => "first_name")
1154
- # # will generate 'first_name_elements'
1155
- #
1156
- # @param [String] the name used for the generated methods
1157
- # @param [Hash] identifier how we find a text field. You can use a multiple parameters
1158
- # by combining of any of the following except xpath. The valid
1159
- # keys are the same ones supported by the standard methods.
1160
- # @param optional block to be invoked when element method is called
1161
- #
1162
- idx = LocatorGenerator::ADVANCED_ELEMENTS.find_index { |type| type == :checkbox }
1163
- elements = LocatorGenerator::ADVANCED_ELEMENTS.clone
1164
- elements[idx] = :checkboxe
1165
- elements.each do |method_name|
1166
- define_method("#{method_name}s") do |name, *identifier, &block|
1167
- define_method("#{name}_elements") do
1168
- return call_block(&block) unless block.nil?
1169
- platform_method = (method_name == :checkboxe) ? 'checkboxs_for' : "#{method_name.to_s}s_for"
1170
- platform.send platform_method, (identifier.first ? identifier.first.clone : {})
1171
- end
1172
- end
1173
- end
1174
- end
1175
- end
1
+ require 'erb'
2
+ require 'page-object/locator_generator'
3
+
4
+ module PageObject
5
+ #
6
+ # Contains the class level methods that are inserted into your page objects
7
+ # when you include the PageObject module. These methods will generate another
8
+ # set of methods that provide access to the elements on the web pages.
9
+ #
10
+ module Accessors
11
+
12
+ #
13
+ # Set some values that can be used within the class. This is
14
+ # typically used to provide values that help build dynamic urls in
15
+ # the page_url method
16
+ #
17
+ # @param [Hash] the value to set the params
18
+ #
19
+ def params=(the_params)
20
+ @params = the_params
21
+ end
22
+
23
+ #
24
+ # Return the params that exist on this page class
25
+ #
26
+ def params
27
+ @params ||= {}
28
+ end
29
+
30
+ #
31
+ # Specify the url for the page. A call to this method will generate a
32
+ # 'goto' method to take you to the page.
33
+ #
34
+ # @param [String] the url for the page.
35
+ # @param [Symbol] a method name to call to get the url
36
+ #
37
+ def page_url(url)
38
+ define_method("goto") do
39
+ platform.navigate_to self.page_url_value
40
+ end
41
+
42
+ define_method('page_url_value') do
43
+ lookup = url.kind_of?(Symbol) ? self.send(url) : url
44
+ erb = ERB.new(%Q{#{lookup}})
45
+ merged_params = self.class.instance_variable_get("@merged_params")
46
+ params = merged_params ? merged_params : self.class.params
47
+ erb.result(binding)
48
+ end
49
+ end
50
+ alias_method :direct_url, :page_url
51
+
52
+ #
53
+ # Creates a method that waits the expected_title of a page to match the actual.
54
+ # @param [String] expected_title the literal expected title for the page
55
+ # @param [Regexp] expected_title the expected title pattern for the page
56
+ # @param [optional, Integer] timeout default value is nil - do not wait
57
+ # @return [boolean]
58
+ # @raise An exception if expected_title does not match actual title
59
+ #
60
+ # @example Specify 'Google' as the expected title of a page
61
+ # expected_title "Google"
62
+ # page.has_expected_title?
63
+ #
64
+ def wait_for_expected_title(expected_title, timeout=::PageObject.default_element_wait)
65
+ define_method("wait_for_expected_title?") do
66
+ error_message = lambda { "Expected title '#{expected_title}' instead of '#{title}'" }
67
+
68
+ has_expected_title = (expected_title === title)
69
+ wait_until(timeout, error_message.call) do
70
+ has_expected_title = (expected_title === title)
71
+ end unless has_expected_title
72
+
73
+ raise error_message.call unless has_expected_title
74
+ has_expected_title
75
+ end
76
+ end
77
+
78
+ #
79
+ # Creates a method that compares the expected_title of a page against the actual.
80
+ # @param [String] expected_title the literal expected title for the page
81
+ # @param [Regexp] expected_title the expected title pattern for the page
82
+ # @return [boolean]
83
+ # @raise An exception if expected_title does not match actual title
84
+ #
85
+ # @example Specify 'Google' as the expected title of a page
86
+ # expected_title "Google"
87
+ # page.has_expected_title?
88
+ #
89
+ def expected_title(expected_title)
90
+ define_method("has_expected_title?") do
91
+ page_title = title
92
+ has_expected_title = (expected_title === page_title)
93
+ raise "Expected title '#{expected_title}' instead of '#{page_title}'" unless has_expected_title
94
+ has_expected_title
95
+ end
96
+ end
97
+
98
+ #
99
+ # Creates a method that provides a way to initialize a page based upon an expected element.
100
+ # This is useful for pages that load dynamic content.
101
+ # @param [Symbol] the name given to the element in the declaration
102
+ # @param [optional, Integer] timeout default value is 5 seconds
103
+ # @return [boolean]
104
+ #
105
+ # @example Specify a text box named :address expected on the page within 10 seconds
106
+ # expected_element(:address, 10)
107
+ # page.has_expected_element?
108
+ #
109
+ def expected_element(element_name, timeout=::PageObject.default_element_wait)
110
+ define_method("has_expected_element?") do
111
+ self.respond_to? "#{element_name}_element" and self.send("#{element_name}_element").when_present timeout
112
+ end
113
+ end
114
+
115
+ #
116
+ # Creates a method that provides a way to initialize a page based upon an expected element to become visible.
117
+ # This is useful for pages that load dynamic content and might have hidden elements that are not shown.
118
+ # @param [Symbol] the name given to the element in the declaration
119
+ # @param [optional, Integer] timeout default value is 5 seconds
120
+ # @param [optional, boolean] also check that element to be visible if set to true
121
+ # @return [boolean]
122
+ #
123
+ # @example Specify a text box named :address expected on the page within 10 seconds
124
+ # expected_element_visible(:address, 10)
125
+ # page.has_expected_element_visible?
126
+ #
127
+ def expected_element_visible(element_name, timeout=::PageObject.default_element_wait, check_visible=false)
128
+ define_method("has_expected_element_visible?") do
129
+ self.respond_to? "#{element_name}_element" and self.send("#{element_name}_element").when_present timeout
130
+ self.respond_to? "#{element_name}_element" and self.send("#{element_name}_element").when_visible timeout
131
+ end
132
+ end
133
+
134
+ #
135
+ # Identify an element as existing within a frame . A frame parameter
136
+ # is passed to the block and must be passed to the other calls to PageObject.
137
+ # You can nest calls to in_frame by passing the frame to the next level.
138
+ #
139
+ # @example
140
+ # in_frame(:id => 'frame_id') do |frame|
141
+ # text_field(:first_name, :id => 'fname', :frame => frame)
142
+ # end
143
+ #
144
+ # @param [Hash] identifier how we find the frame. The valid keys are:
145
+ # * :id
146
+ # * :index
147
+ # * :name
148
+ # * :regexp
149
+ # @param frame passed from a previous call to in_frame. Used to nest calls
150
+ # @param block that contains the calls to elements that exist inside the frame.
151
+ #
152
+ def in_frame(identifier, frame=nil, &block)
153
+ frame = frame.nil? ? [] : frame.dup
154
+ frame << {frame: identifier}
155
+ block.call(frame)
156
+ end
157
+
158
+ #
159
+ # Identify an element as existing within an iframe. A frame parameter
160
+ # is passed to the block and must be passed to the other calls to PageObject.
161
+ # You can nest calls to in_frame by passing the frame to the next level.
162
+ #
163
+ # @example
164
+ # in_iframe(:id => 'frame_id') do |frame|
165
+ # text_field(:first_name, :id => 'fname', :frame => frame)
166
+ # end
167
+ #
168
+ # @param [Hash] identifier how we find the frame. The valid keys are:
169
+ # * :id
170
+ # * :index
171
+ # * :name
172
+ # * :regexp
173
+ # @param frame passed from a previous call to in_iframe. Used to nest calls
174
+ # @param block that contains the calls to elements that exist inside the iframe.
175
+ #
176
+ def in_iframe(identifier, frame=nil, &block)
177
+ frame = frame.nil? ? [] : frame.dup
178
+ frame << {iframe: identifier}
179
+ block.call(frame)
180
+ end
181
+
182
+ #
183
+ # adds four methods to the page object - one to set text in a text field,
184
+ # another to retrieve text from a text field, another to return the text
185
+ # field element, another to check the text field's existence.
186
+ #
187
+ # @example
188
+ # text_field(:first_name, :id => "first_name")
189
+ # # will generate 'first_name', 'first_name=', 'first_name_element',
190
+ # # 'first_name?' methods
191
+ #
192
+ # @param [String] the name used for the generated methods
193
+ # @param [Hash] identifier how we find a text field.
194
+ # @param optional block to be invoked when element method is called
195
+ #
196
+ def text_field(name, identifier={:index => 0}, &block)
197
+ standard_methods(name, identifier, 'text_field_for', &block)
198
+ define_method(name) do
199
+ return platform.text_field_value_for identifier.clone unless block_given?
200
+ self.send("#{name}_element").value
201
+ end
202
+ define_method("#{name}=") do |value|
203
+ return platform.text_field_value_set(identifier.clone, value) unless block_given?
204
+ self.send("#{name}_element").value = value
205
+ end
206
+ end
207
+
208
+ #
209
+ # adds four methods to the page object - one to set value in a date field,
210
+ # another to retrieve value from a date field, another to return the date
211
+ # field element, another to check the date field's existence.
212
+ #
213
+ # @example
214
+ # date_field(:date_of_birth, :id => "date_of_birth")
215
+ # # will generate 'date_of_birth', 'date_of_birth=', 'date_of_birth_element',
216
+ # # 'date_of_birth?' methods
217
+ #
218
+ # @param [String] the name used for the generated methods
219
+ # @param [Hash] identifier how we find a date field.
220
+ # @param optional block to be invoked when element method is called
221
+ #
222
+ def date_field(name, identifier={:index => 0}, &block)
223
+ standard_methods(name, identifier, 'date_field_for', &block)
224
+ define_method(name) do
225
+ return platform.date_field_value_for identifier.clone unless block_given?
226
+ self.send("#{name}_element").value
227
+ end
228
+ define_method("#{name}=") do |value|
229
+ return platform.date_field_value_set(identifier.clone, value) unless block_given?
230
+ self.send("#{name}_element").value = value
231
+ end
232
+ end
233
+
234
+ #
235
+ # adds three methods to the page object - one to get the text from a hidden field,
236
+ # another to retrieve the hidden field element, and another to check the hidden
237
+ # field's existence.
238
+ #
239
+ # @example
240
+ # hidden_field(:user_id, :id => "user_identity")
241
+ # # will generate 'user_id', 'user_id_element' and 'user_id?' methods
242
+ #
243
+ # @param [String] the name used for the generated methods
244
+ # @param [Hash] identifier how we find a hidden field.
245
+ # @param optional block to be invoked when element method is called
246
+ #
247
+ def hidden_field(name, identifier={:index => 0}, &block)
248
+ standard_methods(name, identifier, 'hidden_field_for', &block)
249
+ define_method(name) do
250
+ return platform.hidden_field_value_for identifier.clone unless block_given?
251
+ self.send("#{name}_element").value
252
+ end
253
+ end
254
+ alias_method :hidden, :hidden_field
255
+
256
+ #
257
+ # adds four methods to the page object - one to set text in a text area,
258
+ # another to retrieve text from a text area, another to return the text
259
+ # area element, and another to check the text area's existence.
260
+ #
261
+ # @example
262
+ # text_area(:address, :id => "address")
263
+ # # will generate 'address', 'address=', 'address_element',
264
+ # # 'address?' methods
265
+ #
266
+ # @param [String] the name used for the generated methods
267
+ # @param [Hash] identifier how we find a text area.
268
+ # @param optional block to be invoked when element method is called
269
+ #
270
+ def text_area(name, identifier={:index => 0}, &block)
271
+ standard_methods(name, identifier, 'text_area_for', &block)
272
+ define_method(name) do
273
+ return platform.text_area_value_for identifier.clone unless block_given?
274
+ self.send("#{name}_element").value
275
+ end
276
+ define_method("#{name}=") do |value|
277
+ return platform.text_area_value_set(identifier.clone, value) unless block_given?
278
+ self.send("#{name}_element").value = value
279
+ end
280
+ end
281
+ alias_method :textarea, :text_area
282
+
283
+ #
284
+ # adds five methods - one to select an item in a drop-down,
285
+ # another to fetch the currently selected item text, another
286
+ # to retrieve the select list element, another to check the
287
+ # drop down's existence and another to get all the available options
288
+ # to select from.
289
+ #
290
+ # @example
291
+ # select_list(:state, :id => "state")
292
+ # # will generate 'state', 'state=', 'state_element', 'state?', "state_options" methods
293
+ #
294
+ # @param [Symbol] the name used for the generated methods
295
+ # @param [Hash] identifier how we find a select list.
296
+ # @param optional block to be invoked when element method is called
297
+ #
298
+ def select_list(name, identifier={:index => 0}, &block)
299
+ standard_methods(name, identifier, 'select_list_for', &block)
300
+ define_method(name) do
301
+ return platform.select_list_value_for identifier.clone unless block_given?
302
+ self.send("#{name}_element").value
303
+ end
304
+ define_method("#{name}=") do |value|
305
+ return platform.select_list_value_set(identifier.clone, value) unless block_given?
306
+ self.send("#{name}_element").select(value)
307
+ end
308
+ define_method("#{name}_options") do
309
+ element = self.send("#{name}_element")
310
+ (element && element.options) ? element.options.collect(&:text) : []
311
+ end
312
+ end
313
+ alias_method :select, :select_list
314
+
315
+ #
316
+ # adds three methods - one to select a link, another
317
+ # to return a PageObject::Elements::Link object representing
318
+ # the link, and another that checks the link's existence.
319
+ #
320
+ # @example
321
+ # link(:add_to_cart, :text => "Add to Cart")
322
+ # # will generate 'add_to_cart', 'add_to_cart_element', and 'add_to_cart?' methods
323
+ #
324
+ # @param [Symbol] the name used for the generated methods
325
+ # @param [Hash] identifier how we find a link.
326
+ # @param optional block to be invoked when element method is called
327
+ #
328
+ def link(name, identifier={:index => 0}, &block)
329
+ standard_methods(name, identifier, 'link_for', &block)
330
+ define_method(name) do
331
+ return platform.click_link_for identifier.clone unless block_given?
332
+ self.send("#{name}_element").click
333
+ end
334
+ end
335
+ alias_method :a, :link
336
+
337
+ #
338
+ # adds five methods - one to check, another to uncheck, another
339
+ # to return the state of a checkbox, another to return
340
+ # a PageObject::Elements::CheckBox object representing the checkbox, and
341
+ # a final method to check the checkbox's existence.
342
+ #
343
+ # @example
344
+ # checkbox(:active, :name => "is_active")
345
+ # # will generate 'check_active', 'uncheck_active', 'active_checked?',
346
+ # # 'active_element', and 'active?' methods
347
+ #
348
+ # @param [Symbol] the name used for the generated methods
349
+ # @param [Hash] identifier how we find a checkbox.
350
+ # @param optional block to be invoked when element method is called
351
+ #
352
+ def checkbox(name, identifier={:index => 0}, &block)
353
+ standard_methods(name, identifier, 'checkbox_for', &block)
354
+ define_method("check_#{name}") do
355
+ return platform.check_checkbox(identifier.clone) unless block_given?
356
+ self.send("#{name}_element").check
357
+ end
358
+ define_method("uncheck_#{name}") do
359
+ return platform.uncheck_checkbox(identifier.clone) unless block_given?
360
+ self.send("#{name}_element").uncheck
361
+ end
362
+ define_method("#{name}_checked?") do
363
+ return platform.checkbox_checked?(identifier.clone) unless block_given?
364
+ self.send("#{name}_element").checked?
365
+ end
366
+ end
367
+
368
+ #
369
+ # adds four methods - one to select, another to return if a radio button
370
+ # is selected, another method to return a PageObject::Elements::RadioButton
371
+ # object representing the radio button element, and another to check
372
+ # the radio button's existence.
373
+ #
374
+ # @example
375
+ # radio_button(:north, :id => "north")
376
+ # # will generate 'select_north', 'north_selected?',
377
+ # # 'north_element', and 'north?' methods
378
+ #
379
+ # @param [Symbol] the name used for the generated methods
380
+ # @param [Hash] identifier how we find a radio button.
381
+ # @param optional block to be invoked when element method is called
382
+ #
383
+ def radio_button(name, identifier={:index => 0}, &block)
384
+ standard_methods(name, identifier, 'radio_button_for', &block)
385
+ define_method("select_#{name}") do
386
+ return platform.select_radio(identifier.clone) unless block_given?
387
+ self.send("#{name}_element").select
388
+ end
389
+ define_method("#{name}_selected?") do
390
+ return platform.radio_selected?(identifier.clone) unless block_given?
391
+ self.send("#{name}_element").selected?
392
+ end
393
+ end
394
+ alias_method :radio, :radio_button
395
+
396
+ #
397
+ # adds five methods to help interact with a radio button group -
398
+ # a method to select a radio button in the group by given value/text,
399
+ # a method to return the values of all radio buttons in the group, a method
400
+ # to return if a radio button in the group is selected (will return
401
+ # the text of the selected radio button, if true), a method to return
402
+ # an array of PageObject::Elements::RadioButton objects representing
403
+ # the radio button group, and finally a method to check the existence
404
+ # of the radio button group.
405
+ #
406
+ # @example
407
+ # radio_button_group(:color, :name => "preferred_color")
408
+ # will generate 'select_color', 'color_values', 'color_selected?',
409
+ # 'color_elements', and 'color?' methods
410
+ #
411
+ # @param [Symbol] the name used for the generated methods
412
+ # @param [Hash] shared identifier for the radio button group. Typically, a 'name' attribute.
413
+ # The valid keys are:
414
+ # * :name
415
+ #
416
+ def radio_button_group(name, identifier)
417
+ define_method("select_#{name}") do |value|
418
+ platform.radio_buttons_for(identifier.clone).each do |radio_elem|
419
+ if radio_elem.value == value
420
+ return radio_elem.select
421
+ end
422
+ end
423
+ end
424
+ define_method("#{name}_values") do
425
+ result = []
426
+ platform.radio_buttons_for(identifier.clone).each do |radio_elem|
427
+ result << radio_elem.value
428
+ end
429
+ return result
430
+ end
431
+ define_method("#{name}_selected?") do
432
+ platform.radio_buttons_for(identifier.clone).each do |radio_elem|
433
+ return radio_elem.value if radio_elem.selected?
434
+ end
435
+ return false
436
+ end
437
+ define_method("#{name}_elements") do
438
+ return platform.radio_buttons_for(identifier.clone)
439
+ end
440
+ define_method("#{name}?") do
441
+ return platform.radio_buttons_for(identifier.clone).any?
442
+ end
443
+ end
444
+ alias_method :radio_group, :radio_button_group
445
+
446
+ #
447
+ # adds three methods - one to click a button, another to
448
+ # return the button element, and another to check the button's existence.
449
+ #
450
+ # @example
451
+ # button(:purchase, :id => 'purchase')
452
+ # # will generate 'purchase', 'purchase_element', and 'purchase?' methods
453
+ #
454
+ # @param [Symbol] the name used for the generated methods
455
+ # @param [Hash] identifier how we find a button.
456
+ # @param optional block to be invoked when element method is called
457
+ #
458
+ def button(name, identifier={:index => 0}, &block)
459
+ standard_methods(name, identifier, 'button_for', &block)
460
+ define_method(name) do
461
+ return platform.click_button_for identifier.clone unless block_given?
462
+ self.send("#{name}_element").click
463
+ end
464
+ end
465
+
466
+ #
467
+ # adds three methods - one to retrieve the text from a div,
468
+ # another to return the div element, and another to check the div's existence.
469
+ #
470
+ # @example
471
+ # div(:message, :id => 'message')
472
+ # # will generate 'message', 'message_element', and 'message?' methods
473
+ #
474
+ # @param [Symbol] the name used for the generated methods
475
+ # @param [Hash] identifier how we find a div.
476
+ # @param optional block to be invoked when element method is called
477
+ #
478
+ def div(name, identifier={:index => 0}, &block)
479
+ standard_methods(name, identifier, 'div_for', &block)
480
+ define_method(name) do
481
+ return platform.div_text_for identifier.clone unless block_given?
482
+ self.send("#{name}_element").text
483
+ end
484
+ end
485
+
486
+ #
487
+ # adds three methods - one to retrieve the text from a span,
488
+ # another to return the span element, and another to check the span's existence.
489
+ #
490
+ # @example
491
+ # span(:alert, :id => 'alert')
492
+ # # will generate 'alert', 'alert_element', and 'alert?' methods
493
+ #
494
+ # @param [Symbol] the name used for the generated methods
495
+ # @param [Hash] identifier how we find a span.
496
+ # @param optional block to be invoked when element method is called
497
+ #
498
+ def span(name, identifier={:index => 0}, &block)
499
+ standard_methods(name, identifier, 'span_for', &block)
500
+ define_method(name) do
501
+ return platform.span_text_for identifier.clone unless block_given?
502
+ self.send("#{name}_element").text
503
+ end
504
+ end
505
+
506
+ #
507
+ # adds three methods - one to return the text for the table, one
508
+ # to retrieve the table element, and another to
509
+ # check the table's existence.
510
+ #
511
+ # @example
512
+ # table(:cart, :id => 'shopping_cart')
513
+ # # will generate a 'cart', 'cart_element' and 'cart?' method
514
+ #
515
+ # @param [Symbol] the name used for the generated methods
516
+ # @param [Hash] identifier how we find a table.
517
+ # @param optional block to be invoked when element method is called
518
+ #
519
+ def table(name, identifier={:index => 0}, &block)
520
+ standard_methods(name, identifier, 'table_for', &block)
521
+ define_method(name) do
522
+ return platform.table_text_for identifier.clone unless block_given?
523
+ self.send("#{name}_element").text
524
+ end
525
+ end
526
+
527
+ #
528
+ # adds three methods - one to retrieve the text from a table cell,
529
+ # another to return the table cell element, and another to check the cell's
530
+ # existence.
531
+ #
532
+ # @example
533
+ # cell(:total, :id => 'total_cell')
534
+ # # will generate 'total', 'total_element', and 'total?' methods
535
+ #
536
+ # @param [Symbol] the name used for the generated methods
537
+ # @param [Hash] identifier how we find a cell.
538
+ # @param optional block to be invoked when element method is called
539
+ #
540
+ def cell(name, identifier={:index => 0}, &block)
541
+ standard_methods(name, identifier, 'cell_for', &block)
542
+ define_method("#{name}") do
543
+ return platform.cell_text_for identifier.clone unless block_given?
544
+ self.send("#{name}_element").text
545
+ end
546
+ end
547
+ alias_method :td, :cell
548
+
549
+
550
+ #
551
+ # adds three methods - one to retrieve the text from a table row,
552
+ # another to return the table row element, and another to check the row's
553
+ # existence.
554
+ #
555
+ # @example
556
+ # row(:sums, :id => 'sum_row')
557
+ # # will generate 'sums', 'sums_element', and 'sums?' methods
558
+ #
559
+ # @param [Symbol] the name used for the generated methods
560
+ # @param [Hash] identifier how we find a cell.
561
+ # @param optional block to be invoked when element method is called
562
+ #
563
+ def row(name, identifier={:index => 0}, &block)
564
+ standard_methods(name, identifier, 'row_for', &block)
565
+ define_method("#{name}") do
566
+ return platform.row_text_for identifier.clone unless block_given?
567
+ self.send("#{name}_element").text
568
+ end
569
+ end
570
+
571
+ #
572
+ # adds three methods - one to retrieve the image element, another to
573
+ # check the load status of the image, and another to check the
574
+ # image's existence.
575
+ #
576
+ # @example
577
+ # image(:logo, :id => 'logo')
578
+ # # will generate 'logo_element', 'logo_loaded?', and 'logo?' methods
579
+ #
580
+ # @param [Symbol] the name used for the generated methods
581
+ # @param [Hash] identifier how we find an image.
582
+ # @param optional block to be invoked when element method is called
583
+ #
584
+ def image(name, identifier={:index => 0}, &block)
585
+ standard_methods(name, identifier, 'image_for', &block)
586
+ define_method("#{name}_loaded?") do
587
+ return platform.image_loaded_for identifier.clone unless block_given?
588
+ self.send("#{name}_element").loaded?
589
+ end
590
+ end
591
+ alias_method :img, :image
592
+
593
+ #
594
+ # adds two methods - one to retrieve the form element, and another to
595
+ # check the form's existence.
596
+ #
597
+ # @example
598
+ # form(:login, :id => 'login')
599
+ # # will generate 'login_element' and 'login?' methods
600
+ #
601
+ # @param [Symbol] the name used for the generated methods
602
+ # @param [Hash] identifier how we find a form.
603
+ # @param optional block to be invoked when element method is called
604
+ #
605
+ def form(name, identifier={:index => 0}, &block)
606
+ standard_methods(name, identifier, 'form_for', &block)
607
+ end
608
+
609
+ #
610
+ # adds three methods - one to retrieve the text from a list item,
611
+ # another to return the list item element, and another to check the list item's
612
+ # existence.
613
+ #
614
+ # @example
615
+ # list_item(:item_one, :id => 'one')
616
+ # # will generate 'item_one', 'item_one_element', and 'item_one?' methods
617
+ #
618
+ # @param [Symbol] the name used for the generated methods
619
+ # @param [Hash] identifier how we find a list item.
620
+ # @param optional block to be invoked when element method is called
621
+ #
622
+ def list_item(name, identifier={:index => 0}, &block)
623
+ standard_methods(name, identifier, 'list_item_for', &block)
624
+ define_method(name) do
625
+ return platform.list_item_text_for identifier.clone unless block_given?
626
+ self.send("#{name}_element").text
627
+ end
628
+ end
629
+ alias_method :li, :list_item
630
+
631
+ #
632
+ # adds three methods - one to return the text within the unordered
633
+ # list, one to retrieve the unordered list element, and another to
634
+ # check it's existence.
635
+ #
636
+ # @example
637
+ # unordered_list(:menu, :id => 'main_menu')
638
+ # # will generate 'menu', 'menu_element' and 'menu?' methods
639
+ #
640
+ # @param [Symbol] the name used for the generated methods
641
+ # @param [Hash] identifier how we find an unordered list.
642
+ # @param optional block to be invoked when element method is called
643
+ #
644
+ def unordered_list(name, identifier={:index => 0}, &block)
645
+ standard_methods(name, identifier, 'unordered_list_for', &block)
646
+ define_method(name) do
647
+ return platform.unordered_list_text_for identifier.clone unless block_given?
648
+ self.send("#{name}_element").text
649
+ end
650
+ end
651
+ alias_method :ul, :unordered_list
652
+
653
+ #
654
+ # adds three methods - one to return the text within the ordered
655
+ # list, one to retrieve the ordered list element, and another to
656
+ # test it's existence.
657
+ #
658
+ # @example
659
+ # ordered_list(:top_five, :id => 'top')
660
+ # # will generate 'top_five', 'top_five_element' and 'top_five?' methods
661
+ #
662
+ # @param [Symbol] the name used for the generated methods
663
+ # @param [Hash] identifier how we find an ordered list.
664
+ # @param optional block to be invoked when element method is called
665
+ #
666
+ def ordered_list(name, identifier={:index => 0}, &block)
667
+ standard_methods(name, identifier, 'ordered_list_for', &block)
668
+ define_method(name) do
669
+ return platform.ordered_list_text_for identifier.clone unless block_given?
670
+ self.send("#{name}_element").text
671
+ end
672
+ end
673
+ alias_method :ol, :ordered_list
674
+
675
+ #
676
+ # adds three methods - one to retrieve the text of a h1 element, another to
677
+ # retrieve a h1 element, and another to check for it's existence.
678
+ #
679
+ # @example
680
+ # h1(:title, :id => 'title')
681
+ # # will generate 'title', 'title_element', and 'title?' methods
682
+ #
683
+ # @param [Symbol] the name used for the generated methods
684
+ # @param [Hash] identifier how we find a H1. You can use a multiple parameters
685
+ # by combining of any of the following except xpath.
686
+ # @param optional block to be invoked when element method is called
687
+ #
688
+ def h1(name, identifier={:index => 0}, &block)
689
+ standard_methods(name, identifier,'h1_for', &block)
690
+ define_method(name) do
691
+ return platform.h1_text_for identifier.clone unless block_given?
692
+ self.send("#{name}_element").text
693
+ end
694
+ end
695
+
696
+ #
697
+ # adds three methods - one to retrieve the text of a h2 element, another
698
+ # to retrieve a h2 element, and another to check for it's existence.
699
+ #
700
+ # @example
701
+ # h2(:title, :id => 'title')
702
+ # # will generate 'title', 'title_element', and 'title?' methods
703
+ #
704
+ # @param [Symbol] the name used for the generated methods
705
+ # @param [Hash] identifier how we find a H2.
706
+ # @param optional block to be invoked when element method is called
707
+ #
708
+ def h2(name, identifier={:index => 0}, &block)
709
+ standard_methods(name, identifier, 'h2_for', &block)
710
+ define_method(name) do
711
+ return platform.h2_text_for identifier.clone unless block_given?
712
+ self.send("#{name}_element").text
713
+ end
714
+ end
715
+
716
+ #
717
+ # adds three methods - one to retrieve the text of a h3 element,
718
+ # another to return a h3 element, and another to check for it's existence.
719
+ #
720
+ # @example
721
+ # h3(:title, :id => 'title')
722
+ # # will generate 'title', 'title_element', and 'title?' methods
723
+ #
724
+ # @param [Symbol] the name used for the generated methods
725
+ # @param [Hash] identifier how we find a H3.
726
+ # @param optional block to be invoked when element method is called
727
+ #
728
+ def h3(name, identifier={:index => 0}, &block)
729
+ standard_methods(name, identifier, 'h3_for', &block)
730
+ define_method(name) do
731
+ return platform.h3_text_for identifier.clone unless block_given?
732
+ self.send("#{name}_element").text
733
+ end
734
+ end
735
+
736
+ #
737
+ # adds three methods - one to retrieve the text of a h4 element,
738
+ # another to return a h4 element, and another to check for it's existence.
739
+ #
740
+ # @example
741
+ # h4(:title, :id => 'title')
742
+ # # will generate 'title', 'title_element', and 'title?' methods
743
+ #
744
+ # @param [Symbol] the name used for the generated methods
745
+ # @param [Hash] identifier how we find a H4.
746
+ # @param optional block to be invoked when element method is called
747
+ #
748
+ def h4(name, identifier={:index => 0}, &block)
749
+ standard_methods(name, identifier, 'h4_for', &block)
750
+ define_method(name) do
751
+ return platform.h4_text_for identifier.clone unless block_given?
752
+ self.send("#{name}_element").text
753
+ end
754
+ end
755
+
756
+ #
757
+ # adds three methods - one to retrieve the text of a h5 element,
758
+ # another to return a h5 element, and another to check for it's existence.
759
+ #
760
+ # @example
761
+ # h5(:title, :id => 'title')
762
+ # # will generate 'title', 'title_element', and 'title?' methods
763
+ #
764
+ # @param [Symbol] the name used for the generated methods
765
+ # @param [Hash] identifier how we find a H5.
766
+ # @param optional block to be invoked when element method is called
767
+ #
768
+ def h5(name, identifier={:index => 0}, &block)
769
+ standard_methods(name, identifier, 'h5_for', &block)
770
+ define_method(name) do
771
+ return platform.h5_text_for identifier.clone unless block_given?
772
+ self.send("#{name}_element").text
773
+ end
774
+ end
775
+
776
+ #
777
+ # adds three methods - one to retrieve the text of a h6 element,
778
+ # another to return a h6 element, and another to check for it's existence.
779
+ #
780
+ # @example
781
+ # h6(:title, :id => 'title')
782
+ # # will generate 'title', 'title_element', and 'title?' methods
783
+ #
784
+ # @param [Symbol] the name used for the generated methods
785
+ # @param [Hash] identifier how we find a H6.
786
+ # @param optional block to be invoked when element method is called
787
+ #
788
+ def h6(name, identifier={:index => 0}, &block)
789
+ standard_methods(name, identifier, 'h6_for', &block)
790
+ define_method(name) do
791
+ return platform.h6_text_for identifier.clone unless block_given?
792
+ self.send("#{name}_element").text
793
+ end
794
+ end
795
+
796
+ #
797
+ # adds three methods - one to retrieve the text of a paragraph, another
798
+ # to retrieve a paragraph element, and another to check the paragraph's existence.
799
+ #
800
+ # @example
801
+ # paragraph(:title, :id => 'title')
802
+ # # will generate 'title', 'title_element', and 'title?' methods
803
+ #
804
+ # @param [Symbol] the name used for the generated methods
805
+ # @param [Hash] identifier how we find a paragraph.
806
+ # @param optional block to be invoked when element method is called
807
+ #
808
+ def paragraph(name, identifier={:index => 0}, &block)
809
+ standard_methods(name, identifier, 'paragraph_for', &block)
810
+ define_method(name) do
811
+ return platform.paragraph_text_for identifier.clone unless block_given?
812
+ self.send("#{name}_element").text
813
+ end
814
+ end
815
+ alias_method :p, :paragraph
816
+
817
+ #
818
+ # adds three methods - one to set the file for a file field, another to retrieve
819
+ # the file field element, and another to check it's existence.
820
+ #
821
+ # @example
822
+ # file_field(:the_file, :id => 'file_to_upload')
823
+ # # will generate 'the_file=', 'the_file_element', and 'the_file?' methods
824
+ #
825
+ # @param [Symbol] the name used for the generated methods
826
+ # @param [Hash] identifier how we find a file_field.
827
+ # @param optional block to be invoked when element method is called
828
+ #
829
+ def file_field(name, identifier={:index => 0}, &block)
830
+ standard_methods(name, identifier, 'file_field_for', &block)
831
+ define_method("#{name}=") do |value|
832
+ return platform.file_field_value_set(identifier.clone, value) unless block_given?
833
+ self.send("#{name}_element").value = value
834
+ end
835
+ end
836
+
837
+ #
838
+ # adds three methods - one to retrieve the text from a label,
839
+ # another to return the label element, and another to check the label's existence.
840
+ #
841
+ # @example
842
+ # label(:message, :id => 'message')
843
+ # # will generate 'message', 'message_element', and 'message?' methods
844
+ #
845
+ # @param [Symbol] the name used for the generated methods
846
+ # @param [Hash] identifier how we find a label.
847
+ # @param optional block to be invoked when element method is called
848
+ #
849
+ def label(name, identifier={:index => 0}, &block)
850
+ standard_methods(name, identifier, 'label_for', &block)
851
+ define_method(name) do
852
+ return platform.label_text_for identifier.clone unless block_given?
853
+ self.send("#{name}_element").text
854
+ end
855
+ end
856
+
857
+ #
858
+ # adds three methods - one to click the area,
859
+ # another to return the area element, and another to check the area's existence.
860
+ #
861
+ # @example
862
+ # area(:message, :id => 'message')
863
+ # # will generate 'message', 'message_element', and 'message?' methods
864
+ #
865
+ # @param [Symbol] the name used for the generated methods
866
+ # @param [Hash] identifier how we find an area.
867
+ # @param optional block to be invoked when element method is called
868
+ #
869
+ def area(name, identifier={:index => 0}, &block)
870
+ standard_methods(name, identifier, 'area_for', &block)
871
+ define_method(name) do
872
+ return platform.click_area_for identifier.clone unless block_given?
873
+ self.send("#{name}_element").click
874
+ end
875
+ end
876
+
877
+ #
878
+ # adds two methods - one to return the canvas element and another to check
879
+ # the canvas's existence.
880
+ #
881
+ # @example
882
+ # canvas(:my_canvas, :id => 'canvas_id')
883
+ # # will generate 'my_canvas_element' and 'my_canvas?' methods
884
+ #
885
+ # @param [Symbol] the name used for the generated methods
886
+ # @param [Hash] identifier how we find a canvas.
887
+ # @param optional block to be invoked when element method is called
888
+ #
889
+ def canvas(name, identifier={:index => 0}, &block)
890
+ standard_methods(name, identifier, 'canvas_for', &block)
891
+ end
892
+
893
+ #
894
+ # adds two methods - one to return the audio element and another to check
895
+ # the audio's existence.
896
+ #
897
+ # @example
898
+ # audio(:acdc, :id => 'audio_id')
899
+ # # will generate 'acdc_element' and 'acdc?' methods
900
+ #
901
+ # @param [Symbol] the name used for the generated methods
902
+ # @param [Hash] identifier how we find an audio element.
903
+ # @param optional block to be invoked when element method is called
904
+ #
905
+ def audio(name, identifier={:index => 0}, &block)
906
+ standard_methods(name, identifier, 'audio_for', &block)
907
+ end
908
+
909
+ #
910
+ # adds two methods - one to return the video element and another to check
911
+ # the video's existence.
912
+ #
913
+ # @example
914
+ # video(:movie, :id => 'video_id')
915
+ # # will generate 'movie_element' and 'movie?' methods
916
+ #
917
+ # @param [Symbol] the name used for the generated methods
918
+ # @param [Hash] identifier how we find a video element.
919
+ # @param optional block to be invoked when element method is called
920
+ #
921
+ def video(name, identifier={:index => 0}, &block)
922
+ standard_methods(name, identifier, 'video_for', &block)
923
+ end
924
+
925
+ #
926
+ # adds three methods - one to retrieve the text of a b element, another to
927
+ # retrieve a b element, and another to check for it's existence.
928
+ #
929
+ # @example
930
+ # b(:bold, :id => 'title')
931
+ # # will generate 'bold', 'bold_element', and 'bold?' methods
932
+ #
933
+ # @param [Symbol] the name used for the generated methods
934
+ # @param [Hash] identifier how we find a b.
935
+ # @param optional block to be invoked when element method is called
936
+ #
937
+ def b(name, identifier={:index => 0}, &block)
938
+ standard_methods(name, identifier,'b_for', &block)
939
+ define_method(name) do
940
+ return platform.b_text_for identifier.clone unless block_given?
941
+ self.send("#{name}_element").text
942
+ end
943
+ end
944
+
945
+ #
946
+ # adds three methods - one to retrieve the text of a i element, another to
947
+ # retrieve a i element, and another to check for it's existence.
948
+ #
949
+ # @example
950
+ # i(:italic, :id => 'title')
951
+ # # will generate 'italic', 'italic_element', and 'italic?' methods
952
+ #
953
+ # @param [Symbol] the name used for the generated methods
954
+ # @param [Hash] identifier how we find a i.
955
+ # @param optional block to be invoked when element method is called
956
+ #
957
+ def i(name, identifier={:index => 0}, &block)
958
+ standard_methods(name, identifier,'i_for', &block)
959
+ define_method(name) do
960
+ return platform.i_text_for identifier.clone unless block_given?
961
+ self.send("#{name}_element").text
962
+ end
963
+ end
964
+ alias_method :icon, :i
965
+
966
+ #
967
+ # adds two methods - one to retrieve a svg, and another to check
968
+ # the svg's existence.
969
+ #
970
+ # @example
971
+ # svg(:circle, :id => 'circle')
972
+ # # will generate 'circle_element', and 'circle?' methods
973
+ #
974
+ # @param [Symbol] the name used for the generated methods
975
+ # @param [Hash] identifier how we find a svg.
976
+ # @param optional block to be invoked when element method is called
977
+ #
978
+ def svg(name, identifier={:index => 0}, &block)
979
+ standard_methods(name, identifier, 'svg_for', &block)
980
+ end
981
+
982
+
983
+ #
984
+ # adds three methods - one to retrieve the text of an element, another
985
+ # to retrieve an element, and another to check the element's existence.
986
+ #
987
+ # @example
988
+ # element(:title, :header, :id => 'title')
989
+ # # will generate 'title', 'title_element', and 'title?' methods
990
+ #
991
+ # @param [Symbol] the name used for the generated methods
992
+ # @param [Symbol] the name of the tag for the element
993
+ # @param [Hash] identifier how we find an element.
994
+ # @param optional block to be invoked when element method is called
995
+ #
996
+ def element(name, tag=:element, identifier={ :index => 0 }, &block)
997
+ #
998
+ # sets tag as element if not defined
999
+ #
1000
+ if tag.is_a?(Hash)
1001
+ identifier = tag
1002
+ tag = :element
1003
+ end
1004
+
1005
+ standard_methods(name, identifier, 'element_for', &block)
1006
+
1007
+ define_method("#{name}") do
1008
+ element = self.send("#{name}_element")
1009
+
1010
+ %w(Button TextField Radio Hidden CheckBox FileField).each do |klass|
1011
+ next unless element.element.class.to_s == "Watir::#{klass}"
1012
+ self.class.send(klass.gsub(/(.)([A-Z])/,'\1_\2').downcase, name, identifier, &block)
1013
+ return self.send name
1014
+ end
1015
+ element.text
1016
+ end
1017
+ define_method("#{name}_element") do
1018
+ return call_block(&block) if block_given?
1019
+ platform.element_for(tag, identifier.clone)
1020
+ end
1021
+ define_method("#{name}?") do
1022
+ self.send("#{name}_element").exists?
1023
+ end
1024
+ define_method("#{name}=") do |value|
1025
+ element = self.send("#{name}_element")
1026
+
1027
+ klass = case element.element
1028
+ when Watir::TextField
1029
+ 'text_field'
1030
+ when Watir::TextArea
1031
+ 'text_area'
1032
+ when Watir::Select
1033
+ 'select_list'
1034
+ when Watir::FileField
1035
+ 'file_field'
1036
+ else
1037
+ raise "Can not set a #{element.element} element with #="
1038
+ end
1039
+ self.class.send(klass, name, identifier, &block)
1040
+ self.send("#{name}=", value)
1041
+ end
1042
+ end
1043
+
1044
+ #
1045
+ # adds a method to return a collection of generic Element objects
1046
+ # for a specific tag.
1047
+ #
1048
+ # @example
1049
+ # elements(:title, :header, :id => 'title')
1050
+ # # will generate ''title_elements'
1051
+ #
1052
+ # @param [Symbol] the name used for the generated methods
1053
+ # @param [Symbol] the name of the tag for the element
1054
+ # @param [Hash] identifier how we find an element.
1055
+ # @param optional block to be invoked when element method is called
1056
+ #
1057
+ def elements(name, tag=:element, identifier={:index => 0}, &block)
1058
+ #
1059
+ # sets tag as element if not defined
1060
+ #
1061
+ if tag.is_a?(Hash)
1062
+ identifier = tag
1063
+ tag = :element
1064
+ end
1065
+
1066
+ define_method("#{name}_elements") do
1067
+ return call_block(&block) if block_given?
1068
+ platform.elements_for(tag, identifier.clone)
1069
+ end
1070
+ end
1071
+
1072
+ #
1073
+ # adds a method to return a page object rooted at an element
1074
+ #
1075
+ # @example
1076
+ # page_section(:navigation_bar, NavigationBar, :id => 'nav-bar')
1077
+ # # will generate 'navigation_bar'
1078
+ #
1079
+ # @param [Symbol] the name used for the generated methods
1080
+ # @param [Class] the class to instantiate for the element
1081
+ # @param [Hash] identifier how we find an element.
1082
+ #
1083
+ def page_section(name, section_class, identifier)
1084
+ define_method(name) do
1085
+ platform.page_for(identifier, section_class)
1086
+ end
1087
+ end
1088
+
1089
+ #
1090
+ # adds a method to return a collection of page objects rooted at elements
1091
+ #
1092
+ # @example
1093
+ # page_sections(:articles, Article, :class => 'article')
1094
+ # # will generate 'articles'
1095
+ #
1096
+ # @param [Symbol] the name used for the generated method
1097
+ # @param [Class] the class to instantiate for each element
1098
+ # @param [Hash] identifier how we find an element.
1099
+ #
1100
+ def page_sections(name, section_class, identifier)
1101
+ define_method(name) do
1102
+ platform.pages_for(identifier, section_class)
1103
+ end
1104
+ end
1105
+
1106
+ #
1107
+ # methods to generate accessors for types that follow the same
1108
+ # pattern as element
1109
+ #
1110
+ # @example
1111
+ # article(:my_article, :id => "article_id")
1112
+ # will generate 'my_article', 'my_article_element' and 'my_article?'
1113
+ # articles(:my_article, :id => 'article_id')
1114
+ # will generate 'my_article_elements'
1115
+ #
1116
+ # @param [Symbol] the name used for the generated methods
1117
+ # @param [Symbol] the name of the tag for the element
1118
+ # @param [Hash] identifier how we find an element.
1119
+ # @param optional block to be invoked when element method is called
1120
+ #
1121
+ LocatorGenerator::BASIC_ELEMENTS.each do |tag|
1122
+ define_method(tag) do |name, *identifier, &block|
1123
+ identifier = identifier[0] ? identifier[0] : {:index => 0}
1124
+ element(name, tag, identifier, &block)
1125
+ end
1126
+ define_method("#{tag}s") do |name, *identifier, &block|
1127
+ identifier = identifier[0] ? identifier[0] : {:index => 0}
1128
+ elements(name, tag, identifier, &block)
1129
+ end unless tag == :param
1130
+ end
1131
+
1132
+ def standard_methods(name, identifier, method, &block)
1133
+ define_method("#{name}_element") do
1134
+ return call_block(&block) if block_given?
1135
+ platform.send(method, identifier.clone)
1136
+ end
1137
+ define_method("#{name}?") do
1138
+ return call_block(&block).exists? if block_given?
1139
+ platform.send(method, identifier.clone).exists?
1140
+ end
1141
+ end
1142
+
1143
+ #
1144
+ # adds a method that will return an indexed property. The property will respond to
1145
+ # the [] method with an object that has a set of normal page_object properties that
1146
+ # correspond to the definitions included in the identifier_list parameter, with the
1147
+ # "what" of the "how and what" substituted based on the index provided to the []
1148
+ # method.
1149
+ #
1150
+ # @example
1151
+ # indexed_property(:title, [
1152
+ # [:text_field, :field_1, :id => 'table[%s].field_1'],
1153
+ # [:button, :button_1, :id => 'table[%s].button_1'],
1154
+ # [:text_field, :field_2, :name => 'table[%s].field_2']
1155
+ # ])
1156
+ # # will generate a title method that responds to []. title['foo'] will return an object
1157
+ # # that responds to the normal methods expected for two text_fields and a button with the
1158
+ # # given names, using the given how and what with 'foo' substituted for the %s. title[123]
1159
+ # # will do the same, using the integer 123 instead.
1160
+ #
1161
+ # @param [Symbol] the name used for the generated method
1162
+ # @param [Array] definitions an array of definitions to define on the indexed property. Each
1163
+ # entry in the array should contain two symbols and a hash, corresponding to one of the standard
1164
+ # page_object properties with a single substitution marker in each value in the hash,
1165
+ # e.g. [:text_field, :field_1, :id => 'table[%s].field_1']
1166
+ #
1167
+ def indexed_property (name, identifier_list)
1168
+ define_method("#{name}") do
1169
+ IndexedProperties::TableOfElements.new(@browser, identifier_list)
1170
+ end
1171
+ end
1172
+
1173
+ #
1174
+ # methods to fetch multiple elements of the same type
1175
+ #
1176
+ # adds a method to the page object to return all of the matching elements
1177
+ #
1178
+ # @example
1179
+ # text_fields(:first_name, :id => "first_name")
1180
+ # # will generate 'first_name_elements'
1181
+ #
1182
+ # @param [String] the name used for the generated methods
1183
+ # @param [Hash] identifier how we find a text field. You can use a multiple parameters
1184
+ # by combining of any of the following except xpath. The valid
1185
+ # keys are the same ones supported by the standard methods.
1186
+ # @param optional block to be invoked when element method is called
1187
+ #
1188
+ idx = LocatorGenerator::ADVANCED_ELEMENTS.find_index { |type| type == :checkbox }
1189
+ elements = LocatorGenerator::ADVANCED_ELEMENTS.clone
1190
+ elements[idx] = :checkboxe
1191
+ elements.each do |method_name|
1192
+ define_method("#{method_name}s") do |name, *identifier, &block|
1193
+ define_method("#{name}_elements") do
1194
+ return call_block(&block) unless block.nil?
1195
+ platform_method = (method_name == :checkboxe) ? 'checkboxs_for' : "#{method_name.to_s}s_for"
1196
+ platform.send platform_method, (identifier.first ? identifier.first.clone : {})
1197
+ end
1198
+ end
1199
+ end
1200
+ end
1201
+ end