rdp-rautomation 0.6.3.1
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/.document +5 -0
- data/.rspec +2 -0
- data/.yardopts +6 -0
- data/History.rdoc +103 -0
- data/LICENSE +20 -0
- data/README.rdoc +114 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/ext/AutoItX/AutoItX.chm +0 -0
- data/ext/AutoItX/AutoItX3.dll +0 -0
- data/ext/IAccessibleDLL/IAccessibleDLL.sln +20 -0
- data/ext/IAccessibleDLL/IAccessibleDLL.suo +0 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.cpp +30 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.vcxproj +102 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.vcxproj.filters +42 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.vcxproj.user +3 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/ReadMe.txt +48 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/dllmain.cpp +19 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/stdafx.cpp +8 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/stdafx.h +22 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/table_support.cpp +282 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/targetver.h +8 -0
- data/ext/IAccessibleDLL/Release/IAccessibleDLL.dll +0 -0
- data/ext/ListViewExplorer/ListViewExplorer.sln +20 -0
- data/ext/ListViewExplorer/ListViewExplorer.suo +0 -0
- data/ext/ListViewExplorer/ListViewExplorer/ListViewExplorer.cpp +174 -0
- data/ext/ListViewExplorer/ListViewExplorer/ListViewExplorer.vcxproj +95 -0
- data/ext/ListViewExplorer/ListViewExplorer/ListViewExplorer.vcxproj.filters +42 -0
- data/ext/ListViewExplorer/ListViewExplorer/ListViewExplorer.vcxproj.user +3 -0
- data/ext/ListViewExplorer/ListViewExplorer/ReadMe.txt +40 -0
- data/ext/ListViewExplorer/ListViewExplorer/stdafx.cpp +8 -0
- data/ext/ListViewExplorer/ListViewExplorer/stdafx.h +17 -0
- data/ext/ListViewExplorer/ListViewExplorer/table_support.cpp +250 -0
- data/ext/ListViewExplorer/ListViewExplorer/table_support.h +2 -0
- data/ext/ListViewExplorer/ListViewExplorer/targetver.h +8 -0
- data/ext/UiaDll/Release/UiaDll.dll +0 -0
- data/ext/UiaDll/UiaDll.sln +20 -0
- data/ext/UiaDll/UiaDll.suo +0 -0
- data/ext/UiaDll/UiaDll/ReadMe.txt +48 -0
- data/ext/UiaDll/UiaDll/UiaDll.cpp +205 -0
- data/ext/UiaDll/UiaDll/UiaDll.vcxproj +104 -0
- data/ext/UiaDll/UiaDll/UiaDll.vcxproj.filters +42 -0
- data/ext/UiaDll/UiaDll/dllmain.cpp +39 -0
- data/ext/UiaDll/UiaDll/globals.h +3 -0
- data/ext/UiaDll/UiaDll/stdafx.cpp +8 -0
- data/ext/UiaDll/UiaDll/stdafx.h +19 -0
- data/ext/UiaDll/UiaDll/targetver.h +8 -0
- data/ext/WindowsForms/bin/WindowsForms.exe +0 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms.sln +20 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms.suo +0 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/AboutBox.Designer.cs +80 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/AboutBox.cs +103 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/AboutBox.resx +120 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/DataEntryForm.Designer.cs +187 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/DataEntryForm.cs +46 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/DataEntryForm.resx +120 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/MainFormWindow.Designer.cs +377 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/MainFormWindow.cs +78 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/MainFormWindow.resx +120 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/PersonForm.Designer.cs +119 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/PersonForm.cs +34 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/PersonForm.resx +120 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/Program.cs +21 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/AssemblyInfo.cs +36 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Resources.Designer.cs +71 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Resources.resx +117 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Settings.Designer.cs +30 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/Properties/Settings.settings +7 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/SimpleElementsForm.Designer.cs +93 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/SimpleElementsForm.cs +19 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/SimpleElementsForm.resx +120 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/WindowsForms.csproj +123 -0
- data/ext/WindowsForms/src/WindowsForms/WindowsForms/bin/Release/WindowsForms.exe +0 -0
- data/lib/rautomation.rb +6 -0
- data/lib/rautomation/adapter/autoit.rb +5 -0
- data/lib/rautomation/adapter/autoit/button.rb +59 -0
- data/lib/rautomation/adapter/autoit/locators.rb +22 -0
- data/lib/rautomation/adapter/autoit/text_field.rb +61 -0
- data/lib/rautomation/adapter/autoit/window.rb +184 -0
- data/lib/rautomation/adapter/helper.rb +20 -0
- data/lib/rautomation/adapter/win_ffi.rb +21 -0
- data/lib/rautomation/adapter/win_ffi/button.rb +25 -0
- data/lib/rautomation/adapter/win_ffi/button_helper.rb +24 -0
- data/lib/rautomation/adapter/win_ffi/checkbox.rb +19 -0
- data/lib/rautomation/adapter/win_ffi/constants.rb +94 -0
- data/lib/rautomation/adapter/win_ffi/control.rb +79 -0
- data/lib/rautomation/adapter/win_ffi/functions.rb +333 -0
- data/lib/rautomation/adapter/win_ffi/keystroke_converter.rb +67 -0
- data/lib/rautomation/adapter/win_ffi/label.rb +21 -0
- data/lib/rautomation/adapter/win_ffi/list_box.rb +60 -0
- data/lib/rautomation/adapter/win_ffi/locators.rb +22 -0
- data/lib/rautomation/adapter/win_ffi/ms_uia/uia_dll.rb +36 -0
- data/lib/rautomation/adapter/win_ffi/radio.rb +19 -0
- data/lib/rautomation/adapter/win_ffi/select_list.rb +87 -0
- data/lib/rautomation/adapter/win_ffi/table.rb +57 -0
- data/lib/rautomation/adapter/win_ffi/text_field.rb +52 -0
- data/lib/rautomation/adapter/win_ffi/window.rb +226 -0
- data/lib/rautomation/button.rb +55 -0
- data/lib/rautomation/element_collections.rb +47 -0
- data/lib/rautomation/text_field.rb +60 -0
- data/lib/rautomation/wait_helper.rb +23 -0
- data/lib/rautomation/window.rb +234 -0
- data/spec/adapter/win_ffi/button_spec.rb +41 -0
- data/spec/adapter/win_ffi/checkbox_spec.rb +48 -0
- data/spec/adapter/win_ffi/keystroke_converter_spec.rb +47 -0
- data/spec/adapter/win_ffi/label_spec.rb +21 -0
- data/spec/adapter/win_ffi/listbox_spec.rb +52 -0
- data/spec/adapter/win_ffi/radio_spec.rb +37 -0
- data/spec/adapter/win_ffi/select_list_spec.rb +66 -0
- data/spec/adapter/win_ffi/table_spec.rb +39 -0
- data/spec/adapter/win_ffi/text_field_spec.rb +23 -0
- data/spec/adapter/win_ffi/window_spec.rb +43 -0
- data/spec/button_spec.rb +68 -0
- data/spec/buttons_spec.rb +21 -0
- data/spec/spec_helper.rb +96 -0
- data/spec/text_field_spec.rb +65 -0
- data/spec/text_fields_spec.rb +22 -0
- data/spec/window_spec.rb +122 -0
- data/spec/windows_spec.rb +55 -0
- metadata +207 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module RAutomation
|
|
2
|
+
module Adapter
|
|
3
|
+
module WinFfi
|
|
4
|
+
class KeystrokeConverter
|
|
5
|
+
class << self
|
|
6
|
+
def convert(str)
|
|
7
|
+
special_characters = ""
|
|
8
|
+
|
|
9
|
+
str.split(/([{}])/).inject([]) do |converted_keys, str|
|
|
10
|
+
if str == "}"
|
|
11
|
+
converted_keys << convert_special_characters(special_characters << str)
|
|
12
|
+
special_characters = ""
|
|
13
|
+
elsif str == "{" || !special_characters.empty?
|
|
14
|
+
special_characters << str
|
|
15
|
+
else
|
|
16
|
+
converted_keys += convert_characters(str)
|
|
17
|
+
end
|
|
18
|
+
converted_keys
|
|
19
|
+
end.flatten
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def convert_special_characters chars
|
|
25
|
+
case chars.downcase
|
|
26
|
+
when "{tab}"
|
|
27
|
+
Constants::VK_TAB
|
|
28
|
+
when "{backspace}"
|
|
29
|
+
Constants::VK_BACK
|
|
30
|
+
when "{enter}"
|
|
31
|
+
Constants::VK_RETURN
|
|
32
|
+
when "{left}"
|
|
33
|
+
Constants::VK_LEFT
|
|
34
|
+
when "{right}"
|
|
35
|
+
Constants::VK_RIGHT
|
|
36
|
+
when "{down}"
|
|
37
|
+
Constants::VK_DOWN
|
|
38
|
+
when "{up}"
|
|
39
|
+
Constants::VK_UP
|
|
40
|
+
else
|
|
41
|
+
# unsupported special tag, ignore the tag itself, but convert the
|
|
42
|
+
# characters inside the tag
|
|
43
|
+
convert_characters(chars.gsub(/[{}]/, ""))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def convert_characters(element)
|
|
50
|
+
element.split(//).inject([]) do |chars, char|
|
|
51
|
+
char_code = char.upcase.unpack("c")[0]
|
|
52
|
+
if char =~ /[A-Z]/
|
|
53
|
+
chars += in_upcase(char_code)
|
|
54
|
+
else
|
|
55
|
+
chars << char_code
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def in_upcase(char_code)
|
|
61
|
+
[Constants::VK_LSHIFT, char_code]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module RAutomation
|
|
2
|
+
module Adapter
|
|
3
|
+
module WinFfi
|
|
4
|
+
class Label < Control
|
|
5
|
+
include WaitHelper
|
|
6
|
+
include Locators
|
|
7
|
+
|
|
8
|
+
def value
|
|
9
|
+
Functions.control_value(Functions.control_hwnd(@window.hwnd, @locators))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def exist?
|
|
13
|
+
@locators[:id].nil? ? super : super && matches_type(Constants::UIA_LABEL_CONTROL_TYPE)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
alias_method :exists?, :exist?
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module RAutomation
|
|
2
|
+
module Adapter
|
|
3
|
+
module WinFfi
|
|
4
|
+
|
|
5
|
+
class ListBox < Control
|
|
6
|
+
include WaitHelper
|
|
7
|
+
include Locators
|
|
8
|
+
|
|
9
|
+
def count
|
|
10
|
+
UiaDll::find_children(uia_control(@locators[:id]), nil)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def items
|
|
14
|
+
list_items = []
|
|
15
|
+
children = FFI::MemoryPointer.new :pointer, self.count
|
|
16
|
+
length = UiaDll::find_children(uia_control(@locators[:id]), children)
|
|
17
|
+
children.read_array_of_pointer(length).each do |child|
|
|
18
|
+
child_name = FFI::MemoryPointer.new :char, UiaDll::get_name(child, nil) + 1
|
|
19
|
+
UiaDll::get_name(child, child_name)
|
|
20
|
+
list_items.push child_name.read_string
|
|
21
|
+
end
|
|
22
|
+
list_items
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def exist?
|
|
26
|
+
@locators[:id].nil? ? super : super && matches_type(Constants::UIA_LIST_CONTROL_TYPE)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
alias_method :exists?, :exist?
|
|
30
|
+
|
|
31
|
+
def selected?(index)
|
|
32
|
+
children = FFI::MemoryPointer.new :pointer, self.count
|
|
33
|
+
length = UiaDll::find_children(uia_control(@locators[:id]), children)
|
|
34
|
+
target_element = children.read_array_of_pointer(length)[index]
|
|
35
|
+
is_selected = FFI::MemoryPointer.new :int, 1
|
|
36
|
+
|
|
37
|
+
if UiaDll::get_is_selected(target_element, is_selected) == 1
|
|
38
|
+
return is_selected.read_int == 1
|
|
39
|
+
else
|
|
40
|
+
return false
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def select(index)
|
|
45
|
+
children = FFI::MemoryPointer.new :pointer, self.count
|
|
46
|
+
|
|
47
|
+
length = UiaDll::find_children(uia_control(@locators[:id]), children)
|
|
48
|
+
target_element = children.read_array_of_pointer(length)[index]
|
|
49
|
+
|
|
50
|
+
UiaDll::select(target_element)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
alias_method :strings, :items
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module RAutomation
|
|
2
|
+
module Adapter
|
|
3
|
+
module WinFfi
|
|
4
|
+
# @private
|
|
5
|
+
module Locators
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def extract(locators)
|
|
10
|
+
# windows locators
|
|
11
|
+
@hwnd = locators[:hwnd].to_i if locators[:hwnd]
|
|
12
|
+
locators[:pid] = locators[:pid].to_i if locators[:pid]
|
|
13
|
+
locators[:index] = locators[:index].to_i if locators[:index]
|
|
14
|
+
|
|
15
|
+
# control locator
|
|
16
|
+
locators = self.class::DEFAULT_LOCATORS.merge(locators) if self.class.const_defined?(:DEFAULT_LOCATORS)
|
|
17
|
+
@locators = locators
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module RAutomation
|
|
2
|
+
module Adapter
|
|
3
|
+
module WinFfi
|
|
4
|
+
# @private
|
|
5
|
+
module UiaDll
|
|
6
|
+
extend FFI::Library
|
|
7
|
+
|
|
8
|
+
ffi_lib File.dirname(__FILE__) + '/../../../../../ext/UiaDll/Release/UiaDll.dll'
|
|
9
|
+
ffi_convention :stdcall
|
|
10
|
+
|
|
11
|
+
attach_function :find_window, :RA_FindWindow,
|
|
12
|
+
[:string], :pointer
|
|
13
|
+
attach_function :is_offscreen, :RA_IsOffscreen,
|
|
14
|
+
[:pointer], :bool
|
|
15
|
+
attach_function :element_from_handle, :RA_ElementFromHandle,
|
|
16
|
+
[:long], :pointer
|
|
17
|
+
attach_function :find_child_by_id, :RA_FindChildById,
|
|
18
|
+
[:pointer, :string], :pointer
|
|
19
|
+
attach_function :current_native_window_handle, :RA_CurrentNativeWindowHandle,
|
|
20
|
+
[:pointer], :long
|
|
21
|
+
attach_function :set_focus, :RA_SetFocus,
|
|
22
|
+
[:pointer], :bool
|
|
23
|
+
attach_function :current_control_type, :RA_GetCurrentControlType,
|
|
24
|
+
[:pointer], :int
|
|
25
|
+
attach_function :find_children, :RA_FindChildren,
|
|
26
|
+
[:pointer, :pointer], :int
|
|
27
|
+
attach_function :get_name, :RA_GetName,
|
|
28
|
+
[:pointer, :pointer], :int
|
|
29
|
+
attach_function :get_is_selected, :RA_GetIsSelected,
|
|
30
|
+
[:pointer, :pointer], :int
|
|
31
|
+
attach_function :select, :RA_Select,
|
|
32
|
+
[:pointer], :int
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module RAutomation
|
|
2
|
+
module Adapter
|
|
3
|
+
module WinFfi
|
|
4
|
+
class Radio < Control
|
|
5
|
+
include WaitHelper
|
|
6
|
+
include Locators
|
|
7
|
+
include ButtonHelper
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def exist?
|
|
11
|
+
@locators[:id].nil? ? super : super && matches_type(Constants::UIA_RADIO_BUTTON_CONTROL_TYPE)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
alias_method :exists?, :exist?
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
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(select_list, text, index)
|
|
13
|
+
@select_list = select_list
|
|
14
|
+
@text = text
|
|
15
|
+
@index = index
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def selected?
|
|
19
|
+
selected_idx = Functions.send_message(@select_list.control_hwnd, Constants::CB_GETCURSEL, 0, nil)
|
|
20
|
+
return false if selected_idx == Constants::CB_ERR
|
|
21
|
+
@text == Functions.retrieve_combobox_item_text(@select_list.control_hwnd, selected_idx)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def select
|
|
25
|
+
@select_list.assert_enabled
|
|
26
|
+
Functions.send_message(@select_list.control_hwnd, Constants::CB_SETCURSEL, @index, nil) != Constants::CB_ERR
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
alias_method :set, :select
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def initialize(window, locators)
|
|
33
|
+
super
|
|
34
|
+
@hwnd = Functions.control_hwnd(@window.hwnd, @locators)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def options(options = {})
|
|
38
|
+
items = []
|
|
39
|
+
|
|
40
|
+
item_count.times do |item_no|
|
|
41
|
+
item = Functions.retrieve_combobox_item_text(@hwnd, item_no)
|
|
42
|
+
|
|
43
|
+
if options[:text]
|
|
44
|
+
items.push(SelectListOption.new(self, item, item_no)) if options[:text] == item
|
|
45
|
+
else
|
|
46
|
+
items.push(SelectListOption.new(self, item, item_no))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
items
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def value
|
|
54
|
+
selected_option = options.find {|option| option.selected?}
|
|
55
|
+
selected_option ? selected_option.text : ""
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def option(options)
|
|
59
|
+
item_count.times do |item_no|
|
|
60
|
+
item = Functions.retrieve_combobox_item_text(@hwnd, item_no)
|
|
61
|
+
return SelectListOption.new(self, item, item_no) if options[:text] == item
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def control_hwnd
|
|
68
|
+
@hwnd
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def exist?
|
|
72
|
+
@locators[:id].nil? ? super : super && matches_type(Constants::UIA_COMBOBOX_CONTROL_TYPE)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
alias_method :exists?, :exist?
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def item_count
|
|
80
|
+
Functions.send_message(@hwnd, Constants::CB_GETCOUNT, 0, nil)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module RAutomation
|
|
2
|
+
module Adapter
|
|
3
|
+
module WinFfi
|
|
4
|
+
class Table < Control
|
|
5
|
+
include WaitHelper
|
|
6
|
+
include Locators
|
|
7
|
+
|
|
8
|
+
def strings
|
|
9
|
+
rows = []
|
|
10
|
+
header_columns = []
|
|
11
|
+
|
|
12
|
+
raise "Not a list control" unless UiaDll::current_control_type(uia_control(@locators[:id])) == Constants::UIA_LIST_CONTROL_TYPE
|
|
13
|
+
|
|
14
|
+
children_count = count_children(uia_control(@locators[:id]))
|
|
15
|
+
children = FFI::MemoryPointer.new :pointer, children_count
|
|
16
|
+
UiaDll::find_children(uia_control(@locators[:id]), children)
|
|
17
|
+
children.read_array_of_pointer(children_count).each do |child|
|
|
18
|
+
grandchildren_count = count_children(child)
|
|
19
|
+
grandchildren = FFI::MemoryPointer.new :pointer, grandchildren_count
|
|
20
|
+
UiaDll::find_children(child, grandchildren)
|
|
21
|
+
grandchildren.read_array_of_pointer(grandchildren_count).each do |grandchild|
|
|
22
|
+
grandchild_name = FFI::MemoryPointer.new :char, UiaDll::get_name(grandchild, nil) + 1
|
|
23
|
+
UiaDll::get_name(grandchild, grandchild_name)
|
|
24
|
+
header_columns.push grandchild_name.read_string
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
rows.push header_columns
|
|
28
|
+
header_columns = []
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
rows
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def select(row)
|
|
35
|
+
Functions.select_table_row(Window.oleacc_module_handle, Functions.control_hwnd(@window.hwnd, @locators), row)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def selected?(row)
|
|
39
|
+
state = Functions.get_table_row_state(Window.oleacc_module_handle, Functions.control_hwnd(@window.hwnd, @locators), row)
|
|
40
|
+
state & Constants::STATE_SYSTEM_SELECTED != 0
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def row_count
|
|
44
|
+
UiaDll::find_children(uia_control(@locators[:id]), nil)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def count_children(element)
|
|
50
|
+
UiaDll::find_children(element, nil)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module RAutomation
|
|
2
|
+
module Adapter
|
|
3
|
+
module WinFfi
|
|
4
|
+
class TextField < Control
|
|
5
|
+
include WaitHelper
|
|
6
|
+
include Locators
|
|
7
|
+
|
|
8
|
+
# Default locators used for searching text fields.
|
|
9
|
+
DEFAULT_LOCATORS = {:class => /edit/i}
|
|
10
|
+
|
|
11
|
+
# @see RAutomation::TextField#set
|
|
12
|
+
def set(text)
|
|
13
|
+
raise "Cannot set value on a disabled text field" if disabled?
|
|
14
|
+
|
|
15
|
+
wait_until do
|
|
16
|
+
hwnd = Functions.control_hwnd(@window.hwnd, @locators)
|
|
17
|
+
@window.activate
|
|
18
|
+
@window.active? &&
|
|
19
|
+
Functions.set_control_focus(hwnd) &&
|
|
20
|
+
Functions.set_control_text(hwnd, text) &&
|
|
21
|
+
value == text
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @see RAutomation::TextField#clear
|
|
26
|
+
def clear
|
|
27
|
+
raise "Cannot set value on a disabled text field" if disabled?
|
|
28
|
+
set ""
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @see RAutomation::TextField#value
|
|
32
|
+
def value
|
|
33
|
+
Functions.control_value(hwnd)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @see RAutomation::TextField#hwnd
|
|
37
|
+
def hwnd
|
|
38
|
+
Functions.control_hwnd(@window.hwnd, @locators)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def exist?
|
|
42
|
+
@locators[:id].nil? ? super : super && matches_type(Constants::UIA_EDIT_CONTROL_TYPE)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
alias_method :exists?, :exist?
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
module RAutomation
|
|
2
|
+
module Adapter
|
|
3
|
+
module WinFfi
|
|
4
|
+
autoload :UiaDll, File.dirname(__FILE__) + "/ms_uia/uia_dll"
|
|
5
|
+
|
|
6
|
+
class Window
|
|
7
|
+
include WaitHelper
|
|
8
|
+
include Locators
|
|
9
|
+
extend ElementCollections
|
|
10
|
+
|
|
11
|
+
has_many :controls
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def oleacc_module_handle
|
|
15
|
+
@oleacc_module_handle ||= begin
|
|
16
|
+
oleacc = Functions.load_library "oleacc.dll"
|
|
17
|
+
Functions.co_initialize nil
|
|
18
|
+
oleacc
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Locators of the window.
|
|
24
|
+
attr_reader :locators
|
|
25
|
+
|
|
26
|
+
# Possible locators are :title, :text, :hwnd, :pid, :class and :index.
|
|
27
|
+
|
|
28
|
+
# Creates the window object.
|
|
29
|
+
# @note this method is not meant to be accessed directly, but only through {RAutomation::Window#initialize}!
|
|
30
|
+
# @param [Hash] locators for searching the window.
|
|
31
|
+
# @option locators [String, Regexp] :title Title of the window
|
|
32
|
+
# @option locators [String, Regexp] :text Visible text of the window
|
|
33
|
+
# @option locators [String, Regexp] :class Internal class name of the window
|
|
34
|
+
# @option locators [String, Fixnum] :hwnd Window handle in decimal format
|
|
35
|
+
# @option locators [String, Fixnum] :pid Window process ID (PID)
|
|
36
|
+
# @option locators [String, Fixnum] :index 0-based index to specify n-th window if all other criteria match
|
|
37
|
+
# all other criteria match
|
|
38
|
+
# @see RAutomation::Window#initialize
|
|
39
|
+
def initialize(locators)
|
|
40
|
+
extract(locators)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Retrieves handle of the window.
|
|
44
|
+
# @note Searches only for visible windows.
|
|
45
|
+
# @see RAutomation::Window#hwnd
|
|
46
|
+
def hwnd
|
|
47
|
+
@hwnd ||= Functions.window_hwnd(@locators)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @see RAutomation::Window#pid
|
|
51
|
+
def pid
|
|
52
|
+
Functions.window_pid(hwnd)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @see RAutomation::Window#title
|
|
56
|
+
def title
|
|
57
|
+
Functions.window_title(hwnd)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @see RAutomation::Window#activate
|
|
61
|
+
def activate
|
|
62
|
+
return if !exists? || active?
|
|
63
|
+
restore if minimized?
|
|
64
|
+
Functions.activate_window(hwnd)
|
|
65
|
+
sleep 1
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @see RAutomation::Window#active?
|
|
69
|
+
def active?
|
|
70
|
+
exists? && Functions.foreground_window == hwnd
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @see RAutomation::Window#text
|
|
74
|
+
def text
|
|
75
|
+
Functions.window_text(hwnd)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @see RAutomation::Window#exists?
|
|
79
|
+
def exists?
|
|
80
|
+
result = hwnd && Functions.window_exists(hwnd)
|
|
81
|
+
!!result
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# @see RAutomation::Window#visible?
|
|
85
|
+
def visible?
|
|
86
|
+
Functions.window_visible(hwnd)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @see RAutomation::Window#maximize
|
|
90
|
+
def maximize
|
|
91
|
+
Functions.show_window(hwnd, Constants::SW_MAXIMIZE)
|
|
92
|
+
sleep 1
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @see RAutomation::Window#minimize
|
|
96
|
+
def minimize
|
|
97
|
+
Functions.show_window(hwnd, Constants::SW_MINIMIZE)
|
|
98
|
+
sleep 1
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# @see RAutomation::Window#minimized?
|
|
102
|
+
def minimized?
|
|
103
|
+
Functions.minimized(hwnd)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @see RAutomation::Window#restore
|
|
107
|
+
def restore
|
|
108
|
+
Functions.show_window(hwnd, Constants::SW_RESTORE)
|
|
109
|
+
sleep 1
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Activates the window and sends keys to it.
|
|
113
|
+
#
|
|
114
|
+
# Refer to KeystrokeConverter#convert_special_characters for the special keycodes.
|
|
115
|
+
# @see RAutomation::Window#send_keys
|
|
116
|
+
def send_keys(keys)
|
|
117
|
+
shift_pressed = false
|
|
118
|
+
KeystrokeConverter.convert(keys).each do |key|
|
|
119
|
+
wait_until do
|
|
120
|
+
activate
|
|
121
|
+
active?
|
|
122
|
+
end
|
|
123
|
+
press_key key
|
|
124
|
+
|
|
125
|
+
if key == Constants::VK_LSHIFT
|
|
126
|
+
shift_pressed = true
|
|
127
|
+
next
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
release_key key
|
|
131
|
+
|
|
132
|
+
if shift_pressed
|
|
133
|
+
shift_pressed = false
|
|
134
|
+
release_key Constants::VK_LSHIFT
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @see RAutomation::Window#close
|
|
140
|
+
def close
|
|
141
|
+
Functions.close_window(hwnd)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# @see Button#initialize
|
|
145
|
+
# @see RAutomation::Window#button
|
|
146
|
+
def button(locator)
|
|
147
|
+
Button.new(self, locator)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# @see TextField#initialize
|
|
151
|
+
# @see RAutomation::Window#text_field
|
|
152
|
+
def text_field(locator)
|
|
153
|
+
TextField.new(self, locator)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def label(locator)
|
|
157
|
+
Label.new(self, locator)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def control(locator)
|
|
161
|
+
Control.new(self, locator)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def controls(locator)
|
|
165
|
+
Controls.new(self, locator)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def list_box(locator)
|
|
169
|
+
ListBox.new(self, locator)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Redirects all method calls not part of the public API to the {Functions} directly.
|
|
173
|
+
# @see RAutomation::Window#method_missing
|
|
174
|
+
def method_missing(name, *args)
|
|
175
|
+
Functions.respond_to?(name) ? Functions.send(name, *args) : super
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# extend public API
|
|
179
|
+
RAutomation::Window.class_eval do
|
|
180
|
+
def select_list(locator)
|
|
181
|
+
wait_until_exists
|
|
182
|
+
RAutomation::Adapter::WinFfi::SelectList.new(@window, locator)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def checkbox(locator)
|
|
186
|
+
wait_until_exists
|
|
187
|
+
RAutomation::Adapter::WinFfi::Checkbox.new(@window, locator)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def radio(locator)
|
|
191
|
+
wait_until_exists
|
|
192
|
+
RAutomation::Adapter::WinFfi::Radio.new(@window, locator)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def table(locator)
|
|
196
|
+
wait_until_exists
|
|
197
|
+
RAutomation::Adapter::WinFfi::Table.new(@window, locator)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Creates the child window object.
|
|
201
|
+
# @note This is an WinFfi adapter specific method, not part of the public API
|
|
202
|
+
# @example
|
|
203
|
+
# RAutomation::Window.new(:title => /Windows Internet Explorer/i).
|
|
204
|
+
# child(:title => /some popup/)
|
|
205
|
+
# @param (see Window#initialize)
|
|
206
|
+
# @return [RAutomation::Window] child window, popup or regular window.
|
|
207
|
+
def child(locators)
|
|
208
|
+
RAutomation::Window.new Functions.child_window_locators(@window.hwnd, locators)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
private
|
|
213
|
+
|
|
214
|
+
def press_key key
|
|
215
|
+
Functions.send_key(key, 0, 0, nil)
|
|
216
|
+
sleep 0.01
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def release_key key
|
|
220
|
+
Functions.send_key(key, 0, Constants::KEYEVENTF_KEYUP, nil)
|
|
221
|
+
sleep 0.1
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|