page-object 2.2.2 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  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 -888
  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 -159
  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 -45
  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 -68
  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 -44
  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 -95
  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 +12 -12
@@ -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