marekj-watirloo 0.0.3 → 0.0.5

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.
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