rautomation 0.4.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/History.rdoc +16 -1
  2. data/LICENSE +1 -1
  3. data/README.rdoc +25 -7
  4. data/Rakefile +4 -0
  5. data/VERSION +1 -1
  6. data/ext/IAccessibleDLL/IAccessibleDLL.sln +20 -0
  7. data/ext/IAccessibleDLL/IAccessibleDLL.suo +0 -0
  8. data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.cpp +36 -0
  9. data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.vcxproj +100 -0
  10. data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.vcxproj.filters +39 -0
  11. data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.vcxproj.user +3 -0
  12. data/ext/IAccessibleDLL/IAccessibleDLL/ReadMe.txt +48 -0
  13. data/ext/IAccessibleDLL/IAccessibleDLL/dllmain.cpp +19 -0
  14. data/ext/IAccessibleDLL/IAccessibleDLL/stdafx.cpp +8 -0
  15. data/ext/IAccessibleDLL/IAccessibleDLL/stdafx.h +16 -0
  16. data/ext/IAccessibleDLL/IAccessibleDLL/targetver.h +8 -0
  17. data/ext/IAccessibleDLL/Release/IAccessibleDLL.dll +0 -0
  18. data/ext/IAccessibleDLL/Release/IAccessibleDLL.exp +0 -0
  19. data/ext/IAccessibleDLL/Release/IAccessibleDLL.lib +0 -0
  20. data/ext/WindowsForms/bin/WindowsForms.exe +0 -0
  21. data/ext/WindowsForms/src/WindowsForms/WindowsForms.sln +20 -0
  22. data/ext/WindowsForms/src/WindowsForms/WindowsForms.suo +0 -0
  23. data/ext/WindowsForms/src/WindowsForms/WindowsForms/AboutBox.Designer.cs +80 -0
  24. data/ext/WindowsForms/src/WindowsForms/WindowsForms/AboutBox.cs +103 -0
  25. data/ext/WindowsForms/src/WindowsForms/WindowsForms/AboutBox.resx +120 -0
  26. data/ext/WindowsForms/src/WindowsForms/WindowsForms/DataEntryForm.Designer.cs +167 -0
  27. data/ext/WindowsForms/src/WindowsForms/WindowsForms/DataEntryForm.cs +45 -0
  28. data/ext/WindowsForms/src/WindowsForms/WindowsForms/DataEntryForm.resx +120 -0
  29. data/ext/WindowsForms/src/WindowsForms/WindowsForms/MainFormWindow.Designer.cs +278 -0
  30. data/ext/WindowsForms/src/WindowsForms/WindowsForms/MainFormWindow.cs +78 -0
  31. data/ext/WindowsForms/src/WindowsForms/WindowsForms/MainFormWindow.resx +120 -0
  32. data/ext/WindowsForms/src/WindowsForms/WindowsForms/PersonForm.Designer.cs +119 -0
  33. data/ext/WindowsForms/src/WindowsForms/WindowsForms/PersonForm.cs +34 -0
  34. data/ext/WindowsForms/src/WindowsForms/WindowsForms/PersonForm.resx +120 -0
  35. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Program.cs +21 -0
  36. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/AssemblyInfo.cs +36 -0
  37. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Resources.Designer.cs +71 -0
  38. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Resources.resx +117 -0
  39. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Settings.Designer.cs +30 -0
  40. data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Settings.settings +7 -0
  41. data/ext/WindowsForms/src/WindowsForms/WindowsForms/SimpleElementsForm.Designer.cs +93 -0
  42. data/ext/WindowsForms/src/WindowsForms/WindowsForms/SimpleElementsForm.cs +19 -0
  43. data/ext/WindowsForms/src/WindowsForms/WindowsForms/SimpleElementsForm.resx +120 -0
  44. data/ext/WindowsForms/src/WindowsForms/WindowsForms/WindowsForms.csproj +123 -0
  45. data/ext/WindowsForms/src/WindowsForms/WindowsForms/bin/Debug/WindowsForms.exe +0 -0
  46. data/ext/WindowsForms/src/WindowsForms/WindowsForms/bin/Debug/WindowsForms.pdb +0 -0
  47. data/ext/WindowsForms/src/WindowsForms/WindowsForms/bin/Debug/WindowsForms.vshost.exe +0 -0
  48. data/ext/WindowsForms/src/WindowsForms/WindowsForms/bin/Debug/WindowsForms.vshost.exe.manifest +11 -0
  49. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/DesignTimeResolveAssemblyReferences.cache +0 -0
  50. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache +0 -0
  51. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/GenerateResource.read.1.tlog +0 -0
  52. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/GenerateResource.write.1.tlog +0 -0
  53. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/ResolveAssemblyReference.cache +0 -0
  54. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/WindowsForms.AboutBox.resources +0 -0
  55. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/WindowsForms.DataEntryForm.resources +0 -0
  56. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/WindowsForms.MainFormWindow.resources +0 -0
  57. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/WindowsForms.PersonForm.resources +0 -0
  58. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/WindowsForms.Properties.Resources.resources +0 -0
  59. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/WindowsForms.SimpleElementsForm.resources +0 -0
  60. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/WindowsForms.csproj.FileListAbsolute.txt +25 -0
  61. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/WindowsForms.exe +0 -0
  62. data/ext/WindowsForms/src/WindowsForms/WindowsForms/obj/x86/Debug/WindowsForms.pdb +0 -0
  63. data/lib/{RAutomation.rb → rautomation.rb} +3 -1
  64. data/lib/rautomation/adapter/autoit/button.rb +7 -4
  65. data/lib/rautomation/adapter/autoit/locators.rb +4 -2
  66. data/lib/rautomation/adapter/autoit/text_field.rb +8 -5
  67. data/lib/rautomation/adapter/autoit/window.rb +23 -7
  68. data/lib/rautomation/adapter/win_ffi.rb +9 -3
  69. data/lib/rautomation/adapter/win_ffi/button.rb +3 -34
  70. data/lib/rautomation/adapter/win_ffi/button_helper.rb +24 -0
  71. data/lib/rautomation/adapter/win_ffi/checkbox.rb +12 -0
  72. data/lib/rautomation/adapter/win_ffi/constants.rb +21 -0
  73. data/lib/rautomation/adapter/win_ffi/control.rb +46 -0
  74. data/lib/rautomation/adapter/win_ffi/functions.rb +52 -1
  75. data/lib/rautomation/adapter/win_ffi/locators.rb +2 -1
  76. data/lib/rautomation/adapter/win_ffi/radio.rb +12 -0
  77. data/lib/rautomation/adapter/win_ffi/select_list.rb +64 -0
  78. data/lib/rautomation/adapter/win_ffi/table.rb +18 -0
  79. data/lib/rautomation/adapter/win_ffi/text_field.rb +4 -20
  80. data/lib/rautomation/adapter/win_ffi/window.rb +48 -10
  81. data/lib/rautomation/element_collections.rb +44 -0
  82. data/lib/rautomation/window.rb +29 -5
  83. data/spec/adapter/win_ffi/checkbox_spec.rb +29 -0
  84. data/spec/adapter/win_ffi/radio_spec.rb +21 -0
  85. data/spec/adapter/win_ffi/select_list_spec.rb +41 -0
  86. data/spec/adapter/win_ffi/table_spec.rb +20 -0
  87. data/spec/adapter/win_ffi/window_spec.rb +6 -12
  88. data/spec/button_spec.rb +28 -20
  89. data/spec/buttons_spec.rb +21 -0
  90. data/spec/spec_helper.rb +46 -37
  91. data/spec/text_field_spec.rb +11 -11
  92. data/spec/text_fields_spec.rb +22 -0
  93. data/spec/window_spec.rb +11 -19
  94. data/spec/windows_spec.rb +55 -0
  95. metadata +84 -9
  96. data/.gitignore +0 -25
  97. data/rautomation.gemspec +0 -90
  98. data/spec/test.html +0 -15
@@ -0,0 +1,12 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+ class Checkbox < Control
5
+ include WaitHelper
6
+ include Locators
7
+ include ButtonHelper
8
+
9
+ end
10
+ end
11
+ end
12
+ end
@@ -19,6 +19,8 @@ module RAutomation
19
19
  PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF
20
20
 
21
21
  BM_CLICK = 0xF5
22
+ BM_GETSTATE = 0xF2
23
+ BST_CHECKED = 0x1
22
24
 
23
25
  # keybd_event constants
24
26
  KEYEVENTF_EXTENDEDKEY = 0x1
@@ -26,6 +28,25 @@ module RAutomation
26
28
 
27
29
  # GetWindow constants
28
30
  GW_ENABLEDPOPUP = 6
31
+
32
+ # HRESULT
33
+ S_OK = 0
34
+
35
+ # IAccessible Button States
36
+ STATE_SYSTEM_CHECKED = 0x00000010
37
+
38
+ # Combobox
39
+ CB_GETCOUNT = 0x0146
40
+ CB_GETLBTEXTLEN = 0x0149
41
+ CB_GETLBTEXT = 0x0148
42
+ CB_GETCURSEL = 0x0147
43
+ CB_ERR = -1
44
+ CB_SETCURSEL = 0x14E
45
+
46
+ # listview
47
+ LVM_FIRST = 0x1000
48
+ LVM_GETITEMCOUNT = LVM_FIRST + 4
49
+
29
50
  end
30
51
  end
31
52
  end
@@ -0,0 +1,46 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+ class Control
5
+ include WaitHelper
6
+ include Locators
7
+
8
+ # Creates the control object.
9
+ # @note this method is not meant to be accessed directly
10
+ # @param [RAutomation::Window] window this button belongs to.
11
+ # @param [Hash] locators for searching the button.
12
+ # @option locators [String, Regexp] :value Value (text) of the button
13
+ # @option locators [String, Regexp] :class Internal class name of the button
14
+ # @option locators [String, Fixnum] :id Internal ID of the button
15
+ # @option locators [String, Fixnum] :index 0-based index to specify n-th button if all other criteria match
16
+ # @see RAutomation::Window#button
17
+ def initialize(window, locators)
18
+ @window = window
19
+ extract(locators)
20
+ end
21
+
22
+ def click
23
+ clicked = false
24
+ wait_until do
25
+ hwnd = Functions.control_hwnd(@window.hwnd, @locators)
26
+
27
+ @window.activate
28
+ @window.active? &&
29
+ Functions.set_control_focus(hwnd) &&
30
+ Functions.control_click(hwnd) &&
31
+ clicked = true # is clicked at least once
32
+
33
+ block_given? ? yield : clicked && !exist?
34
+ end
35
+ end
36
+
37
+ def exist?
38
+ !!Functions.control_hwnd(@window.hwnd, @locators)
39
+ end
40
+
41
+ alias_method :exists?, :exist?
42
+
43
+ end
44
+ end
45
+ end
46
+ end
@@ -5,7 +5,8 @@ module RAutomation
5
5
  module Functions
6
6
  extend FFI::Library
7
7
 
8
- ffi_lib 'user32', 'kernel32'
8
+ # TODO once done adapt the path to the DLL (somewhere in the packaged gem)
9
+ ffi_lib 'user32', 'kernel32', 'ole32', File.dirname(__FILE__) + '/../../../../ext/IAccessibleDLL/Release/iaccessibleDll.dll'
9
10
  ffi_convention :stdcall
10
11
 
11
12
  callback :enum_callback, [:long, :pointer], :bool
@@ -57,6 +58,8 @@ module RAutomation
57
58
  [:long], :long
58
59
  attach_function :get_window, :GetWindow,
59
60
  [:long, :uint], :long
61
+ attach_function :get_last_error, :GetLastError,
62
+ [], :long
60
63
 
61
64
  # kernel32
62
65
  attach_function :open_process, :OpenProcess,
@@ -65,8 +68,19 @@ module RAutomation
65
68
  [:long, :uint], :bool
66
69
  attach_function :close_handle, :CloseHandle,
67
70
  [:long], :bool
71
+ attach_function :load_library, :LoadLibraryA,
72
+ [:string], :long
73
+
74
+ # ole32
75
+ attach_function :co_initialize, :CoInitialize,
76
+ [:pointer], :uint16
77
+
78
+ # iaccessible
79
+ attach_function :get_button_state, :get_button_state,
80
+ [:long], :long
68
81
 
69
82
  class << self
83
+
70
84
  def window_title(hwnd)
71
85
  title_length = window_title_length(hwnd) + 1
72
86
  title = FFI::MemoryPointer.new :char, title_length
@@ -108,6 +122,20 @@ module RAutomation
108
122
  locators
109
123
  end
110
124
 
125
+ def child_window_locators(parent_hwnd, locators)
126
+ child_hwnd = locators[:hwnd] || child_hwnd(parent_hwnd, locators)
127
+ if child_hwnd
128
+ locators.merge!(:hwnd => child_hwnd)
129
+ else
130
+ popup_hwnd = get_window(parent_hwnd, Constants::GW_ENABLEDPOPUP)
131
+ if popup_hwnd != parent_hwnd
132
+ popup_properties = window_properties(popup_hwnd, locators)
133
+ locators.merge!(:hwnd => popup_hwnd) if locators_match?(locators, popup_properties)
134
+ end
135
+ end
136
+ locators
137
+ end
138
+
111
139
  def window_pid(hwnd)
112
140
  pid = FFI::MemoryPointer.new :int
113
141
  window_thread_process_id(hwnd, pid)
@@ -171,6 +199,28 @@ module RAutomation
171
199
  send_message(control_hwnd, Constants::WM_SETTEXT, 0, text)
172
200
  end
173
201
 
202
+ def control_set?(control_hwnd)
203
+ get_button_state(control_hwnd) & Constants::STATE_SYSTEM_CHECKED != 0
204
+ end
205
+
206
+ def retrieve_combobox_item_text(control_hwnd, item_no)
207
+ text_len = send_message(control_hwnd, Constants::CB_GETLBTEXTLEN, item_no, nil)
208
+
209
+ string_buffer = FFI::MemoryPointer.new :char, text_len
210
+ send_message(control_hwnd, Constants::CB_GETLBTEXT, item_no, string_buffer)
211
+ string_buffer.read_string
212
+ end
213
+
214
+ def control_name(control_hwnd)
215
+ puts "Trying to get control name for #{control_hwnd}"
216
+ string_buffer = FFI::MemoryPointer.new :char, 255
217
+ if (get_control_name(control_hwnd, string_buffer) == Constants::S_OK)
218
+ string_buffer.read_string
219
+ else
220
+ fail "Cannot get name for control with HWND 0x" + control_hwnd.to_s(16)
221
+ end
222
+ end
223
+
174
224
  private
175
225
 
176
226
  def within_foreground_thread(hwnd)
@@ -246,3 +296,4 @@ module RAutomation
246
296
  end
247
297
  end
248
298
  end
299
+
@@ -14,9 +14,10 @@ module RAutomation
14
14
 
15
15
  # control locator
16
16
  locators[:id] = locators[:id].to_i if locators[:id]
17
+ locators = self.class::DEFAULT_LOCATORS.merge(locators) if self.class.const_defined?(:DEFAULT_LOCATORS)
17
18
  @locators = locators
18
19
  end
19
20
  end
20
21
  end
21
22
  end
22
- end
23
+ end
@@ -0,0 +1,12 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+ class Radio < Control
5
+ include WaitHelper
6
+ include Locators
7
+ include ButtonHelper
8
+
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,64 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+
5
+ class SelectList < Control
6
+ include WaitHelper
7
+ include Locators
8
+
9
+ class SelectListOption
10
+ attr_accessor :text, :index, :control_hwnd
11
+
12
+ def initialize(control_hwnd, text, index)
13
+ @control_hwnd = control_hwnd
14
+ @text = text
15
+ @index = index
16
+ end
17
+
18
+ def selected?
19
+ selected_idx = Functions.send_message(@control_hwnd, Constants::CB_GETCURSEL, 0, nil)
20
+ return false if selected_idx == Constants::CB_ERR
21
+ @text == Functions.retrieve_combobox_item_text(@control_hwnd, selected_idx)
22
+ end
23
+
24
+ def select
25
+ Functions.send_message(@control_hwnd, Constants::CB_SETCURSEL, @index, nil) != Constants::CB_ERR
26
+ end
27
+ end
28
+
29
+ def initialize(window, locators)
30
+ super
31
+ @hwnd = Functions.control_hwnd(@window.hwnd, @locators)
32
+ end
33
+
34
+ def options(options = {})
35
+ items = []
36
+
37
+ item_count.times do |item_no|
38
+ item = Functions.retrieve_combobox_item_text(@hwnd, item_no)
39
+
40
+ if options[:text]
41
+ items.push(SelectListOption.new(@hwnd, item, item_no)) if options[:text] == item
42
+ else
43
+ items.push(SelectListOption.new(@hwnd, item, item_no))
44
+ end
45
+ end
46
+
47
+ items
48
+ end
49
+
50
+ def value
51
+ selected_option = options.find {|option| option.selected?}
52
+ selected_option ? selected_option.text : ""
53
+ end
54
+
55
+ private
56
+
57
+ def item_count
58
+ Functions.send_message(@hwnd, Constants::CB_GETCOUNT, 0, nil)
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,18 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module WinFfi
4
+ class Table < Control
5
+ include WaitHelper
6
+ include Locators
7
+
8
+ private
9
+
10
+ def row_count
11
+ Functions.send_message(Functions.control_hwnd(@window.hwnd, @locators), Constants::LVM_GETITEMCOUNT, 0, nil)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -1,23 +1,12 @@
1
1
  module RAutomation
2
2
  module Adapter
3
3
  module WinFfi
4
- class TextField
4
+ class TextField < Control
5
5
  include WaitHelper
6
6
  include Locators
7
7
 
8
- # Creates the text field object.
9
- # @note this method is not meant to be accessed directly, but only through {RAutomation::Window#text_field}!
10
- # @param [RAutomation::Window] window this text field belongs to.
11
- # @param [Hash] locators for searching the text field.
12
- # @option locators [String, Regexp] :class Internal class name of the text field
13
- # @option locators [String, Regexp] :value Value (text) of the text field
14
- # @option locators [String, Fixnum] :id Internal ID of the text field
15
- # @option locators [String, Fixnum] :index 0-based index to specify n-th text field if all other criteria match
16
- # @see RAutomation::Window#text_field
17
- def initialize(window, locators)
18
- @window = window
19
- extract(locators)
20
- end
8
+ # Default locators used for searching text fields.
9
+ DEFAULT_LOCATORS = {:class => /edit/i}
21
10
 
22
11
  # @see RAutomation::TextField#set
23
12
  def set(text)
@@ -41,12 +30,7 @@ module RAutomation
41
30
  Functions.control_value(Functions.control_hwnd(@window.hwnd, @locators))
42
31
  end
43
32
 
44
- # @see RAutomation::TextField#exists?
45
- def exists?
46
- !!Functions.control_hwnd(@window.hwnd, @locators)
47
- end
48
-
49
33
  end
50
34
  end
51
35
  end
52
- end
36
+ end
@@ -5,6 +5,19 @@ module RAutomation
5
5
  include WaitHelper
6
6
  include Locators
7
7
 
8
+ class << self
9
+ def initialize_com
10
+ @@oleacc_module_handle = Functions.load_library "oleacc.dll"
11
+ Functions.co_initialize nil
12
+ end
13
+ end
14
+
15
+ initialize_com
16
+
17
+ def oleacc_module_handle
18
+ @@oleacc_module_handle
19
+ end
20
+
8
21
  # Locators of the window.
9
22
  attr_reader :locators
10
23
 
@@ -105,7 +118,9 @@ module RAutomation
105
118
  activate
106
119
  active?
107
120
  end
121
+ Functions.send_key(0x12, 0, 0, nil)
108
122
  Functions.send_key(key, 0, 0, nil)
123
+ Functions.send_key(0x12, 0, Constants::KEYEVENTF_KEYUP, nil)
109
124
  Functions.send_key(key, 0, Constants::KEYEVENTF_KEYUP, nil)
110
125
  end
111
126
  end
@@ -133,17 +148,40 @@ module RAutomation
133
148
  Functions.respond_to?(name) ? Functions.send(name, *args) : super
134
149
  end
135
150
 
136
- # Creates the child window object.
137
- # @note This is an WinFfi adapter specific method, not part of the public API
138
- # @example
139
- # RAutomation::Window.new(:title => /Windows Internet Explorer/i).
140
- # child(:title => /some popup/)
141
- # @param (see Window#initialize)
142
- # @return [RAutomation::Window] child window, popup or regular window.
143
- def child(locators)
144
- RAutomation::Window.new Functions.child_window_locators(hwnd, locators)
151
+ # extend public API
152
+ RAutomation::Window.class_eval do
153
+ def select_list(locator)
154
+ wait_until_exists
155
+ RAutomation::Adapter::WinFfi::SelectList.new(@window, locator)
156
+ end
157
+
158
+ def checkbox(locator)
159
+ wait_until_exists
160
+ RAutomation::Adapter::WinFfi::Checkbox.new(@window, locator)
161
+ end
162
+
163
+ def radio(locator)
164
+ wait_until_exists
165
+ RAutomation::Adapter::WinFfi::Radio.new(@window, locator)
166
+ end
167
+
168
+ def table(locator)
169
+ wait_until_exists
170
+ RAutomation::Adapter::WinFfi::Table.new(@window, locator)
171
+ end
172
+
173
+ # Creates the child window object.
174
+ # @note This is an WinFfi adapter specific method, not part of the public API
175
+ # @example
176
+ # RAutomation::Window.new(:title => /Windows Internet Explorer/i).
177
+ # child(:title => /some popup/)
178
+ # @param (see Window#initialize)
179
+ # @return [RAutomation::Window] child window, popup or regular window.
180
+ def child(locators)
181
+ RAutomation::Window.new Functions.child_window_locators(hwnd, locators)
182
+ end
145
183
  end
146
-
184
+
147
185
  end
148
186
  end
149
187
  end
@@ -0,0 +1,44 @@
1
+ module RAutomation
2
+ # @private
3
+ module ElementCollections
4
+ # Creates collection classes and methods for elements.
5
+ # @param [Array<Symbol>] elements for which to create collection classes
6
+ # and methods.
7
+ def has_many(*elements)
8
+ elements.each do |element|
9
+ class_name = element.to_s.split("_").map {|e| e.capitalize}.join
10
+ RAutomation.class_eval %Q{
11
+ class #{class_name}
12
+ include Enumerable
13
+
14
+ def initialize(window, locators)
15
+ @window = window
16
+ @locators = locators
17
+ end
18
+
19
+ def each
20
+ i = -1
21
+ while true
22
+ args = [@window, @locators.merge(:index => i += 1)].compact
23
+ object = RAutomation::#{class_name.chop}.new(*args)
24
+ break unless object.exists?
25
+ yield object
26
+ end
27
+ end
28
+
29
+ def method_missing(name, *args)
30
+ ary = self.to_a
31
+ ary.respond_to?(name) ? ary.send(name, *args) : super
32
+ end
33
+ end
34
+ }
35
+
36
+ class_eval %Q{
37
+ def #{element}(locators = {})
38
+ #{class_name}.new(@window, locators)
39
+ end
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end