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.
- data/History.txt +19 -0
- data/Manifest.txt +36 -0
- data/README.rdoc +104 -0
- data/Rakefile.rb +59 -0
- data/lib/watirloo.rb +113 -0
- data/lib/watirloo/firewatir_ducktape.rb +194 -0
- data/lib/watirloo/reflector.rb +137 -0
- data/lib/watirloo/watir_ducktape.rb +376 -0
- data/script/console +10 -0
- data/script/console.cmd +1 -0
- data/script/destroy +14 -0
- data/script/destroy.cmd +1 -0
- data/script/generate +14 -0
- data/script/generate.cmd +1 -0
- data/script/reflect.rb +8 -0
- data/test/checkbox_group_test.rb +68 -0
- data/test/checkboxes_value_test.rb +46 -0
- data/test/firewatir/attach_instance_test.rb +38 -0
- data/test/html/census.html +332 -0
- data/test/html/checkbox_group1.html +20 -0
- data/test/html/labels.html +32 -0
- data/test/html/person.html +37 -0
- data/test/html/radio_group.html +41 -0
- data/test/html/select_lists.html +82 -0
- data/test/label_test.rb +64 -0
- data/test/person_def_wrappers_test.rb +56 -0
- data/test/radio_group_test.rb +82 -0
- data/test/select_list_as_face_test.rb +46 -0
- data/test/select_list_options_test.rb +44 -0
- data/test/select_lists_test.rb +145 -0
- data/test/test_helper.rb +9 -0
- data/test/text_fields_test.rb +74 -0
- data/watirloo.gemspec +44 -0
- metadata +135 -0
@@ -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
|