uia 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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.GetClickablePoint();
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) _element.GetCurrentPattern(pattern);
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
- return NullOr(AutomationElement.FromHandle(windowHandle));
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
- automationElement.SetFocus();
38
- return true;
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
  }
@@ -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
@@ -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
@@ -5,6 +5,10 @@ module Uia
5
5
  filter(pattern: :selection_item).map {|e| e.as :selection_item }
6
6
  end
7
7
 
8
+ def selected_items
9
+ Library.selected_items(@element).map {|e| e.as :selection_item }
10
+ end
11
+
8
12
  def multi_select?
9
13
  Library.selection_info(@element).multi_select?
10
14
  end
@@ -1,3 +1,3 @@
1
1
  module Uia
2
- VERSION = '0.0.8'
2
+ VERSION = '0.0.9'
3
3
  end
@@ -22,7 +22,7 @@ end
22
22
  RSpec.configure do |config|
23
23
  config.before(:all) do
24
24
  @app = ChildProcess.build('spec/app/WindowsForms.exe').start
25
- sleep 0.5
25
+ wait_until { Uia.find_element title: /MainFormWindow/ }
26
26
  end
27
27
 
28
28
  config.after(:all) do
@@ -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.8
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: 2013-12-02 00:00:00.000000000 Z
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: 720468909
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: 720468909
261
+ hash: -154468511
262
262
  requirements: []
263
263
  rubyforge_project:
264
264
  rubygems_version: 1.8.24