marekj-watirloo 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +12 -0
- data/Manifest.txt +59 -34
- data/README.rdoc +63 -80
- data/Rakefile.rb +32 -39
- data/config/locker.yml +0 -0
- data/lib/watirloo.rb +9 -162
- data/lib/watirloo/browsers.rb +73 -0
- data/lib/watirloo/desktop.rb +44 -0
- data/lib/watirloo/{firewatir_ducktape.rb → extension/firewatir_ducktape.rb} +0 -0
- data/lib/watirloo/extension/object.rb +26 -0
- data/lib/watirloo/{watir_ducktape.rb → extension/watir_ducktape.rb} +181 -16
- data/lib/watirloo/extension/watir_reflector.rb +83 -0
- data/lib/watirloo/locker.rb +84 -0
- data/lib/watirloo/page.rb +105 -0
- data/spec/browser_spec.rb +38 -0
- data/spec/browser_threads_spec.rb +45 -0
- data/spec/checkbox_group_spec.rb +136 -0
- data/spec/checkbox_groups_spec.rb +55 -0
- data/spec/checkboxes_value_spec.rb +35 -0
- data/spec/desktop_spec.rb +54 -0
- data/spec/extra/browser_events_spec.rb +76 -0
- data/spec/extra/page_objects_metrics.rb +139 -0
- data/spec/face_mixing_spec.rb +55 -0
- data/{test → spec}/firewatir/attach_instance_test.rb +0 -0
- data/spec/firewatir/spec_results.html +263 -0
- data/spec/firewatir/spec_results.txt +23 -0
- data/spec/firewatir/spec_results_failed.txt +3 -0
- data/{test → spec}/html/census.html +0 -0
- data/spec/html/checkbox_group1.html +33 -0
- data/spec/html/labels.html +53 -0
- data/spec/html/no_title.html +13 -0
- data/{test → spec}/html/person.html +0 -0
- data/spec/html/radio_group.html +35 -0
- data/{test → spec}/html/select_lists.html +0 -0
- data/spec/input_element_spec.rb +51 -0
- data/spec/label_spec.rb +65 -0
- data/spec/locker_spec.rb +49 -0
- data/spec/page_spec.rb +53 -0
- data/spec/person_def_wrappers_spec.rb +40 -0
- data/spec/radio_group_spec.rb +95 -0
- data/spec/radio_groups_spec.rb +55 -0
- data/spec/reflector_spec.rb +82 -0
- data/spec/select_list_options_spec.rb +40 -0
- data/spec/select_lists_spec.rb +151 -0
- data/{test/test_helper.rb → spec/spec_helper.rb} +6 -4
- data/spec/spec_helper_ff.rb +5 -0
- data/spec/spec_helper_runner.rb +13 -0
- data/spec/spec_results.html +566 -0
- data/spec/spec_results.txt +179 -0
- data/spec/spec_results_failed.txt +1 -0
- data/spec/text_fields_spec.rb +56 -0
- data/watirloo.gemspec +44 -44
- metadata +80 -39
- data/lib/watirloo/reflector.rb +0 -137
- data/test/checkbox_group_test.rb +0 -83
- data/test/checkboxes_value_test.rb +0 -50
- data/test/html/checkbox_group1.html +0 -20
- data/test/html/labels.html +0 -32
- data/test/html/radio_group.html +0 -41
- data/test/interfaces_test.rb +0 -79
- data/test/label_test.rb +0 -64
- data/test/person_def_wrappers_test.rb +0 -55
- data/test/radio_group_test.rb +0 -97
- data/test/select_list_in_class_test.rb +0 -39
- data/test/select_list_options_test.rb +0 -39
- data/test/select_lists_test.rb +0 -145
- data/test/text_fields_test.rb +0 -68
@@ -0,0 +1,73 @@
|
|
1
|
+
module Watirloo
|
2
|
+
|
3
|
+
# This is 'opinionated' method.
|
4
|
+
# The way I work with browsers is this:
|
5
|
+
# I save the current handle of the browser (ie.hwnd) to the storage yaml file so I can reattach to the same
|
6
|
+
# browser later. Basically in exploratory testing I don't want to start and close browsers. I want to maintain
|
7
|
+
# reference to one (or more) browsers and I have nicknames for them.
|
8
|
+
# on restart of tests useing Watirloo I reuse the browser. If the browser is not there I just start a new browser which
|
9
|
+
# will from now on become my new 'default' test session browser.
|
10
|
+
# In Case of Firefox I attach to the 'one' existing firefox or a start a new one.
|
11
|
+
# So this method either attaches to one that's there or it starts a new one and puts it in Browsers::Storage
|
12
|
+
# this way of working with browsers is opinionated I think.
|
13
|
+
# if you want you can just use Watir::IE.start and reuse that browsers for tests. This here is a convenience method
|
14
|
+
def self.browser(key = 'default')
|
15
|
+
case Browsers.target
|
16
|
+
when :ie then Browsers.ie key
|
17
|
+
when :firefox then Browsers.ff
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# manage references to browsers. Currently IE or Firefox.
|
22
|
+
# Safari? Other Browser? not yet
|
23
|
+
module Browsers
|
24
|
+
|
25
|
+
@@target = :ie #by default we talk to IE on Windows.
|
26
|
+
@@targets = [:ie, :firefox]
|
27
|
+
|
28
|
+
class << self
|
29
|
+
|
30
|
+
# set and get the target. by default we talk to :ie.
|
31
|
+
def target
|
32
|
+
return @@target
|
33
|
+
end
|
34
|
+
|
35
|
+
def target=(indicator)
|
36
|
+
if @@targets.include? indicator
|
37
|
+
@@target = indicator
|
38
|
+
else
|
39
|
+
raise Exception, "target indicator #{indicator} is not valid: use :ie or :firefox"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# provides browser instance to client.
|
45
|
+
# attaches to the existing 'default' test session browser on the desktop
|
46
|
+
# By convention the mental model here is that we are working
|
47
|
+
# with one browser on the desktop. This is how a person would typically work
|
48
|
+
def ie(key='default')
|
49
|
+
begin
|
50
|
+
Locker.browser key
|
51
|
+
rescue => e #XXX it's probably a bad practice to use exception for logic control
|
52
|
+
# TODO logger here
|
53
|
+
ie = Watir::IE.start
|
54
|
+
sleep 3
|
55
|
+
Locker.add(ie, key)
|
56
|
+
ie #return newly created browser for the test session and store it for laterusage
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def ff
|
61
|
+
require 'watirloo/firewatir_ducktape'
|
62
|
+
# this is a cruch for quick work with pages.
|
63
|
+
# in reality you want to create a browser and pass it as argument to initialize Page class
|
64
|
+
begin
|
65
|
+
FireWatir::Firefox.attach #this attach is a crutch
|
66
|
+
rescue
|
67
|
+
puts 'got to start browser ff dude'
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Watirloo
|
2
|
+
|
3
|
+
# The browser desktop manager
|
4
|
+
# checks to see what browsers already exist on the dekstop.
|
5
|
+
# compares what is on the desktop to what was there last time
|
6
|
+
module Desktop
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# returns browser windows found on the desktop
|
11
|
+
def browsers
|
12
|
+
brs =[]
|
13
|
+
Watir::IE.each {|ie| brs << ie }
|
14
|
+
brs
|
15
|
+
end
|
16
|
+
|
17
|
+
# return handles of Browsers found on desktop
|
18
|
+
def hwnds
|
19
|
+
hs =[]
|
20
|
+
browsers.each {|ie| hs << ie.hwnd}
|
21
|
+
hs
|
22
|
+
end
|
23
|
+
|
24
|
+
# returns handles for browsers that appeared on Desktop since the last scan for browsers
|
25
|
+
def additions(known_hwnds)
|
26
|
+
hwnds.select {|h| !known_hwnds.include?(h)}
|
27
|
+
end
|
28
|
+
|
29
|
+
# returns handles for browsers no longer found on the Desktop since the last scan for browsers
|
30
|
+
def deletions(known_hwnds)
|
31
|
+
known_hwnds.select {|h| !hwnds.include?(h)}
|
32
|
+
end
|
33
|
+
|
34
|
+
# Closes all the browsers on the desktop.
|
35
|
+
# Creats a known clear slate where no browsers exist
|
36
|
+
def clear
|
37
|
+
Watir::IE.each {|ie| ie.close}
|
38
|
+
sleep 3
|
39
|
+
raise Exception, "Failed to clear all the browsers from the desktop" unless browsers.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
# see this;http://blog.jayfields.com/2008/02/ruby-dynamically-define-method.html
|
3
|
+
# instance_exec is used for face methods
|
4
|
+
if VERSION <= '1.8.6'
|
5
|
+
class Object
|
6
|
+
module InstanceExecHelper; end
|
7
|
+
include InstanceExecHelper
|
8
|
+
# instance_exec method evaluates a block of code relative to the specified object, with parameters whom come from outside the object.
|
9
|
+
def instance_exec(*args, &block)
|
10
|
+
begin
|
11
|
+
old_critical, Thread.critical = Thread.critical, true
|
12
|
+
n = 0
|
13
|
+
n += 1 while respond_to?(mname="__instance_exec#{n}")
|
14
|
+
InstanceExecHelper.module_eval{ define_method(mname, &block) }
|
15
|
+
ensure
|
16
|
+
Thread.critical = old_critical
|
17
|
+
end
|
18
|
+
begin
|
19
|
+
ret = send(mname, *args)
|
20
|
+
ensure
|
21
|
+
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
|
22
|
+
end
|
23
|
+
ret
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -59,13 +59,13 @@ module Watir
|
|
59
59
|
|
60
60
|
def values
|
61
61
|
opts = []
|
62
|
-
@o.each {|
|
62
|
+
@o.each {|rc| opts << rc.ole_object.invoke('value')}
|
63
63
|
return opts
|
64
64
|
end
|
65
65
|
|
66
66
|
def get_by_value value
|
67
67
|
if values.member? value
|
68
|
-
@o.find {|
|
68
|
+
@o.find {|rc| rc.ole_object.invoke('value') == value}
|
69
69
|
else
|
70
70
|
raise ::Watir::Exception::WatirException, "value #{value} not found in hidden_values"
|
71
71
|
end
|
@@ -93,6 +93,14 @@ module Watir
|
|
93
93
|
def selected_radio
|
94
94
|
@o.find {|r| r.isSet?}
|
95
95
|
end
|
96
|
+
|
97
|
+
# if a radio button in a group is set then the group is set
|
98
|
+
# by default it should be set but many HTML implementations provide
|
99
|
+
# the radiogroup to the user with no default one set (Bad practice perhaps)
|
100
|
+
def set?
|
101
|
+
selected_radio ? true : false
|
102
|
+
end
|
103
|
+
|
96
104
|
end
|
97
105
|
|
98
106
|
# radios that share the same :name attribute form a RadioGroup.
|
@@ -118,11 +126,30 @@ module Watir
|
|
118
126
|
include RadioCheckGroup
|
119
127
|
include RadioGroupCommonWatir
|
120
128
|
|
121
|
-
def initialize(container,
|
129
|
+
def initialize(container, how, what)
|
122
130
|
@container = container
|
123
|
-
@
|
124
|
-
@
|
131
|
+
@how = how
|
132
|
+
@what = what
|
133
|
+
@o = locate
|
134
|
+
end
|
135
|
+
|
136
|
+
def name
|
137
|
+
@name
|
138
|
+
end
|
139
|
+
|
140
|
+
def locate
|
141
|
+
@name = case @how
|
142
|
+
when :name then @what
|
143
|
+
when :index then
|
144
|
+
names = []
|
145
|
+
@container.radios.each do |r|
|
146
|
+
names << r.name
|
147
|
+
end
|
148
|
+
names.uniq.at(@what-1) # follow 1-based index addressing for Watir API
|
149
|
+
end
|
150
|
+
@container.radios.find_all {|r| r.name == @name}
|
125
151
|
end
|
152
|
+
private :locate
|
126
153
|
|
127
154
|
# which value is selected?. returns value text as string
|
128
155
|
# So per HTML401 spec I am not sure if we should ever have empyt array returned here
|
@@ -138,17 +165,66 @@ module Watir
|
|
138
165
|
|
139
166
|
end
|
140
167
|
|
168
|
+
class TextFields < ElementCollections
|
169
|
+
|
170
|
+
def reflect
|
171
|
+
ret = []
|
172
|
+
self.each do |item|
|
173
|
+
how, what = get_how_what get_attribs(item)
|
174
|
+
facename = suggest_def_name what
|
175
|
+
value = item.value
|
176
|
+
# this approach relies on doc element
|
177
|
+
ret << "face(:#{facename}) {doc.text_field(:#{how}, #{what.inspect})}"
|
178
|
+
ret << "#{facename}.value.should == #{value.inspect}"
|
179
|
+
end
|
180
|
+
ret
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
class RadioGroups < ElementCollections
|
186
|
+
|
187
|
+
def element_class; RadioGroup; end
|
188
|
+
def length
|
189
|
+
names = []
|
190
|
+
@container.radios.each do |r|
|
191
|
+
names << r.name
|
192
|
+
end
|
193
|
+
names.uniq.size #non repeating names
|
194
|
+
end
|
195
|
+
|
196
|
+
def reflect
|
197
|
+
ret = []
|
198
|
+
self.each do |item|
|
199
|
+
name = item.name
|
200
|
+
facename = suggest_def_name name
|
201
|
+
values = item.values
|
202
|
+
selected = item.selected
|
203
|
+
ret << "face(:#{facename}) {doc.radio_group(#{name.inspect})}"
|
204
|
+
ret << "#{facename}.values.should == #{values.inspect}"
|
205
|
+
ret << "#{facename}.selected.should == #{selected.inspect}"
|
206
|
+
end
|
207
|
+
ret
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
private
|
212
|
+
def iterator_object(i)
|
213
|
+
@container.radio_group(:index, i + 1)
|
214
|
+
end
|
215
|
+
end
|
141
216
|
|
142
217
|
module CheckboxGroupCommonWatir
|
143
218
|
|
144
|
-
# returns selected
|
145
|
-
# []
|
146
|
-
# [checkbox, checkbox] = checkboxes that are selected
|
219
|
+
# returns selected checkboxes as array
|
220
|
+
# when empty [] then nothing is selected
|
221
|
+
# when [checkbox, checkbox] = array of checkboxes that are selected
|
222
|
+
# that you can iterate over for tests.
|
147
223
|
def selected_checkboxes
|
148
224
|
@o.select {|cb| cb.isSet?}
|
149
225
|
end
|
150
226
|
|
151
|
-
#
|
227
|
+
# convenience method as a filter for selected_values
|
152
228
|
# returns:
|
153
229
|
# nil => when no checkbox is set
|
154
230
|
# 'value' => if one checkbox is set
|
@@ -168,6 +244,14 @@ module Watir
|
|
168
244
|
# compare to SelectList where selected returns selected_item
|
169
245
|
alias selected selected_value
|
170
246
|
|
247
|
+
|
248
|
+
# if at least one checkbox is selected then the group is considered set
|
249
|
+
def set?
|
250
|
+
(selected_checkboxes != []) ? true : false
|
251
|
+
end
|
252
|
+
|
253
|
+
alias checked? set?
|
254
|
+
|
171
255
|
end
|
172
256
|
|
173
257
|
# Checkbox group semantically behaves like multi select list box.
|
@@ -179,11 +263,30 @@ module Watir
|
|
179
263
|
include RadioCheckGroup
|
180
264
|
include CheckboxGroupCommonWatir
|
181
265
|
|
182
|
-
def initialize(container,
|
266
|
+
def initialize(container, how, what)
|
183
267
|
@container = container
|
184
|
-
@
|
185
|
-
@
|
268
|
+
@how = how
|
269
|
+
@what = what
|
270
|
+
@o = locate
|
271
|
+
end
|
272
|
+
|
273
|
+
def name
|
274
|
+
@name
|
275
|
+
end
|
276
|
+
|
277
|
+
def locate
|
278
|
+
@name = case @how
|
279
|
+
when :name then @what
|
280
|
+
when :index then
|
281
|
+
names = []
|
282
|
+
@container.checkboxes.each do |cb|
|
283
|
+
names << cb.name
|
284
|
+
end
|
285
|
+
names.uniq.at(@what-1) # follow 1-based index addressing for Watir API
|
286
|
+
end
|
287
|
+
@container.checkboxes.find_all {|cb| cb.name == @name}
|
186
288
|
end
|
289
|
+
private :locate
|
187
290
|
|
188
291
|
# returns array of value attributes. Each Checkbox in a group
|
189
292
|
# has a value which is invisible to the user
|
@@ -196,15 +299,57 @@ module Watir
|
|
196
299
|
end
|
197
300
|
end
|
198
301
|
|
302
|
+
class CheckboxGroups < ElementCollections
|
303
|
+
def element_class; CheckboxGroup; end
|
304
|
+
def length
|
305
|
+
names = []
|
306
|
+
@container.checkboxes.each do |cb|
|
307
|
+
names << cb.name
|
308
|
+
end
|
309
|
+
names.uniq.size #non repeating names
|
310
|
+
end
|
311
|
+
|
312
|
+
def reflect
|
313
|
+
ret = []
|
314
|
+
self.each do |item|
|
315
|
+
name = item.name
|
316
|
+
facename = suggest_def_name(name)
|
317
|
+
values = item.values
|
318
|
+
selected = item.selected
|
319
|
+
ret << "face(:#{facename}) {doc.checkbox_group(#{name.inspect})}"
|
320
|
+
ret << "#{facename}.values.should == #{values.inspect}"
|
321
|
+
ret << "#{facename}.selected.should == #{selected.inspect}"
|
322
|
+
end
|
323
|
+
ret
|
324
|
+
end
|
325
|
+
|
326
|
+
private
|
327
|
+
def iterator_object(i)
|
328
|
+
@container.checkbox_group(:index, i + 1)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
199
332
|
|
200
333
|
module Container
|
201
|
-
|
202
|
-
|
334
|
+
|
335
|
+
def radio_group(how, what=nil)
|
336
|
+
how, what = process_default :name, how, what
|
337
|
+
RadioGroup.new(self, how, what)
|
203
338
|
end
|
204
339
|
|
205
|
-
def
|
206
|
-
|
340
|
+
def radio_groups
|
341
|
+
RadioGroups.new(self)
|
342
|
+
end
|
343
|
+
|
344
|
+
def checkbox_group(how, what=nil)
|
345
|
+
how, what = process_default :name, how, what
|
346
|
+
CheckboxGroup.new(self, how, what)
|
347
|
+
end
|
348
|
+
|
349
|
+
def checkbox_groups
|
350
|
+
CheckboxGroups.new(self)
|
207
351
|
end
|
352
|
+
|
208
353
|
end
|
209
354
|
|
210
355
|
class RadioCheckCommon
|
@@ -372,5 +517,25 @@ module Watir
|
|
372
517
|
# alias, items or contents return the same visible text items
|
373
518
|
alias items getAllContents
|
374
519
|
|
520
|
+
|
521
|
+
|
522
|
+
def reflect
|
523
|
+
ret = []
|
524
|
+
self.each do |item|
|
525
|
+
name = item.name
|
526
|
+
facename = suggest_def_name name
|
527
|
+
values = item.values
|
528
|
+
items = item.items
|
529
|
+
selected_item = item.selected_item
|
530
|
+
selected_value = item.selected_value
|
531
|
+
|
532
|
+
ret << "face(:#{facename}) {doc.select_list(:name, #{name.inspect})}"
|
533
|
+
ret << "#{facename}.items.should == #{items.inspect}"
|
534
|
+
ret << "#{facename}.values.should == #{values.inspect}"
|
535
|
+
ret << "#{facename}.selected_item.should == #{selected_item.inspect}"
|
536
|
+
ret << "#{facename}.selected_value.should == #{selected_value.inspect}"
|
537
|
+
end
|
538
|
+
ret
|
539
|
+
end
|
375
540
|
end
|
376
541
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Look Ma!, I can Has Reflect The Browser
|
3
|
+
|
4
|
+
Watir::Reflector module added to watir.
|
5
|
+
reflect watir element collections. 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
|
+
=end
|
9
|
+
|
10
|
+
module Watir
|
11
|
+
|
12
|
+
# Watirloo::Page objects scaffold creation. Talks to the current page and reflects
|
13
|
+
# the watir elements to be used for semantic test objects tests.
|
14
|
+
module Reflector
|
15
|
+
|
16
|
+
#cleanup the def name for some kind of semantic name
|
17
|
+
def suggest_def_name(how)
|
18
|
+
how.gsub!(/_+/,'_') # double underscores to one
|
19
|
+
how.gsub!(/^_/, '') # if it begins with undrscore kill it.
|
20
|
+
how.gsub!(/\s+/, '_') # kill spaces if for some strange reason they exist
|
21
|
+
how.underscore #Any CamelCase will be converted to camel_no_case
|
22
|
+
end
|
23
|
+
|
24
|
+
# glean(:text_fields, [:id, :name, :value]
|
25
|
+
# glean(:radios, [:id, :name, :value])
|
26
|
+
# glean and make a map of types and attributes needed for reflection
|
27
|
+
# this should be private I think
|
28
|
+
def get_attribs(item)
|
29
|
+
attribs = [:id, :name, :value]
|
30
|
+
h = {}
|
31
|
+
attribs.each do |k|
|
32
|
+
v = item.attribute_value k.to_s
|
33
|
+
h[k] = v
|
34
|
+
end
|
35
|
+
h
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_how_what h
|
39
|
+
how, what = '', ''
|
40
|
+
if h[:id] != '' #First Choice: if id is not blank then we'll use it
|
41
|
+
how, what = :id, h[:id]
|
42
|
+
elsif h[:name] != '' #Second Choice: if name is not blank then we'll use it instead of id
|
43
|
+
how, what = :name, h[:name]
|
44
|
+
elsif h[:value] != ''
|
45
|
+
how, what = :value, h[:value]
|
46
|
+
end
|
47
|
+
[how, what]
|
48
|
+
end
|
49
|
+
|
50
|
+
# public interface for Reflector.
|
51
|
+
# ie.reflect # => returns object definitions for entire dom using ie as container
|
52
|
+
# ie.frame('main').select_lists.reflect# => returns definitions for select_lists
|
53
|
+
# only contained by the frame
|
54
|
+
# you can be as granular as needed
|
55
|
+
def reflect
|
56
|
+
puts "I has not exist. Implements me please"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
module Container
|
62
|
+
|
63
|
+
# container asks collections to reflect themselves
|
64
|
+
# each collection knows how to reflect itself and what to reflect
|
65
|
+
def reflect
|
66
|
+
ref = []
|
67
|
+
[:radio_groups, :checkbox_groups, :text_fields, :select_lists].each do |type|
|
68
|
+
ret << self.send(type).reflect
|
69
|
+
end
|
70
|
+
return ref
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
class ElementCollections
|
77
|
+
|
78
|
+
# adds reflect method to element collections
|
79
|
+
include ::Watir::Reflector
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|