marekj-watirloo 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,137 @@
1
+ =begin rdoc
2
+ Look Ma!, I can Has Reflect The Browser
3
+
4
+ Watir::Reflector module added to watir.
5
+ Run the script to reflect watir elements. reflections create wrapper methods
6
+ with suggested semantic naming based on id, name, value or combination.
7
+ the intention is to create a scaffolding for Watirloo::Page elements.
8
+ author: marekj
9
+ works with IE class and DOM elements.
10
+ =end
11
+ module Watir
12
+
13
+ # Watirloo::Page objects scaffold creation. Talks to the current page and reflects
14
+ # the watir elements to be used for semantic test objects tests.
15
+ module Reflector
16
+
17
+ @@reflectable_list = [
18
+ :text_fields,
19
+ :radios,
20
+ :checkboxes,
21
+ :select_lists
22
+ ]
23
+
24
+ #cleanup the def name for some kind of semantic name
25
+ def suggest_def_name(how)
26
+ how.gsub!(/_+/,'_') # double underscores to one
27
+ how.gsub!(/^_/, '') # if it begins with undrscore kill it.
28
+ how.gsub!(/\s+/, '_') # kill spaces if for some strange reason exist
29
+ how = how[0,1].downcase << how[1,how.size] #downcase firs char
30
+ end
31
+
32
+ # glean(:text_fields, [:id, :name, :value]
33
+ # glean(:radios, [:id, :name, :value])
34
+ # glean and make a map of types and attributes needed for reflection
35
+ # this should be private I think
36
+ def glean(types, attribs)
37
+ result = []
38
+ send(types).each do |el|
39
+ subresult = {}
40
+ attribs.each do |key|
41
+ v = el.attribute_value key.to_s
42
+ subresult.update key => v
43
+ end
44
+ result << subresult
45
+ end
46
+ return result
47
+ end
48
+
49
+ # example make_reflection(:checkboxes) # => [ defs, setters, faces]
50
+ # returns array of def wrappers, setters for elements and face definitions configs.
51
+ def make_reflection(types)
52
+ attribs = [:id, :name, :value]
53
+ faces = glean(types, attribs)
54
+ watir_method = types.id2name.chop
55
+ if watir_method == 'checkboxe'
56
+ watir_method = 'checkbox' #ooopps ... irregular plural
57
+ end
58
+ def_results = "# #{types.id2name.upcase}: def wrappers with suggested semantic names for elements\n" #holds definition wrappers
59
+ set_results = "# #{types.id2name.upcase}: setters calling def wrappers with captured values\n" #holds setters with gleaned values
60
+ face_results = "# #{types.id2name.upcase}: face definitions\n" #holds faces
61
+
62
+ faces.each do |face|
63
+ id, name, value = face[:id], face[:name], face[:value]
64
+
65
+ if id != ''
66
+ how, how_s = id, :id
67
+ elsif name != ''
68
+ how, how_s = name, :name
69
+ elsif value != ''
70
+ how, how_s = value, :value
71
+ end
72
+
73
+ def_name = suggest_def_name(how)
74
+
75
+ case types
76
+ when :checkboxes, :radios
77
+ extra_value = ", '#{value}'" #for checkboxes and radios
78
+ def_value = "_#{value}" #for checkboxes and radios
79
+ def_results << "\ndef #{def_name}#{def_value}\n\s\s@b.#{watir_method}(:#{how_s}, '#{how}'#{extra_value})\nend\n"
80
+ set_results << "#{def_name}#{def_value}.set\n"
81
+ face_results << ":#{def_name}#{def_value} => [:#{watir_method}, :#{how_s}, '#{how}'#{extra_value}]\n"
82
+
83
+ when :select_lists
84
+ # round trip back to browser for items and contents
85
+ value = eval("select_list(:#{how_s}, '#{how}').getSelectedItems")
86
+ items = eval("select_list(:#{how_s}, '#{how}').getAllContents")
87
+
88
+ def_results << "def #{def_name}\n\s\s@b.select_list(:#{how_s}, '#{how}')\nend\n"
89
+ set_results << "@@#{def_name}_items=#{items.inspect}\n" #class vars for values collections
90
+ set_results << "#{def_name}.set #{value.inspect}\n"
91
+ face_results << "#:#{def_name} => [:select_list, :#{how_s}, '#{name}}']\n"
92
+
93
+ else
94
+ def_results << "\ndef #{def_name}#{def_value}\n\s\s@b.#{watir_method}(:#{how_s}, '#{how}'#{extra_value})\nend\n"
95
+ set_results << "#{def_name}#{def_value}.set\n"
96
+ face_results << "\n#:#{def_name}#{def_value} = [:#{watir_method}, :#{how_s}, '#{how}#{extra_value}']\n"
97
+
98
+ end
99
+
100
+ end
101
+
102
+ return [def_results, set_results, face_results]
103
+ end
104
+ private :suggest_def_name, :glean, :make_reflection
105
+
106
+ # public interface for Reflector.
107
+ # ie.reflect(:all) # => returns object definitions for entire dom using ie as container
108
+ # ie.frame('main').reflect(:select_lists) # => returns definitions for select_lists only contained by the frame
109
+ # ie.div(:id, 'main').div(:id, 'content').reflect(:all) # => definitions for all supported elements contained by a div 'content' inside div 'main'
110
+ # you can be as granular as needed
111
+ def reflect(watir_types=:all)
112
+ results = []
113
+ case watir_types
114
+ when :all
115
+ @@reflectable_list.each do |types|
116
+ results << make_reflection(types)
117
+ end
118
+ else
119
+ unless @@reflectable_list.include?(watir_types)
120
+ raise ArgumentError, "reflect method does not respond to this argument: #{watir_method}"
121
+ end
122
+ results << make_reflection(watir_types)
123
+ end
124
+ return results
125
+ end
126
+ end
127
+
128
+ # ducktape IE container and include the Reflector.
129
+ class IE
130
+ include Reflector
131
+ end
132
+
133
+ module Container
134
+ include Reflector
135
+ end
136
+
137
+ end
@@ -0,0 +1,376 @@
1
+ gem 'watir', '>=1.6.2'
2
+ require 'watir'
3
+ require 'watir/ie'
4
+
5
+ module Watir
6
+
7
+ # for firefox and ie
8
+ module RadioCheckGroupCommonWatir
9
+
10
+ # size or count of controls in a group
11
+ def size
12
+ @o.size
13
+ end
14
+ alias count size
15
+
16
+ # sets control in a group by either position in a group
17
+ # or by hidden value attribute
18
+ def set(what)
19
+ if what.kind_of?(Array)
20
+ what.each {|thing| set thing } #calls itself with Fixnum or String
21
+ else
22
+ if what.kind_of?(Fixnum)
23
+ get_by_position(what).set
24
+ elsif what.kind_of?(String)
25
+ get_by_value(what).set
26
+ else
27
+ raise ::Watir::Exception::WatirException, "argument error #{what} not allowed"
28
+ end
29
+ end
30
+ end
31
+
32
+ # returns array of value attributes
33
+ def values
34
+ raise ::Watir::Exception::WatirException, "method should be implemented"
35
+ end
36
+
37
+ # returns Radio||Checkbox from a group that
38
+ # has specific value attribute
39
+ def get_by_value value
40
+ raise ::Watir::Exception::WatirException, "method should be implemented"
41
+ end
42
+
43
+ # returns Radio||Checkbox from a group that
44
+ # occupies specifi position in a group
45
+ # WARNING: it is 1-based NOT 0-based
46
+ # the intention is to enumerate position staring with 1, the way
47
+ # customer would enumerate items
48
+ def get_by_position position
49
+ if (1..self.size).member? position
50
+ @o[position-1]
51
+ else
52
+ raise ::Watir::Exception::WatirException, "positon #{position} is out of range of size"
53
+ end
54
+ end
55
+ end
56
+
57
+ # for IE only
58
+ module RadioCheckGroup
59
+
60
+ def values
61
+ opts = []
62
+ @o.each {|r| opts << r.ole_object.invoke('value')}
63
+ return opts
64
+ end
65
+
66
+ def get_by_value value
67
+ if values.member? value
68
+ @o.find {|r| r.ole_object.invoke('value') == value}
69
+ else
70
+ raise ::Watir::Exception::WatirException, "value #{value} not found in hidden_values"
71
+ end
72
+ end
73
+ end
74
+
75
+ #for firefox and ie
76
+ module RadioGroupCommonWatir
77
+
78
+ # Only one radio in RadioGroup can be selected just like
79
+ # only one option in single select list box can be selected.
80
+ # this method is a bit gratuitious because it will always return array
81
+ # with one item but it's here to keep the plural for compatibility with
82
+ # CheckboxGroup or SelectList. if at some point your page object gets changed from RadioGroup
83
+ # to SelectList your tests will not have to change
84
+ def selected_values
85
+ selected_value.to_a
86
+ end
87
+
88
+
89
+ # returns radio that is selected.
90
+ # there can only be one radio selected.
91
+ # in the event that none is selected it returns nil
92
+ # see selected_value commentary
93
+ def selected_radio
94
+ @o.find {|r| r.isSet?}
95
+ end
96
+ end
97
+
98
+ # radios that share the same :name attribute form a RadioGroup.
99
+ # RadioGroup semantically behaves like single select list box
100
+ # usage: this class is accessed by Watir::Container#radio_group
101
+ # RadioGroup semantically behaves like single select list box.
102
+ #
103
+ # per HTML401: -
104
+ # "If no radio button in a set sharing the same control name
105
+ # is initially 'on', user agent behavior for choosing which
106
+ # control is initially 'on' is undefined
107
+ #
108
+ # The idea of having all radios off makes no sense but in the wild you can see lots of examples.
109
+ # it would be better to just have a single select list box with no items selected instead of radios.
110
+ # The point of having radios is that at least one radio is 'ON' providing a default value for the group
111
+ #
112
+ # @browser = Watir::IE.attach :url, //
113
+ # @browser.radio_group('food') # => RadioGroup with :name, 'food'
114
+ #
115
+ class RadioGroup
116
+
117
+ include RadioCheckGroupCommonWatir
118
+ include RadioCheckGroup
119
+ include RadioGroupCommonWatir
120
+
121
+ def initialize(container, name)
122
+ @container = container
123
+ @name = name
124
+ @o = @container.radios.find_all {|r| r.name == @name}
125
+ end
126
+
127
+ # which value is selected?. returns value text as string
128
+ # So per HTML401 spec I am not sure if we should ever have empyt array returned here
129
+ # if you do get empty array then I would speak with developers to fix this and explicity
130
+ # provide checked for one radio on page load.
131
+ def selected_value
132
+ selected_radio.ole_object.invoke('value')
133
+ end
134
+
135
+ # in the absence of visible text like in select list we treat value
136
+ # as a selected text invisible to the user
137
+ alias selected selected_value
138
+
139
+ end
140
+
141
+
142
+ module CheckboxGroupCommonWatir
143
+
144
+ # returns selected checkboxex as array
145
+ # [] = nothing selected
146
+ # [checkbox, checkbox] = checkboxes that are selected.
147
+ def selected_checkboxes
148
+ @o.select {|cb| cb.isSet?}
149
+ end
150
+
151
+ # convinience method as a filter for select_values
152
+ # returns:
153
+ # nil => when no checkbox is set
154
+ # 'value' => if one checkbox is set
155
+ # or bypass filter and return selected_values array
156
+ def selected_value
157
+ arr = selected_values
158
+ case arr.size
159
+ when 0 then nil
160
+ when 1 then arr[0]
161
+ else arr
162
+ end
163
+ end
164
+
165
+ # in case of checkbox there are no visible text items.
166
+ # We rely on value attributes that must be present
167
+ # to differentiate the checkbox in a group
168
+ # compare to SelectList where selected returns selected_item
169
+ alias selected selected_value
170
+
171
+ end
172
+
173
+ # Checkbox group semantically behaves like multi select list box.
174
+ # each checkbox is a menu item groupped by the common attribute :name
175
+ # each checkbox can be off initially (a bit different semantics than RadioGroup)
176
+ class CheckboxGroup
177
+
178
+ include RadioCheckGroupCommonWatir
179
+ include RadioCheckGroup
180
+ include CheckboxGroupCommonWatir
181
+
182
+ def initialize(container, name)
183
+ @container = container
184
+ @name = name
185
+ @o = @container.checkboxes.find_all {|cb| cb.name == @name}
186
+ end
187
+
188
+ # returns array of value attributes. Each Checkbox in a group
189
+ # has a value which is invisible to the user
190
+ def selected_values
191
+ values = []
192
+ selected_checkboxes.each do |cb|
193
+ values << cb.ole_object.invoke('value')
194
+ end
195
+ return values
196
+ end
197
+ end
198
+
199
+
200
+ module Container
201
+ def radio_group(name)
202
+ RadioGroup.new(self, name)
203
+ end
204
+
205
+ def checkbox_group(name)
206
+ CheckboxGroup.new(self, name)
207
+ end
208
+ end
209
+
210
+ class RadioCheckCommon
211
+ alias set? isSet?
212
+ end
213
+
214
+ # these methods work for IE and for Firefox
215
+ module SelectListCommonWatir
216
+
217
+ # selected_items examples
218
+ # [] => nothing selected
219
+ # ['item'] => if one selected
220
+ # ['item1', 'item2', 'item3'] => several items selected
221
+ def selected_items
222
+ getSelectedItems
223
+ end
224
+
225
+ # selected_item is a convenience filter for selected_items
226
+ # returns
227
+ # nil if no options selected
228
+ # 'text' string if one option selected.
229
+ # or selected_items if more than one option selected
230
+ def selected_item
231
+ arr = selected_items # limit to one mehtod call
232
+ case arr.size
233
+ when 0 then nil
234
+ when 1 then arr[0]
235
+ else arr
236
+ end
237
+ end
238
+
239
+
240
+ # for selecte lists by default we return the text of an option
241
+ # compare to selected in RadioGroup or Checkbox group which return the
242
+ # value attributes since there is no visible text for the user
243
+ alias selected selected_item
244
+
245
+ # set :value or :text
246
+ def _set(how, what)
247
+ if what.kind_of? Array
248
+ what.each { |item| _set(how,item)} # call self with individual item
249
+ else
250
+ if what.kind_of? Fixnum # if by position then translate to set by text
251
+ if (0..items.size).member? what
252
+ _set :text, items[what-1]
253
+ else
254
+ raise ::Watir::Exception::WatirException, "number #{item} is out of range of item count"
255
+ end
256
+ else
257
+ select_item_in_select_list(how, what) #finally as :value or :text
258
+ end
259
+ end
260
+
261
+ end
262
+ private :_set
263
+
264
+
265
+
266
+
267
+ # similar to selected_items but returns array of option value attributes
268
+ def selected_values
269
+ assert_exists
270
+ arr = []
271
+ @o.each do |thisItem|
272
+ if thisItem.selected
273
+ arr << thisItem.value
274
+ end
275
+ end
276
+ return arr
277
+ end
278
+
279
+ # convinience method as a filter for select_values
280
+ # returns:
281
+ # nil for nothing selected.
282
+ # single value if only once selected or just
283
+ # or returns selected_values
284
+ def selected_value
285
+ arr = selected_values
286
+ case arr.size
287
+ when 0 then nil
288
+ when 1 then arr[0]
289
+ else arr
290
+ end
291
+ end
292
+
293
+
294
+ end
295
+
296
+ # SelectList acts like RadioGroup or CheckboxGroup
297
+ # They all have options to select
298
+ # There are two kinds of SelectLists. SingleSelect and MultiSelect
299
+ # SelectList presents user with visible items to select from.
300
+ # Each Item has a visible :text and invisible :value attributes
301
+ # (sometimes :value attributes are missing)
302
+ #
303
+ # In Watirloo
304
+ # The invisible :value attributes of options we call :values
305
+ # The visible :text of options we call :items
306
+ # The selected items as visible text we call :selected
307
+ # The selected items as values we call :selected_values
308
+ #
309
+ # example of single select list
310
+ #
311
+ # <select name="controlname">
312
+ # <option value="opt0"></option>
313
+ # <option value="opt1">item1</option>
314
+ # <option value="opt2" selected>item2</option>
315
+ # </select>
316
+ #
317
+ # items => ['', 'item1', 'item2']
318
+ # values => ['opt0','opt1', 'opt2']
319
+ # selected => ['item2']
320
+ # selected_values => ['opt2']
321
+ #
322
+ # example of multi select list
323
+ #
324
+ # <select name="controlname" multiple size=2>
325
+ # <option value="o1">item1
326
+ # <option value="o2" selected>item2
327
+ # <option value="o3" selected>item3
328
+ # </select>
329
+ #
330
+ # items => ['item1', 'item2', 'item3']
331
+ # values => ['o1','o2','o3']
332
+ # selected => ['item2', 'item3']
333
+ # selected_values => ['o2', 'o3']
334
+ #
335
+ class SelectList
336
+
337
+ include SelectListCommonWatir
338
+
339
+
340
+ # accepts one text item or array of text items. if array then sets one after another.
341
+ # For single select lists the last item in array wins
342
+ #
343
+ # examples
344
+ # select_list.set 'bla' # => single option text
345
+ # select_list.set ['bla','foo','gugu'] # => set 3 options by text. If
346
+ # this is a single select list box it will set each value in turn
347
+ # select_list set 1 # => set the first option in a list
348
+ # select_list.set [1,3,5] => set the first, third and fith options
349
+ def set(item)
350
+ _set(:text, item)
351
+ end
352
+
353
+ # set item by the option value attribute. if array then set one after anohter.
354
+ # see examples in set method
355
+ def set_value(value)
356
+ _set(:value, value)
357
+ end
358
+
359
+ # returns array of value attributes
360
+ # each option usually has a value attribute
361
+ # which is hidden to the person viewing the page
362
+ def values
363
+ a = []
364
+ attribute_value('options').each do |item|
365
+ a << item.value
366
+ end
367
+ return a
368
+ end
369
+
370
+ alias clear clearSelection
371
+
372
+ # alias, items or contents return the same visible text items
373
+ alias items getAllContents
374
+
375
+ end
376
+ end