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 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