rautomation 2.0.1-x86-mingw32
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.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +41 -0
- data/.rspec +2 -0
- data/.yardopts +8 -0
- data/CHANGES.md +276 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +142 -0
- data/Rakefile +73 -0
- data/ext/AutoItX/AutoItX.chm +0 -0
- data/ext/AutoItX/AutoItX3.dll +0 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.cpp +30 -0
- data/ext/IAccessibleDLL/IAccessibleDLL/IAccessibleDLL.vcxproj +179 -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/IAccessibleDLL.sln +31 -0
- data/ext/IAccessibleDLL/IAccessibleDLL.suo +0 -0
- data/ext/IAccessibleDLL/Release/IAccessibleDLL.dll +0 -0
- data/ext/UiaDll/RAutomation.UIA/Controls/Clicker.cs +63 -0
- data/ext/UiaDll/RAutomation.UIA/Controls/SelectList.cs +116 -0
- data/ext/UiaDll/RAutomation.UIA/Controls/Spinner.cs +46 -0
- data/ext/UiaDll/RAutomation.UIA/Controls/TabControl.cs +54 -0
- data/ext/UiaDll/RAutomation.UIA/Controls/TableControl.cs +132 -0
- data/ext/UiaDll/RAutomation.UIA/Controls/TextControl.cs +50 -0
- data/ext/UiaDll/RAutomation.UIA/Expander.cs +59 -0
- data/ext/UiaDll/RAutomation.UIA/Extensions/Element.cs +166 -0
- data/ext/UiaDll/RAutomation.UIA/Extensions/Enumerable.cs +76 -0
- data/ext/UiaDll/RAutomation.UIA/Extensions/Property.cs +27 -0
- data/ext/UiaDll/RAutomation.UIA/Properties/AssemblyInfo.cs +36 -0
- data/ext/UiaDll/RAutomation.UIA/Properties/AutomationProperties.cs +18 -0
- data/ext/UiaDll/RAutomation.UIA/RAutomation.UIA.csproj +77 -0
- data/ext/UiaDll/Release/RAutomation.UIA.dll +0 -0
- data/ext/UiaDll/Release/UiaDll.dll +0 -0
- data/ext/UiaDll/UiaDll/ArrayHelper.cpp +14 -0
- data/ext/UiaDll/UiaDll/ArrayHelper.h +7 -0
- data/ext/UiaDll/UiaDll/AssemblyInfo.cpp +40 -0
- data/ext/UiaDll/UiaDll/ControlMethods.cpp +16 -0
- data/ext/UiaDll/UiaDll/DynamicAssemblyResolver.cpp +24 -0
- data/ext/UiaDll/UiaDll/DynamicAssemblyResolver.h +16 -0
- data/ext/UiaDll/UiaDll/Locator.cpp +93 -0
- data/ext/UiaDll/UiaDll/Locator.h +55 -0
- data/ext/UiaDll/UiaDll/MenuItemSelector.cpp +75 -0
- data/ext/UiaDll/UiaDll/MenuItemSelector.h +24 -0
- data/ext/UiaDll/UiaDll/MenuMethods.cpp +62 -0
- data/ext/UiaDll/UiaDll/ReadMe.txt +38 -0
- data/ext/UiaDll/UiaDll/SelectListMethods.cpp +84 -0
- data/ext/UiaDll/UiaDll/SpinnerMethods.cpp +61 -0
- data/ext/UiaDll/UiaDll/StringHelper.cpp +57 -0
- data/ext/UiaDll/UiaDll/StringHelper.h +15 -0
- data/ext/UiaDll/UiaDll/StringMethods.cpp +8 -0
- data/ext/UiaDll/UiaDll/TabControlMethods.cpp +40 -0
- data/ext/UiaDll/UiaDll/TableMethods.cpp +108 -0
- data/ext/UiaDll/UiaDll/TextMethods.cpp +25 -0
- data/ext/UiaDll/UiaDll/ToggleStateHelper.h +10 -0
- data/ext/UiaDll/UiaDll/UiaDll.cpp +216 -0
- data/ext/UiaDll/UiaDll/UiaDll.h +13 -0
- data/ext/UiaDll/UiaDll/UiaDll.vcxproj +167 -0
- data/ext/UiaDll/UiaDll/UiaDll.vcxproj.filters +110 -0
- data/ext/UiaDll/UiaDll/app.ico +0 -0
- data/ext/UiaDll/UiaDll/app.rc +0 -0
- data/ext/UiaDll/UiaDll/dllmain.cpp +1 -0
- data/ext/UiaDll/UiaDll/resource.h +3 -0
- data/ext/UiaDll/UiaDll/stdafx.cpp +5 -0
- data/ext/UiaDll/UiaDll/stdafx.h +41 -0
- data/ext/UiaDll/UiaDll/targetver.h +8 -0
- data/ext/UiaDll/UiaDll.sln +57 -0
- data/ext/WindowsForms/.nuget/NuGet.Config +6 -0
- data/ext/WindowsForms/.nuget/NuGet.exe +0 -0
- data/ext/WindowsForms/.nuget/NuGet.targets +136 -0
- data/ext/WindowsForms/Release/FizzWare.NBuilder.dll +0 -0
- data/ext/WindowsForms/Release/UIA.Extensions.dll +0 -0
- data/ext/WindowsForms/Release/WindowsForms.exe +0 -0
- data/ext/WindowsForms/WindowsForms/AboutBox.Designer.cs +168 -0
- data/ext/WindowsForms/WindowsForms/AboutBox.cs +103 -0
- data/ext/WindowsForms/WindowsForms/AboutBox.resx +123 -0
- data/ext/WindowsForms/WindowsForms/DataEntryForm.Designer.cs +218 -0
- data/ext/WindowsForms/WindowsForms/DataEntryForm.cs +52 -0
- data/ext/WindowsForms/WindowsForms/DataEntryForm.resx +120 -0
- data/ext/WindowsForms/WindowsForms/DataGridView.Designer.cs +77 -0
- data/ext/WindowsForms/WindowsForms/DataGridView.cs +43 -0
- data/ext/WindowsForms/WindowsForms/DataGridView.resx +120 -0
- data/ext/WindowsForms/WindowsForms/FizzWare.NBuilder.dll +0 -0
- data/ext/WindowsForms/WindowsForms/MainFormWindow.Designer.cs +527 -0
- data/ext/WindowsForms/WindowsForms/MainFormWindow.cs +109 -0
- data/ext/WindowsForms/WindowsForms/MainFormWindow.resx +126 -0
- data/ext/WindowsForms/WindowsForms/PersonForm.Designer.cs +119 -0
- data/ext/WindowsForms/WindowsForms/PersonForm.cs +34 -0
- data/ext/WindowsForms/WindowsForms/PersonForm.resx +120 -0
- data/ext/WindowsForms/WindowsForms/Program.cs +21 -0
- data/ext/WindowsForms/WindowsForms/Properties/AssemblyInfo.cs +36 -0
- data/ext/WindowsForms/WindowsForms/Properties/Resources.Designer.cs +71 -0
- data/ext/WindowsForms/WindowsForms/Properties/Resources.resx +117 -0
- data/ext/WindowsForms/WindowsForms/Properties/Settings.Designer.cs +30 -0
- data/ext/WindowsForms/WindowsForms/Properties/Settings.settings +7 -0
- data/ext/WindowsForms/WindowsForms/SimpleElementsForm.Designer.cs +93 -0
- data/ext/WindowsForms/WindowsForms/SimpleElementsForm.cs +19 -0
- data/ext/WindowsForms/WindowsForms/SimpleElementsForm.resx +120 -0
- data/ext/WindowsForms/WindowsForms/UIA.Extensions.dll +0 -0
- data/ext/WindowsForms/WindowsForms/ValueMonthCalendar.cs +28 -0
- data/ext/WindowsForms/WindowsForms/WindowsForms.csproj +166 -0
- data/ext/WindowsForms/WindowsForms/packages.config +4 -0
- data/ext/WindowsForms/WindowsForms.sln +46 -0
- data/lib/rautomation/adapter/autoit/button.rb +59 -0
- data/lib/rautomation/adapter/autoit/locators.rb +22 -0
- data/lib/rautomation/adapter/autoit/mouse.rb +38 -0
- data/lib/rautomation/adapter/autoit/text_field.rb +70 -0
- data/lib/rautomation/adapter/autoit/window.rb +197 -0
- data/lib/rautomation/adapter/autoit.rb +8 -0
- data/lib/rautomation/adapter/helper.rb +64 -0
- data/lib/rautomation/adapter/ms_uia/button.rb +21 -0
- data/lib/rautomation/adapter/ms_uia/button_helper.rb +19 -0
- data/lib/rautomation/adapter/ms_uia/checkbox.rb +23 -0
- data/lib/rautomation/adapter/ms_uia/constants.rb +111 -0
- data/lib/rautomation/adapter/ms_uia/control.rb +134 -0
- data/lib/rautomation/adapter/ms_uia/functions.rb +279 -0
- data/lib/rautomation/adapter/ms_uia/keys.rb +121 -0
- data/lib/rautomation/adapter/ms_uia/keystroke_converter.rb +122 -0
- data/lib/rautomation/adapter/ms_uia/label.rb +18 -0
- data/lib/rautomation/adapter/ms_uia/list_box.rb +46 -0
- data/lib/rautomation/adapter/ms_uia/list_item.rb +22 -0
- data/lib/rautomation/adapter/ms_uia/locators.rb +23 -0
- data/lib/rautomation/adapter/ms_uia/menu.rb +36 -0
- data/lib/rautomation/adapter/ms_uia/radio.rb +23 -0
- data/lib/rautomation/adapter/ms_uia/select_list.rb +85 -0
- data/lib/rautomation/adapter/ms_uia/spinner.rb +31 -0
- data/lib/rautomation/adapter/ms_uia/tab_control.rb +46 -0
- data/lib/rautomation/adapter/ms_uia/table.rb +140 -0
- data/lib/rautomation/adapter/ms_uia/text_field.rb +39 -0
- data/lib/rautomation/adapter/ms_uia/uia_dll.rb +428 -0
- data/lib/rautomation/adapter/ms_uia/value_control.rb +21 -0
- data/lib/rautomation/adapter/ms_uia/window.rb +289 -0
- data/lib/rautomation/adapter/ms_uia.rb +27 -0
- data/lib/rautomation/adapter/win_32/button.rb +14 -0
- data/lib/rautomation/adapter/win_32/button_helper.rb +24 -0
- data/lib/rautomation/adapter/win_32/checkbox.rb +14 -0
- data/lib/rautomation/adapter/win_32/constants.rb +73 -0
- data/lib/rautomation/adapter/win_32/control.rb +86 -0
- data/lib/rautomation/adapter/win_32/functions.rb +336 -0
- data/lib/rautomation/adapter/win_32/keys.rb +146 -0
- data/lib/rautomation/adapter/win_32/label.rb +10 -0
- data/lib/rautomation/adapter/win_32/list_box.rb +40 -0
- data/lib/rautomation/adapter/win_32/locators.rb +22 -0
- data/lib/rautomation/adapter/win_32/mouse.rb +59 -0
- data/lib/rautomation/adapter/win_32/password_field.rb +15 -0
- data/lib/rautomation/adapter/win_32/radio.rb +11 -0
- data/lib/rautomation/adapter/win_32/select_list.rb +97 -0
- data/lib/rautomation/adapter/win_32/table.rb +25 -0
- data/lib/rautomation/adapter/win_32/text_field.rb +52 -0
- data/lib/rautomation/adapter/win_32/window.rb +277 -0
- data/lib/rautomation/adapter/win_32.rb +23 -0
- data/lib/rautomation/button.rb +55 -0
- data/lib/rautomation/element_collections.rb +55 -0
- data/lib/rautomation/platform.rb +15 -0
- data/lib/rautomation/text_field.rb +60 -0
- data/lib/rautomation/version.rb +3 -0
- data/lib/rautomation/wait_helper.rb +23 -0
- data/lib/rautomation/window.rb +248 -0
- data/lib/rautomation.rb +8 -0
- data/rautomation.gemspec +52 -0
- data/spec/adapter/autoit/mouse_spec.rb +53 -0
- data/spec/adapter/autoit/window_spec.rb +17 -0
- data/spec/adapter/ms_uia/button_spec.rb +39 -0
- data/spec/adapter/ms_uia/checkbox_spec.rb +55 -0
- data/spec/adapter/ms_uia/control_spec.rb +88 -0
- data/spec/adapter/ms_uia/functions_spec.rb +39 -0
- data/spec/adapter/ms_uia/keystroke_converter_spec.rb +48 -0
- data/spec/adapter/ms_uia/label_spec.rb +18 -0
- data/spec/adapter/ms_uia/list_item_spec.rb +14 -0
- data/spec/adapter/ms_uia/listbox_spec.rb +73 -0
- data/spec/adapter/ms_uia/radio_spec.rb +39 -0
- data/spec/adapter/ms_uia/select_list_spec.rb +118 -0
- data/spec/adapter/ms_uia/spinner_spec.rb +44 -0
- data/spec/adapter/ms_uia/tab_control_spec.rb +53 -0
- data/spec/adapter/ms_uia/table_spec.rb +197 -0
- data/spec/adapter/ms_uia/text_field_spec.rb +43 -0
- data/spec/adapter/ms_uia/value_control_spec.rb +11 -0
- data/spec/adapter/ms_uia/window_spec.rb +81 -0
- data/spec/adapter/win_32/button_spec.rb +31 -0
- data/spec/adapter/win_32/checkbox_spec.rb +46 -0
- data/spec/adapter/win_32/label_spec.rb +13 -0
- data/spec/adapter/win_32/listbox_spec.rb +41 -0
- data/spec/adapter/win_32/mouse_spec.rb +49 -0
- data/spec/adapter/win_32/radio_spec.rb +33 -0
- data/spec/adapter/win_32/select_list_spec.rb +65 -0
- data/spec/adapter/win_32/table_spec.rb +27 -0
- data/spec/adapter/win_32/text_field_spec.rb +24 -0
- data/spec/adapter/win_32/window_spec.rb +61 -0
- data/spec/button_spec.rb +67 -0
- data/spec/buttons_spec.rb +21 -0
- data/spec/spec_helper.rb +128 -0
- data/spec/text_field_spec.rb +64 -0
- data/spec/text_fields_spec.rb +22 -0
- data/spec/window_spec.rb +150 -0
- data/spec/windows_spec.rb +76 -0
- metadata +369 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
module RAutomation
|
2
|
+
module Adapter
|
3
|
+
module Win32
|
4
|
+
class SelectList < Control
|
5
|
+
include WaitHelper
|
6
|
+
include Locators
|
7
|
+
|
8
|
+
# Default locators used for searching buttons.
|
9
|
+
DEFAULT_LOCATORS = {:class => /combobox/i}
|
10
|
+
|
11
|
+
class SelectListOption
|
12
|
+
attr_accessor :text, :index
|
13
|
+
|
14
|
+
def initialize(select_list, text, index)
|
15
|
+
@select_list = select_list
|
16
|
+
@text = text
|
17
|
+
@index = index
|
18
|
+
end
|
19
|
+
|
20
|
+
def selected?
|
21
|
+
selected_idx = Functions.send_message(@select_list.hwnd, Constants::CB_GETCURSEL, 0, nil)
|
22
|
+
return false if selected_idx == Constants::CB_ERR
|
23
|
+
@text == Functions.retrieve_combobox_item_text(@select_list.hwnd, selected_idx)
|
24
|
+
end
|
25
|
+
|
26
|
+
def select
|
27
|
+
@select_list.send :assert_enabled
|
28
|
+
Functions.send_message(@select_list.hwnd, Constants::CB_SETCURSEL, @index, nil) != Constants::CB_ERR
|
29
|
+
end
|
30
|
+
|
31
|
+
alias_method :set, :select
|
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(self, item, item_no)) if options[:text] == item
|
42
|
+
else
|
43
|
+
items.push(SelectListOption.new(self, 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
|
+
def option(options)
|
56
|
+
item_count.times do |item_no|
|
57
|
+
item = Functions.retrieve_combobox_item_text(hwnd, item_no)
|
58
|
+
return SelectListOption.new(self, item, item_no) if options[:text] == item
|
59
|
+
end
|
60
|
+
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def select(index)
|
65
|
+
Functions.send_message(hwnd, Constants::CB_SETCURSEL, index, nil) != Constants::CB_ERR
|
66
|
+
end
|
67
|
+
|
68
|
+
def set(text)
|
69
|
+
option(:text => text).set
|
70
|
+
end
|
71
|
+
|
72
|
+
def list_item_height
|
73
|
+
Functions.send_message(hwnd, Constants::CB_GETITEMHEIGHT, 0 ,nil)
|
74
|
+
end
|
75
|
+
|
76
|
+
def dropbox_boundary
|
77
|
+
boundary = FFI::MemoryPointer.new :long, 4
|
78
|
+
|
79
|
+
Functions.send_message(hwnd, Constants::CB_GETDROPPEDCONTROLRECT, 0 ,boundary)
|
80
|
+
|
81
|
+
boundary.read_array_of_long(4)
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_top_index
|
85
|
+
Functions.send_message(hwnd, Constants::CB_GETTOPINDEX, 0 ,nil)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def item_count
|
91
|
+
Functions.send_message(hwnd, Constants::CB_GETCOUNT, 0, nil)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RAutomation
|
2
|
+
module Adapter
|
3
|
+
module Win32
|
4
|
+
class Table < Control
|
5
|
+
include WaitHelper
|
6
|
+
include Locators
|
7
|
+
|
8
|
+
# Default locators used for searching buttons.
|
9
|
+
DEFAULT_LOCATORS = {:class => /syslistview/i}
|
10
|
+
|
11
|
+
def select(row)
|
12
|
+
Functions.select_table_row(Window.oleacc_module_handle, Functions.control_hwnd(@window.hwnd, @locators), row)
|
13
|
+
end
|
14
|
+
|
15
|
+
def selected?(row)
|
16
|
+
handle = Window.oleacc_module_handle
|
17
|
+
control_hwnd = Functions.control_hwnd(@window.hwnd, @locators)
|
18
|
+
|
19
|
+
state = Functions.get_table_row_state(handle, control_hwnd, row)
|
20
|
+
state & Constants::STATE_SYSTEM_SELECTED != 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module RAutomation
|
2
|
+
module Adapter
|
3
|
+
module Win32
|
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
|
+
assert_enabled
|
14
|
+
|
15
|
+
wait_until do
|
16
|
+
hwnd = Functions.control_hwnd(@window.hwnd, @locators)
|
17
|
+
@window.activate
|
18
|
+
@window.active? &&
|
19
|
+
focus &&
|
20
|
+
Functions.set_control_text(hwnd, text) &&
|
21
|
+
set?(text)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @see RAutomation::TextField#clear
|
26
|
+
def clear
|
27
|
+
assert_enabled
|
28
|
+
set ""
|
29
|
+
end
|
30
|
+
|
31
|
+
# @see RAutomation::Window#send_keys
|
32
|
+
def send_keys(*keys)
|
33
|
+
@window.container.wait_until_present
|
34
|
+
assert_enabled
|
35
|
+
@window.activate
|
36
|
+
focus
|
37
|
+
@window.send_keys(keys)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def set?(text)
|
43
|
+
value == text
|
44
|
+
end
|
45
|
+
|
46
|
+
def assert_enabled
|
47
|
+
raise "Cannot set value on a disabled text field" if disabled?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
module RAutomation
|
2
|
+
module Adapter
|
3
|
+
module Win32
|
4
|
+
class Window
|
5
|
+
include WaitHelper
|
6
|
+
include Locators
|
7
|
+
extend ElementCollections
|
8
|
+
|
9
|
+
has_many :controls
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def oleacc_module_handle
|
13
|
+
@oleacc_module_handle ||= begin
|
14
|
+
oleacc = Functions.load_library("oleacc.dll")
|
15
|
+
Functions.co_initialize nil
|
16
|
+
oleacc
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :container
|
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, Integer] :hwnd Window handle in decimal format
|
35
|
+
# @option locators [String, Integer] :pid Window process ID (PID)
|
36
|
+
# @option locators [String, Integer] :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(container, locators)
|
40
|
+
@container = container
|
41
|
+
extract(locators)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Retrieves handle of the window.
|
45
|
+
# @note Searches only for visible windows.
|
46
|
+
# @see RAutomation::Window#hwnd
|
47
|
+
def hwnd
|
48
|
+
@hwnd ||= Functions.window_hwnd(@locators)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @see RAutomation::Window#pid
|
52
|
+
def pid
|
53
|
+
Functions.window_pid(hwnd)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @see RAutomation::Window#title
|
57
|
+
def title
|
58
|
+
Functions.window_title(hwnd)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @see RAutomation::Window#class_names
|
62
|
+
def class_names
|
63
|
+
classes = []
|
64
|
+
controls.each do |control|
|
65
|
+
classes << control.class_name
|
66
|
+
end
|
67
|
+
classes.sort
|
68
|
+
end
|
69
|
+
|
70
|
+
# @see RAutomation::Window#activate
|
71
|
+
def activate
|
72
|
+
return if !exists? || active?
|
73
|
+
restore if minimized?
|
74
|
+
Functions.activate_window(hwnd)
|
75
|
+
sleep 0.1
|
76
|
+
end
|
77
|
+
|
78
|
+
# @see RAutomation::Window#active?
|
79
|
+
def active?
|
80
|
+
exists? && Functions.foreground_window == hwnd
|
81
|
+
end
|
82
|
+
|
83
|
+
# @see RAutomation::Window#text
|
84
|
+
def text
|
85
|
+
Functions.window_text(hwnd)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @see RAutomation::Window#exists?
|
89
|
+
def exists?
|
90
|
+
result = hwnd && Functions.window_exists(hwnd)
|
91
|
+
!!result
|
92
|
+
end
|
93
|
+
|
94
|
+
# @see RAutomation::Window#visible?
|
95
|
+
def visible?
|
96
|
+
Functions.window_visible(hwnd)
|
97
|
+
end
|
98
|
+
|
99
|
+
# @see RAutomation::Window#maximize
|
100
|
+
def maximize
|
101
|
+
Functions.show_window(hwnd, Constants::SW_MAXIMIZE)
|
102
|
+
sleep 1
|
103
|
+
end
|
104
|
+
|
105
|
+
# @see RAutomation::Window#minimize
|
106
|
+
def minimize
|
107
|
+
Functions.show_window(hwnd, Constants::SW_MINIMIZE)
|
108
|
+
sleep 1
|
109
|
+
end
|
110
|
+
|
111
|
+
# @see RAutomation::Window#minimized?
|
112
|
+
def minimized?
|
113
|
+
Functions.minimized(hwnd)
|
114
|
+
end
|
115
|
+
|
116
|
+
# @see RAutomation::Window#restore
|
117
|
+
def restore
|
118
|
+
Functions.show_window(hwnd, Constants::SW_RESTORE)
|
119
|
+
sleep 1
|
120
|
+
end
|
121
|
+
|
122
|
+
# Moves/resizes the window.
|
123
|
+
# @note All coordinates are optional and if not specified current coordinates will be used
|
124
|
+
# @param [Hash] coords for specifying the coordinates.
|
125
|
+
# @option coords [Integer] :left Window coordinate from the left side of screen
|
126
|
+
# @option coords [Integer] :top Window coordinate from the top of the screen
|
127
|
+
# @option coords [Integer] :width Width of window
|
128
|
+
# @option coords [Integer] :height Height of window
|
129
|
+
def move(coords={})
|
130
|
+
@container.wait_until_present
|
131
|
+
rect = dimensions.merge(coords)
|
132
|
+
Functions.move_window(hwnd, rect[:left], rect[:top], rect[:width], rect[:height])
|
133
|
+
sleep 1
|
134
|
+
end
|
135
|
+
|
136
|
+
# @return [Hash] Hash[:left, :top, :width, :height] with window coordinates
|
137
|
+
def dimensions
|
138
|
+
@container.wait_until_present
|
139
|
+
left, top, right, bottom = Functions.window_rect(hwnd)
|
140
|
+
{:left => left, :top => top, :width => right - left, :height => bottom - top}
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
# Activates the window and sends keys to it.
|
145
|
+
#
|
146
|
+
# @example
|
147
|
+
# RAutomation::Window.new(:title => //).send_keys "hello!"
|
148
|
+
# RAutomation::Window.new(:title => //).send_keys [:control, "a"], "world!"
|
149
|
+
# Refer to {Keys::KEYS} for all the special keycodes.
|
150
|
+
# @see RAutomation::Window#send_keys
|
151
|
+
def send_keys(args)
|
152
|
+
Keys.encode(args).each do |arg|
|
153
|
+
wait_until do
|
154
|
+
activate
|
155
|
+
active?
|
156
|
+
end
|
157
|
+
|
158
|
+
if arg.is_a?(Array)
|
159
|
+
arg.reduce([]) do |pressed_keys, k|
|
160
|
+
if k == Keys[:null]
|
161
|
+
pressed_keys.each {|pressed_key| release_key pressed_key}
|
162
|
+
pressed_keys = []
|
163
|
+
else
|
164
|
+
pressed_keys << press_key(k)
|
165
|
+
end
|
166
|
+
pressed_keys
|
167
|
+
end
|
168
|
+
else
|
169
|
+
send_key arg
|
170
|
+
end
|
171
|
+
end
|
172
|
+
sleep 1
|
173
|
+
end
|
174
|
+
|
175
|
+
# @see RAutomation::Window#close
|
176
|
+
def close
|
177
|
+
Functions.close_window(hwnd)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Retrieves the {Mouse} object for automating mouse movements and clicks
|
181
|
+
# @raise [UnknownWindowException] if the window doesn't exist.
|
182
|
+
def mouse
|
183
|
+
@container.wait_until_present
|
184
|
+
Mouse.new(self)
|
185
|
+
end
|
186
|
+
|
187
|
+
# @see Button#initialize
|
188
|
+
# @see RAutomation::Window#button
|
189
|
+
def button(locator)
|
190
|
+
Button.new(self, locator)
|
191
|
+
end
|
192
|
+
|
193
|
+
# @see TextField#initialize
|
194
|
+
# @see RAutomation::Window#text_field
|
195
|
+
def text_field(locator)
|
196
|
+
TextField.new(self, locator)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Redirects all method calls not part of the public API to the {Functions} directly.
|
200
|
+
# @see RAutomation::Window#method_missing
|
201
|
+
def method_missing(name, *args)
|
202
|
+
Functions.respond_to?(name) ? Functions.send(name, *args) : super
|
203
|
+
end
|
204
|
+
|
205
|
+
# Win32 adapter specific API methods
|
206
|
+
def label(locator={})
|
207
|
+
@container.wait_until_present
|
208
|
+
Label.new(self, locator)
|
209
|
+
end
|
210
|
+
|
211
|
+
def password_field(locator)
|
212
|
+
@container.wait_until_present
|
213
|
+
PasswordField.new(self, locator)
|
214
|
+
end
|
215
|
+
|
216
|
+
def control(locator={})
|
217
|
+
@container.wait_until_present
|
218
|
+
Control.new(self, locator)
|
219
|
+
end
|
220
|
+
|
221
|
+
def list_box(locator={})
|
222
|
+
@container.wait_until_present
|
223
|
+
ListBox.new(self, locator)
|
224
|
+
end
|
225
|
+
|
226
|
+
def select_list(locator={})
|
227
|
+
@container.wait_until_present
|
228
|
+
SelectList.new(self, locator)
|
229
|
+
end
|
230
|
+
|
231
|
+
def checkbox(locator={})
|
232
|
+
@container.wait_until_present
|
233
|
+
Checkbox.new(self, locator)
|
234
|
+
end
|
235
|
+
|
236
|
+
def radio(locator={})
|
237
|
+
@container.wait_until_present
|
238
|
+
Radio.new(self, locator)
|
239
|
+
end
|
240
|
+
|
241
|
+
def table(locator={})
|
242
|
+
@container.wait_until_present
|
243
|
+
Table.new(self, locator)
|
244
|
+
end
|
245
|
+
|
246
|
+
# Creates the child window object.
|
247
|
+
# @note This is an Win32 adapter specific method, not part of the public API
|
248
|
+
# @example
|
249
|
+
# RAutomation::Window.new(:title => /Windows Internet Explorer/i).
|
250
|
+
# child(:title => /some popup/)
|
251
|
+
# @param (see Window#initialize)
|
252
|
+
# @return [RAutomation::Window] child window, popup or regular window.
|
253
|
+
def child(locators)
|
254
|
+
RAutomation::Window.new Functions.child_window_locators(hwnd, locators)
|
255
|
+
end
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
def press_key key
|
260
|
+
Functions.send_key(key, 0, 0, nil)
|
261
|
+
key
|
262
|
+
end
|
263
|
+
|
264
|
+
def release_key key
|
265
|
+
Functions.send_key(key, 0, Constants::KEYEVENTF_KEYUP, nil)
|
266
|
+
key
|
267
|
+
end
|
268
|
+
|
269
|
+
def send_key key
|
270
|
+
press_key key
|
271
|
+
release_key key
|
272
|
+
key
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
gem "ffi"
|
3
|
+
rescue Gem::LoadError
|
4
|
+
raise Gem::LoadError, "Unable to load FFI gem. Install it with:\n\tgem install ffi"
|
5
|
+
end
|
6
|
+
require "ffi"
|
7
|
+
require File.dirname(__FILE__) + "/win_32/constants"
|
8
|
+
require File.dirname(__FILE__) + "/win_32/keys"
|
9
|
+
require File.dirname(__FILE__) + "/win_32/functions"
|
10
|
+
require File.dirname(__FILE__) + "/win_32/locators"
|
11
|
+
require File.dirname(__FILE__) + "/win_32/window"
|
12
|
+
require File.dirname(__FILE__) + "/win_32/button_helper"
|
13
|
+
require File.dirname(__FILE__) + "/win_32/control"
|
14
|
+
require File.dirname(__FILE__) + "/win_32/button"
|
15
|
+
require File.dirname(__FILE__) + "/win_32/checkbox"
|
16
|
+
require File.dirname(__FILE__) + "/win_32/radio"
|
17
|
+
require File.dirname(__FILE__) + "/win_32/text_field"
|
18
|
+
require File.dirname(__FILE__) + "/win_32/select_list"
|
19
|
+
require File.dirname(__FILE__) + "/win_32/table"
|
20
|
+
require File.dirname(__FILE__) + "/win_32/label"
|
21
|
+
require File.dirname(__FILE__) + "/win_32/list_box"
|
22
|
+
require File.dirname(__FILE__) + "/win_32/mouse"
|
23
|
+
require File.dirname(__FILE__) + "/win_32/password_field"
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RAutomation
|
2
|
+
class Button
|
3
|
+
# @private
|
4
|
+
# This constructor is meant to be accessed only through {Window#button} method.
|
5
|
+
def initialize(window, locators)
|
6
|
+
@window = window
|
7
|
+
@locators = locators
|
8
|
+
@button = @window.button(@locators)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Performs a click on the button.
|
12
|
+
# By default click is considered successful if the button doesn't exist after clicking (e.g. window has closed)
|
13
|
+
# @yield [button] optional block specifying successful clicking condition.
|
14
|
+
# @yieldparam [Button] button which is being clicked on.
|
15
|
+
# @yieldreturn [Boolean] true if clicking on the button is successful, false otherwise.
|
16
|
+
# @raise [UnknownButtonException] if the button doesn't exist.
|
17
|
+
def click
|
18
|
+
wait_until_exists
|
19
|
+
if block_given?
|
20
|
+
@button.click {yield self}
|
21
|
+
else
|
22
|
+
@button.click
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Retrieves the value (text) of the button, usually the visible text.
|
27
|
+
# @return [String] the value (text) of the button.
|
28
|
+
# @raise [UnknownButtonException] if the button doesn't exist.
|
29
|
+
def value
|
30
|
+
wait_until_exists
|
31
|
+
@button.value
|
32
|
+
end
|
33
|
+
|
34
|
+
# Checks if the button exists.
|
35
|
+
# @return [Boolean] true if button exists, false otherwise.
|
36
|
+
def exists?
|
37
|
+
@button.exists?
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :exist?, :exists?
|
41
|
+
|
42
|
+
# Allows to execute specific {Adapter} methods not part of the public API.
|
43
|
+
def method_missing(name, *args)
|
44
|
+
@button.send(name, *args)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def wait_until_exists
|
50
|
+
WaitHelper.wait_until {exists?}
|
51
|
+
rescue WaitHelper::TimeoutError
|
52
|
+
raise UnknownButtonException, "Button #{@locators.inspect} doesn't exist on window #{@window.locators.inspect}!"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RAutomation
|
2
|
+
# @private
|
3
|
+
module ElementCollections
|
4
|
+
class UnsupportedLocatorException < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
# Creates collection classes and methods for elements.
|
8
|
+
# @param [Array<Symbol>] elements for which to create collection classes
|
9
|
+
# and methods.
|
10
|
+
def has_many(*elements)
|
11
|
+
elements.each do |element|
|
12
|
+
class_name_plural = element.to_s.split("_").map {|e| e.capitalize}.join
|
13
|
+
class_name = class_name_plural.chop
|
14
|
+
adapter_class = self.to_s.scan(/(.*)::/).flatten.first
|
15
|
+
clazz = RAutomation.constants.include?(class_name) ? RAutomation : class_eval(adapter_class)
|
16
|
+
clazz.class_eval %Q{
|
17
|
+
class #{class_name_plural}
|
18
|
+
include Enumerable
|
19
|
+
|
20
|
+
def initialize(window, locators)
|
21
|
+
if locators[:hwnd] || locators[:pid]
|
22
|
+
raise UnsupportedLocatorException,
|
23
|
+
":hwnd or :pid in " + locators.inspect + " are not supported for #{adapter_class}::#{class_name_plural}"
|
24
|
+
end
|
25
|
+
|
26
|
+
@window = window
|
27
|
+
@locators = locators
|
28
|
+
end
|
29
|
+
|
30
|
+
def each
|
31
|
+
i = -1
|
32
|
+
while true
|
33
|
+
args = [@window, @locators.merge(:index => i += 1)].compact
|
34
|
+
object = #{clazz}::#{class_name}.new(*args)
|
35
|
+
break unless object.exists?
|
36
|
+
yield object
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(name, *args)
|
41
|
+
ary = self.to_a
|
42
|
+
ary.respond_to?(name) ? ary.send(name, *args) : super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
}
|
46
|
+
|
47
|
+
class_eval %Q{
|
48
|
+
def #{element}(locators = {})
|
49
|
+
#{adapter_class}::#{class_name_plural}.new(@window || self, locators)
|
50
|
+
end
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module RAutomation
|
2
|
+
class TextField
|
3
|
+
# @private
|
4
|
+
# This constructor is meant to be accessed only through {Window#text_field} method.
|
5
|
+
def initialize(window, locators) #:nodoc:
|
6
|
+
@window = window
|
7
|
+
@locators = locators
|
8
|
+
@text_field = @window.text_field(@locators)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sets text of the text field.
|
12
|
+
# @param [String] text of the field to set.
|
13
|
+
# @raise [UnknownTextFieldException] if the text field doesn't exist.
|
14
|
+
def set(text)
|
15
|
+
wait_until_exists
|
16
|
+
@text_field.set(text)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Clears text field's text.
|
20
|
+
# @raise [UnknownTextFieldException] if the text field doesn't exist.
|
21
|
+
def clear
|
22
|
+
wait_until_exists
|
23
|
+
@text_field.clear
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns text field's current value (text).
|
27
|
+
# @return [String] the value (text) of the text field.
|
28
|
+
# @raise [UnknownTextFieldException] if the text field doesn't exist.
|
29
|
+
def value
|
30
|
+
wait_until_exists
|
31
|
+
@text_field.value
|
32
|
+
end
|
33
|
+
|
34
|
+
# Checks if the text field exists.
|
35
|
+
# @return [Boolean] true if text field exists, false otherwise.
|
36
|
+
def exists?
|
37
|
+
@text_field.exists?
|
38
|
+
end
|
39
|
+
|
40
|
+
def hwnd
|
41
|
+
wait_until_exists
|
42
|
+
@text_field.hwnd
|
43
|
+
end
|
44
|
+
|
45
|
+
alias_method :exist?, :exists?
|
46
|
+
|
47
|
+
# Allows to execute specific {Adapter} methods not part of the public API.
|
48
|
+
def method_missing(name, *args)
|
49
|
+
@text_field.send(name, *args)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def wait_until_exists
|
55
|
+
WaitHelper.wait_until {exists?}
|
56
|
+
rescue WaitHelper::TimeoutError
|
57
|
+
raise UnknownTextFieldException, "Text field #{@locators.inspect} doesn't exist on window #{@window.locators.inspect}!" unless exists?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RAutomation
|
2
|
+
# Waiting with timeout
|
3
|
+
module WaitHelper
|
4
|
+
extend self
|
5
|
+
|
6
|
+
class TimeoutError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
# @private
|
10
|
+
# Wait until the block evaluates to true or times out.
|
11
|
+
def wait_until(timeout = Window.wait_timeout, &block)
|
12
|
+
end_time = ::Time.now + timeout
|
13
|
+
|
14
|
+
until ::Time.now > end_time
|
15
|
+
result = yield(self)
|
16
|
+
return result if result
|
17
|
+
sleep 0.5
|
18
|
+
end
|
19
|
+
|
20
|
+
raise TimeoutError, "timed out after #{timeout} seconds"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|