uia 0.0.8 → 0.0.9
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/ChangeLog +9 -0
- data/ext/UiaDll/UIA.Helper/Clicker.cs +14 -1
- data/ext/UiaDll/UIA.Helper/Element.cs +21 -2
- data/ext/UiaDll/UIA.Helper/Extensions.cs +15 -2
- data/ext/UiaDll/UiaDll.Test/ElementInformationTest.cpp +8 -0
- data/ext/UiaDll/UiaDll.Test/ElementStub.h +12 -0
- data/ext/UiaDll/UiaDll/ElementMethods.cpp +9 -0
- data/ext/UiaDll/UiaDll/ElementStructures.h +8 -0
- data/ext/UiaDll/UiaDll/SelectionMethods.cpp +11 -0
- data/lib/uia/element.rb +5 -1
- data/lib/uia/library.rb +2 -0
- data/lib/uia/library/element_structs.rb +8 -2
- data/lib/uia/patterns/selection.rb +4 -0
- data/lib/uia/version.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/uia/element_spec.rb +17 -0
- data/spec/uia/patterns/selection_spec.rb +8 -0
- metadata +4 -4
data/ChangeLog
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
=== Version 0.0.9 / 2014-02-06
|
|
2
|
+
* Enhancements
|
|
3
|
+
* Element#focused? and Element#focus
|
|
4
|
+
* Element#bounding_rectangle
|
|
5
|
+
* Selection#selected_items for multi-select
|
|
6
|
+
* #focus will try multiple times to focus an element before it gives up
|
|
7
|
+
* #click will attempt to use the bounding_rectangle if there is no
|
|
8
|
+
"clickable point"
|
|
9
|
+
|
|
1
10
|
=== Version 0.0.8 / 2013-12-02
|
|
2
11
|
* Enhancements
|
|
3
12
|
* Uia#find_element and Element#find can locate by :title
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
using System;
|
|
2
2
|
using System.Runtime.InteropServices;
|
|
3
|
+
using System.Windows;
|
|
3
4
|
using System.Windows.Automation;
|
|
4
5
|
using System.Windows.Forms;
|
|
5
6
|
|
|
@@ -25,10 +26,22 @@ namespace UIA.Helper
|
|
|
25
26
|
element.ScrollToIfPossible();
|
|
26
27
|
element.TryToFocus();
|
|
27
28
|
|
|
28
|
-
var clickablePoint = element
|
|
29
|
+
var clickablePoint = ClickablePoint(element);
|
|
29
30
|
Cursor.Position = new System.Drawing.Point((int)clickablePoint.X, (int)clickablePoint.Y);
|
|
30
31
|
mouse_event(MOUSEEVENTLF_LEFTDOWN, 0, 0, 0, 0);
|
|
31
32
|
mouse_event(MOUSEEVENTLF_LEFTUP, 0, 0, 0, 0);
|
|
32
33
|
}
|
|
34
|
+
|
|
35
|
+
private static Point ClickablePoint(AutomationElement element)
|
|
36
|
+
{
|
|
37
|
+
try
|
|
38
|
+
{
|
|
39
|
+
return element.GetClickablePoint();
|
|
40
|
+
}
|
|
41
|
+
catch (Exception)
|
|
42
|
+
{
|
|
43
|
+
return element.Current.BoundingRectangle.Center();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
33
46
|
}
|
|
34
47
|
}
|
|
@@ -19,7 +19,7 @@ namespace UIA.Helper
|
|
|
19
19
|
|
|
20
20
|
public TPattern As<TPattern>(AutomationPattern pattern)
|
|
21
21
|
{
|
|
22
|
-
return (TPattern)
|
|
22
|
+
return (TPattern)_element.GetCurrentPattern(pattern);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
public void SendKeys(string keysToSend)
|
|
@@ -28,11 +28,25 @@ namespace UIA.Helper
|
|
|
28
28
|
System.Windows.Forms.SendKeys.SendWait(keysToSend);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
public void SetFocus()
|
|
32
|
+
{
|
|
33
|
+
_element.SetFocus();
|
|
34
|
+
}
|
|
35
|
+
|
|
31
36
|
public virtual int[] RuntimeId
|
|
32
37
|
{
|
|
33
38
|
get { return _element.GetRuntimeId(); }
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
public virtual long[] BoundingRectangle
|
|
42
|
+
{
|
|
43
|
+
get
|
|
44
|
+
{
|
|
45
|
+
var r = _element.Current.BoundingRectangle;
|
|
46
|
+
return new[] { (long)r.Left, (long)r.Top, (long)(r.Right - r.Left), (long)(r.Bottom - r.Top) };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
36
50
|
public virtual string Name
|
|
37
51
|
{
|
|
38
52
|
get { return _element.Current.Name; }
|
|
@@ -58,6 +72,11 @@ namespace UIA.Helper
|
|
|
58
72
|
get { return _element.Current.IsOffscreen == false; }
|
|
59
73
|
}
|
|
60
74
|
|
|
75
|
+
public virtual bool HasKeyboardFocus
|
|
76
|
+
{
|
|
77
|
+
get { return _element.Current.HasKeyboardFocus; }
|
|
78
|
+
}
|
|
79
|
+
|
|
61
80
|
public virtual int NativeWindowHandle
|
|
62
81
|
{
|
|
63
82
|
get { return _element.Current.NativeWindowHandle; }
|
|
@@ -135,7 +154,7 @@ namespace UIA.Helper
|
|
|
135
154
|
|
|
136
155
|
public static Element ByHandle(IntPtr windowHandle)
|
|
137
156
|
{
|
|
138
|
-
|
|
157
|
+
return NullOr(AutomationElement.FromHandle(windowHandle));
|
|
139
158
|
}
|
|
140
159
|
|
|
141
160
|
public static Element ByRuntimeId(int[] runtimeId)
|
|
@@ -13,6 +13,14 @@ namespace UIA.Helper
|
|
|
13
13
|
{
|
|
14
14
|
return string.Join(separator, items.Select(x => x.ToString()));
|
|
15
15
|
}
|
|
16
|
+
|
|
17
|
+
public static Point Center(this Rect rectangle)
|
|
18
|
+
{
|
|
19
|
+
if( rectangle == Rect.Empty )
|
|
20
|
+
throw new Exception("No point could be found");
|
|
21
|
+
|
|
22
|
+
return new Point(rectangle.Left + rectangle.Width / 2, rectangle.Top + rectangle.Height / 2);
|
|
23
|
+
}
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
public static class PropertyExtensions
|
|
@@ -34,8 +42,13 @@ namespace UIA.Helper
|
|
|
34
42
|
{
|
|
35
43
|
try
|
|
36
44
|
{
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
var timesTried = 0;
|
|
46
|
+
while (timesTried++ < 5 && !automationElement.Current.HasKeyboardFocus)
|
|
47
|
+
{
|
|
48
|
+
automationElement.SetFocus();
|
|
49
|
+
Thread.Sleep(100);
|
|
50
|
+
}
|
|
51
|
+
return automationElement.Current.HasKeyboardFocus;
|
|
39
52
|
} catch { return false; }
|
|
40
53
|
}
|
|
41
54
|
|
|
@@ -90,6 +90,14 @@ TEST_F(ElementInformationTest, ItKnowIfItIsEnabled)
|
|
|
90
90
|
ASSERT_EQ(true, ElementInformation(element).isEnabled);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
TEST_F(ElementInformationTest, ItKnowsIfItHasFocus)
|
|
94
|
+
{
|
|
95
|
+
auto element = gcnew ElementStub("");
|
|
96
|
+
element->HasKeyboardFocus = true;
|
|
97
|
+
|
|
98
|
+
ASSERT_EQ(true, ElementInformation(element).hasKeyboardFocus);
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
TEST_F(ElementInformationTest, ItCanBeRefreshed)
|
|
94
102
|
{
|
|
95
103
|
auto elementInformation = ElementInformation(gcnew ElementStub("Initial", 0));
|
|
@@ -40,11 +40,22 @@ public:
|
|
|
40
40
|
void set(bool value) { _isVisible = value; }
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
virtual property bool HasKeyboardFocus
|
|
44
|
+
{
|
|
45
|
+
bool get() override { return _hasKeyboardFocus; }
|
|
46
|
+
void set(bool value) { _hasKeyboardFocus = value; }
|
|
47
|
+
}
|
|
48
|
+
|
|
43
49
|
virtual property array<int>^ RuntimeId
|
|
44
50
|
{
|
|
45
51
|
array<int>^ get() override { return _runtimeIds; }
|
|
46
52
|
}
|
|
47
53
|
|
|
54
|
+
virtual property array<long long>^ BoundingRectangle
|
|
55
|
+
{
|
|
56
|
+
array<long long>^ get() override { return gcnew array<long long> {0, 0, 0, 0}; }
|
|
57
|
+
}
|
|
58
|
+
|
|
48
59
|
virtual property int NativeWindowHandle
|
|
49
60
|
{
|
|
50
61
|
int get() override { return _nativeWindowHandle; }
|
|
@@ -77,4 +88,5 @@ private:
|
|
|
77
88
|
int _controlTypeId;
|
|
78
89
|
bool _isEnabled;
|
|
79
90
|
bool _isVisible;
|
|
91
|
+
bool _hasKeyboardFocus;
|
|
80
92
|
};
|
|
@@ -143,4 +143,13 @@ extern "C" {
|
|
|
143
143
|
StringHelper::CopyToUnmanagedString(error->Message, errorInfo, errorLength);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
+
|
|
147
|
+
__declspec(dllexport) void Element_Focus(ElementInformationPtr element, char* errorInfo, const int errorLength) {
|
|
148
|
+
try {
|
|
149
|
+
Find(element)->SetFocus();
|
|
150
|
+
} catch(Exception^ error) {
|
|
151
|
+
StringHelper::CopyToUnmanagedString(error->Message, errorInfo, errorLength);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
146
155
|
}
|
|
@@ -15,6 +15,8 @@ typedef struct _ElementInformation {
|
|
|
15
15
|
|
|
16
16
|
bool isEnabled;
|
|
17
17
|
bool isVisible;
|
|
18
|
+
bool hasKeyboardFocus;
|
|
19
|
+
long boundingRectangle[4];
|
|
18
20
|
|
|
19
21
|
_ElementInformation() : name(NULL), nativeWindowHandle(0), runtimeId(NULL), patterns(NULL), id(NULL), className(NULL) {}
|
|
20
22
|
|
|
@@ -52,6 +54,12 @@ typedef struct _ElementInformation {
|
|
|
52
54
|
|
|
53
55
|
isEnabled = element->IsEnabled;
|
|
54
56
|
isVisible = element->IsVisible;
|
|
57
|
+
hasKeyboardFocus = element->HasKeyboardFocus;
|
|
58
|
+
|
|
59
|
+
auto r = element->BoundingRectangle;
|
|
60
|
+
for(auto coord = 0; coord < 4; coord++) {
|
|
61
|
+
boundingRectangle[coord] = r[coord];
|
|
62
|
+
}
|
|
55
63
|
}
|
|
56
64
|
|
|
57
65
|
~_ElementInformation() {
|
|
@@ -13,4 +13,15 @@ extern "C" {
|
|
|
13
13
|
return NULL;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
|
|
17
|
+
__declspec(dllexport) int Selection_Selections(ElementInformationPtr element, ElementInformation** selections, char* errorInfo, const int errorInfoLength) {
|
|
18
|
+
try {
|
|
19
|
+
auto selectedElements = Element::From(Find(element)->As<SelectionPattern^>(SelectionPattern::Pattern)->Current.GetSelection());
|
|
20
|
+
*selections = ElementInformation::From(selectedElements);
|
|
21
|
+
return selectedElements->Length;
|
|
22
|
+
} catch(Exception^ e) {
|
|
23
|
+
StringHelper::CopyToUnmanagedString(e->Message, errorInfo, errorInfoLength);
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
16
27
|
}
|
data/lib/uia/element.rb
CHANGED
|
@@ -20,7 +20,7 @@ module Uia
|
|
|
20
20
|
|
|
21
21
|
element_attr :id, :name, :handle, :runtime_id,
|
|
22
22
|
:class_name, :children, :descendants
|
|
23
|
-
refreshed_element_attr :enabled?, :visible
|
|
23
|
+
refreshed_element_attr :enabled?, :visible?, :focused?, :bounding_rectangle
|
|
24
24
|
|
|
25
25
|
def control_type
|
|
26
26
|
Library::Constants::ControlTypes.find(@default) { |_, v| v == @element.control_type_id }.first
|
|
@@ -67,5 +67,9 @@ module Uia
|
|
|
67
67
|
Library.click(@element)
|
|
68
68
|
true
|
|
69
69
|
end
|
|
70
|
+
|
|
71
|
+
def focus
|
|
72
|
+
Library.focus(@element)
|
|
73
|
+
end
|
|
70
74
|
end
|
|
71
75
|
end
|
data/lib/uia/library.rb
CHANGED
|
@@ -71,6 +71,7 @@ module Uia
|
|
|
71
71
|
elements_from :children, :Element_Children, [:pointer]
|
|
72
72
|
elements_from :descendants, :Element_Descendants, [:pointer]
|
|
73
73
|
attach_throwable_function :click, :Element_Click, [:pointer], :void
|
|
74
|
+
attach_throwable_function :focus, :Element_Focus, [:pointer], :void
|
|
74
75
|
attach_throwable_function :refresh, :Element_Refresh, [:pointer], :void
|
|
75
76
|
|
|
76
77
|
# WindowPattern methods
|
|
@@ -96,6 +97,7 @@ module Uia
|
|
|
96
97
|
|
|
97
98
|
# SelectionPattern methods
|
|
98
99
|
attach_throwable_function :selection_info, :Selection_Information, [:pointer], SelectionInformation.by_ref
|
|
100
|
+
elements_from :selected_items, :Selection_Selections, [:pointer]
|
|
99
101
|
|
|
100
102
|
# SelectionItemPattern methods
|
|
101
103
|
attach_throwable_function :selection_item_info, :SelectionItem_Information, [:pointer], SelectionItemInformation.by_ref
|
|
@@ -19,15 +19,21 @@ module Uia
|
|
|
19
19
|
:patterns_length, :int,
|
|
20
20
|
:id, :string,
|
|
21
21
|
:is_enabled, :bool,
|
|
22
|
-
:is_visible, :bool
|
|
22
|
+
:is_visible, :bool,
|
|
23
|
+
:has_focus, :bool,
|
|
24
|
+
:bounding_rectangle, [:long, 4]
|
|
23
25
|
|
|
24
26
|
struct_attr :id, :name, :handle, :control_type_id, :class_name,
|
|
25
|
-
[:enabled?, :is_enabled], [:visible?, :is_visible]
|
|
27
|
+
[:enabled?, :is_enabled], [:visible?, :is_visible], [:focused?, :has_focus]
|
|
26
28
|
|
|
27
29
|
def runtime_id
|
|
28
30
|
self[:runtime_id].read_array_of_int(number_of_ids)
|
|
29
31
|
end
|
|
30
32
|
|
|
33
|
+
def bounding_rectangle
|
|
34
|
+
self[:bounding_rectangle].to_a
|
|
35
|
+
end
|
|
36
|
+
|
|
31
37
|
def pattern_ids
|
|
32
38
|
self[:patterns].read_array_of_int(self[:patterns_length])
|
|
33
39
|
end
|
data/lib/uia/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
data/spec/uia/element_spec.rb
CHANGED
|
@@ -12,12 +12,29 @@ describe Uia::Element do
|
|
|
12
12
|
Then { element.id == 'MainFormWindow' }
|
|
13
13
|
Then { element.class_name =~ /Forms.*app/i }
|
|
14
14
|
Then { expect(element.find(id: 'textField')).to be_enabled }
|
|
15
|
+
Then do
|
|
16
|
+
expect(element.bounding_rectangle.count).to eq(4)
|
|
17
|
+
expect(element.bounding_rectangle).to be_all {|e| e.instance_of? Fixnum }
|
|
18
|
+
end
|
|
15
19
|
|
|
16
20
|
context 'visibility' do
|
|
17
21
|
When { element.as(:window).visual_state = :minimized }
|
|
18
22
|
Then { element.visible? == false }
|
|
19
23
|
end
|
|
20
24
|
|
|
25
|
+
context 'focus' do
|
|
26
|
+
Given(:text_field) { element.find(id: 'textField').as :value }
|
|
27
|
+
|
|
28
|
+
context 'initially' do
|
|
29
|
+
Then { text_field.focused? == false }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context 'explicitly' do
|
|
33
|
+
When { text_field.focus }
|
|
34
|
+
Then { text_field.focused? == true }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
21
38
|
context '#control_type' do
|
|
22
39
|
Then { element.control_type == :window }
|
|
23
40
|
|
|
@@ -27,5 +27,13 @@ describe Uia::Patterns::Selection do
|
|
|
27
27
|
Then { tree_view.selection_items.map(&:name) == ['Parent One', 'Child 1', 'Child 2', 'Parent Two'] }
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
context '#selected_items' do
|
|
32
|
+
When do
|
|
33
|
+
main.find(name: 'Toggle Multi-Select').click
|
|
34
|
+
select_list.selection_items.each &:add_to_selection
|
|
35
|
+
end
|
|
36
|
+
Then { expect(select_list.selected_items.map(&:name)).to eq ['Apple', 'Orange', 'Mango'] }
|
|
37
|
+
end
|
|
30
38
|
end
|
|
31
39
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: uia
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.9
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2014-02-06 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: ffi
|
|
@@ -249,7 +249,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
249
249
|
version: '0'
|
|
250
250
|
segments:
|
|
251
251
|
- 0
|
|
252
|
-
hash:
|
|
252
|
+
hash: -154468511
|
|
253
253
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
254
254
|
none: false
|
|
255
255
|
requirements:
|
|
@@ -258,7 +258,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
258
258
|
version: '0'
|
|
259
259
|
segments:
|
|
260
260
|
- 0
|
|
261
|
-
hash:
|
|
261
|
+
hash: -154468511
|
|
262
262
|
requirements: []
|
|
263
263
|
rubyforge_project:
|
|
264
264
|
rubygems_version: 1.8.24
|