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.
Files changed (67) hide show
  1. data/History.txt +12 -0
  2. data/Manifest.txt +59 -34
  3. data/README.rdoc +63 -80
  4. data/Rakefile.rb +32 -39
  5. data/config/locker.yml +0 -0
  6. data/lib/watirloo.rb +9 -162
  7. data/lib/watirloo/browsers.rb +73 -0
  8. data/lib/watirloo/desktop.rb +44 -0
  9. data/lib/watirloo/{firewatir_ducktape.rb → extension/firewatir_ducktape.rb} +0 -0
  10. data/lib/watirloo/extension/object.rb +26 -0
  11. data/lib/watirloo/{watir_ducktape.rb → extension/watir_ducktape.rb} +181 -16
  12. data/lib/watirloo/extension/watir_reflector.rb +83 -0
  13. data/lib/watirloo/locker.rb +84 -0
  14. data/lib/watirloo/page.rb +105 -0
  15. data/spec/browser_spec.rb +38 -0
  16. data/spec/browser_threads_spec.rb +45 -0
  17. data/spec/checkbox_group_spec.rb +136 -0
  18. data/spec/checkbox_groups_spec.rb +55 -0
  19. data/spec/checkboxes_value_spec.rb +35 -0
  20. data/spec/desktop_spec.rb +54 -0
  21. data/spec/extra/browser_events_spec.rb +76 -0
  22. data/spec/extra/page_objects_metrics.rb +139 -0
  23. data/spec/face_mixing_spec.rb +55 -0
  24. data/{test → spec}/firewatir/attach_instance_test.rb +0 -0
  25. data/spec/firewatir/spec_results.html +263 -0
  26. data/spec/firewatir/spec_results.txt +23 -0
  27. data/spec/firewatir/spec_results_failed.txt +3 -0
  28. data/{test → spec}/html/census.html +0 -0
  29. data/spec/html/checkbox_group1.html +33 -0
  30. data/spec/html/labels.html +53 -0
  31. data/spec/html/no_title.html +13 -0
  32. data/{test → spec}/html/person.html +0 -0
  33. data/spec/html/radio_group.html +35 -0
  34. data/{test → spec}/html/select_lists.html +0 -0
  35. data/spec/input_element_spec.rb +51 -0
  36. data/spec/label_spec.rb +65 -0
  37. data/spec/locker_spec.rb +49 -0
  38. data/spec/page_spec.rb +53 -0
  39. data/spec/person_def_wrappers_spec.rb +40 -0
  40. data/spec/radio_group_spec.rb +95 -0
  41. data/spec/radio_groups_spec.rb +55 -0
  42. data/spec/reflector_spec.rb +82 -0
  43. data/spec/select_list_options_spec.rb +40 -0
  44. data/spec/select_lists_spec.rb +151 -0
  45. data/{test/test_helper.rb → spec/spec_helper.rb} +6 -4
  46. data/spec/spec_helper_ff.rb +5 -0
  47. data/spec/spec_helper_runner.rb +13 -0
  48. data/spec/spec_results.html +566 -0
  49. data/spec/spec_results.txt +179 -0
  50. data/spec/spec_results_failed.txt +1 -0
  51. data/spec/text_fields_spec.rb +56 -0
  52. data/watirloo.gemspec +44 -44
  53. metadata +80 -39
  54. data/lib/watirloo/reflector.rb +0 -137
  55. data/test/checkbox_group_test.rb +0 -83
  56. data/test/checkboxes_value_test.rb +0 -50
  57. data/test/html/checkbox_group1.html +0 -20
  58. data/test/html/labels.html +0 -32
  59. data/test/html/radio_group.html +0 -41
  60. data/test/interfaces_test.rb +0 -79
  61. data/test/label_test.rb +0 -64
  62. data/test/person_def_wrappers_test.rb +0 -55
  63. data/test/radio_group_test.rb +0 -97
  64. data/test/select_list_in_class_test.rb +0 -39
  65. data/test/select_list_options_test.rb +0 -39
  66. data/test/select_lists_test.rb +0 -145
  67. 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
@@ -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 {|r| opts << r.ole_object.invoke('value')}
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 {|r| r.ole_object.invoke('value') == value}
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, name)
129
+ def initialize(container, how, what)
122
130
  @container = container
123
- @name = name
124
- @o = @container.radios.find_all {|r| r.name == @name}
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 checkboxex as array
145
- # [] = nothing selected
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
- # convinience method as a filter for select_values
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, name)
266
+ def initialize(container, how, what)
183
267
  @container = container
184
- @name = name
185
- @o = @container.checkboxes.find_all {|cb| cb.name == @name}
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
- def radio_group(name)
202
- RadioGroup.new(self, name)
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 checkbox_group(name)
206
- CheckboxGroup.new(self, name)
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