uia 0.0.5.1 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/ChangeLog +17 -2
  2. data/Gemfile +1 -0
  3. data/Rakefile +13 -0
  4. data/ext/UiaDll/UIA.Helper/{AutomationProperty.cs → AutomationPropertyCondition.cs} +1 -1
  5. data/ext/UiaDll/UIA.Helper/Element.cs +46 -6
  6. data/ext/UiaDll/UIA.Helper/Extensions.cs +13 -0
  7. data/ext/UiaDll/UIA.Helper/UIA.Helper.csproj +1 -1
  8. data/ext/UiaDll/UiaDll.Test/ElementInformationTest.cpp +20 -4
  9. data/ext/UiaDll/UiaDll.Test/ElementStub.h +16 -2
  10. data/ext/UiaDll/UiaDll.Test/PatternInformationTest.cpp +35 -0
  11. data/ext/UiaDll/UiaDll.Test/UiaDll.Test.vcxproj +6 -4
  12. data/ext/UiaDll/UiaDll.Test/UiaDll.Test.vcxproj.filters +3 -0
  13. data/ext/UiaDll/UiaDll/ElementMethods.cpp +62 -17
  14. data/ext/UiaDll/UiaDll/ElementStructures.h +19 -9
  15. data/ext/UiaDll/UiaDll/ExpandCollapseMethods.cpp +33 -0
  16. data/ext/UiaDll/UiaDll/InvokePatternMethods.cpp +11 -0
  17. data/ext/UiaDll/UiaDll/PatternInformationStructures.h +90 -0
  18. data/ext/UiaDll/UiaDll/SelectionItemMethods.cpp +40 -0
  19. data/ext/UiaDll/UiaDll/SelectionMethods.cpp +16 -0
  20. data/ext/UiaDll/UiaDll/Stdafx.h +3 -0
  21. data/ext/UiaDll/UiaDll/ToggleMethods.cpp +25 -0
  22. data/ext/UiaDll/UiaDll/UiaDll.vcxproj +9 -0
  23. data/ext/UiaDll/UiaDll/UiaDll.vcxproj.filters +24 -0
  24. data/ext/UiaDll/UiaDll/ValuePatternMethods.cpp +25 -0
  25. data/lib/uia.rb +14 -6
  26. data/lib/uia/element.rb +35 -11
  27. data/lib/uia/finder.rb +33 -0
  28. data/lib/uia/library.rb +48 -6
  29. data/lib/uia/library/element_attributes.rb +11 -0
  30. data/lib/uia/library/structs.rb +93 -5
  31. data/lib/uia/patterns/expand_collapse.rb +17 -0
  32. data/lib/uia/patterns/invoke.rb +9 -0
  33. data/lib/uia/patterns/selection.rb +17 -0
  34. data/lib/uia/patterns/selection_item.rb +25 -0
  35. data/lib/uia/patterns/toggle.rb +17 -0
  36. data/lib/uia/patterns/value.rb +22 -0
  37. data/lib/uia/version.rb +1 -1
  38. data/spec/spec_helper.rb +0 -2
  39. data/spec/uia/element_spec.rb +35 -8
  40. data/spec/uia/patterns/expand_collapse_spec.rb +24 -0
  41. data/spec/uia/patterns/invoke_spec.rb +15 -0
  42. data/spec/uia/patterns/selection_item_spec.rb +55 -0
  43. data/spec/uia/patterns/selection_spec.rb +30 -0
  44. data/spec/uia/patterns/toggle_spec.rb +41 -0
  45. data/spec/uia/patterns/value_spec.rb +16 -0
  46. data/spec/uia_spec.rb +26 -7
  47. metadata +37 -4
data/lib/uia/finder.rb ADDED
@@ -0,0 +1,33 @@
1
+ module Uia
2
+ module Finder
3
+ def find_by_id(id)
4
+ find_by_property(:id, id)
5
+ end
6
+
7
+ def find_by_name(name)
8
+ find_by_property(:name, name)
9
+ end
10
+
11
+ def find_by_pid(pid)
12
+ Library.find_by_pid(pid)
13
+ end
14
+
15
+ def find_by_runtime_id(runtime_id)
16
+ Library.find_by_runtime_id(runtime_id)
17
+ end
18
+
19
+ def find_by_handle(handle)
20
+ Library.find_by_handle handle
21
+ end
22
+
23
+ private
24
+ def find_by_property(property, what)
25
+ case what
26
+ when String
27
+ Library.send("find_by_#{property}", what)
28
+ when Regexp
29
+ children.find { |e| e.send(property) =~ what }
30
+ end
31
+ end
32
+ end
33
+ end
data/lib/uia/library.rb CHANGED
@@ -17,33 +17,75 @@ module Uia
17
17
  attach_function :init, :initialize, [:string], :void
18
18
  init(uia_directory)
19
19
 
20
- def self.attach_throwable_function(name_alias, name, arg_types, return_type)
20
+ def self.attach_throwable_function(name_alias, name, arg_types, return_type, &block)
21
21
  attach_function name, arg_types + [:pointer, :int], return_type
22
22
  define_singleton_method(name_alias) do |*args|
23
- can_throw(name, *args)
23
+ result = can_throw(name, *args)
24
+ return block.call(result) if block
25
+ result
24
26
  end
25
27
  end
26
28
 
29
+ # returns nil rather than empty FFI::Struct for Uia::Element
30
+ element_or_nil = lambda { |e| Uia::Element.new(e) unless e.empty? }
31
+
27
32
  # cleanup
28
33
  attach_function :release_element, :Element_Release, [:pointer], :void
29
34
  attach_function :release_elements, :Element_ReleaseMany, [:pointer], :void
35
+ attach_function :release_value_info, :Value_Release, [:pointer], :void
36
+ attach_function :release_toggle_info, :Toggle_Release, [:pointer], :void
37
+ attach_function :release_selection_info, :Selection_Release, [:pointer], :void
38
+ attach_function :release_selection_item_info, :SelectionItem_Release, [:pointer], :void
39
+ attach_function :release_expand_collapse_info, :ExpandCollapse_Release, [:pointer], :void
40
+
41
+ # root methods
42
+ attach_throwable_function :root_children, :Root_Children, [], ElementChildrenStruct.by_ref
30
43
 
31
44
  # finding elements
32
- attach_throwable_function :find_by_id, :Element_FindById, [:string], ElementStruct.by_ref
33
- attach_throwable_function :find_by_pid, :Element_FindByProcessId, [:int], ElementStruct.by_ref
45
+ attach_throwable_function :find_by_id, :Element_FindById, [:string], ElementStruct.by_ref, &element_or_nil
46
+ attach_throwable_function :find_by_name, :Element_FindByName, [:string], ElementStruct.by_ref, &element_or_nil
47
+ attach_throwable_function :find_by_pid, :Element_FindByProcessId, [:int], ElementStruct.by_ref, &element_or_nil
34
48
  attach_throwable_function :find_by_handle, :Element_FindByHandle, [:int], ElementStruct.by_ref
35
49
  attach_function :Element_FindByRuntimeId, [:pointer, :int, :pointer, :int], ElementStruct.by_ref
36
50
 
37
51
  # element methods
52
+ attach_throwable_function :find_child_by_id, :Element_FindChildById, [:pointer, :string], ElementStruct.by_ref, &element_or_nil
53
+ attach_throwable_function :find_child_by_name, :Element_FindChildByName, [:pointer, :string], ElementStruct.by_ref, &element_or_nil
38
54
  attach_throwable_function :children, :Element_Children, [:pointer], ElementChildrenStruct.by_ref
39
- attach_throwable_function :children_of_type, :Element_ChildrenOfType, [:pointer, PropertyId], ElementChildrenStruct.by_ref
40
55
  attach_throwable_function :descendants, :Element_Descendants, [:pointer], ElementChildrenStruct.by_ref
41
56
  attach_throwable_function :click, :Element_Click, [:pointer], :void
57
+ attach_throwable_function :refresh, :Element_Refresh, [:pointer], :void
58
+
59
+ # ValuePattern methods
60
+ attach_throwable_function :set_value, :Value_Set, [:pointer, :string], :void
61
+ attach_throwable_function :value_info, :Value_Information, [:pointer], ValueInformation.by_ref
62
+
63
+ # ExpandCollapsePattern methods
64
+ attach_throwable_function :expand_collapse_info, :ExpandCollapse_Information, [:pointer], ExpandCollapseInformation.by_ref
65
+ attach_throwable_function :expand, :ExpandCollapse_Expand, [:pointer], :void
66
+ attach_throwable_function :collapse, :ExpandCollapse_Collapse, [:pointer], :void
67
+
68
+ # InvokePattern methods
69
+ attach_throwable_function :invoke, :Invoke, [:pointer], :void
70
+
71
+ # TogglePattern methods
72
+ attach_throwable_function :toggle_information, :Toggle_Information, [:pointer], ToggleInformation.by_ref
73
+ attach_throwable_function :toggle, :Toggle, [:pointer], :void
74
+
75
+ # SelectionPattern methods
76
+ attach_throwable_function :selection_info, :Selection_Information, [:pointer], SelectionInformation.by_ref
77
+
78
+ # SelectionItemPattern methods
79
+ attach_throwable_function :selection_item_info, :SelectionItem_Information, [:pointer], SelectionItemInformation.by_ref
80
+ attach_throwable_function :select, :SelectionItem_Select, [:pointer], :void
81
+ attach_throwable_function :add_to_selection, :SelectionItem_AddToSelection, [:pointer], :void
82
+ attach_throwable_function :remove_from_selection, :SelectionItem_RemoveFromSelection, [:pointer], :void
42
83
 
43
84
  def self.find_by_runtime_id(id)
44
85
  p = FFI::MemoryPointer.new :int, id.count
45
86
  p.write_array_of_int(id)
46
- can_throw(:Element_FindByRuntimeId, p, id.count)
87
+ result = can_throw(:Element_FindByRuntimeId, p, id.count)
88
+ Uia::Element.new(result) unless result.empty?
47
89
  end
48
90
 
49
91
  def self.can_throw(method, *args)
@@ -0,0 +1,11 @@
1
+ module Uia
2
+ module ElementAttributes
3
+ def element_attr(*attrs)
4
+ attrs.each do |attr|
5
+ define_method(attr) do
6
+ @element.send(attr)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -9,10 +9,12 @@ module Uia
9
9
  :runtime_id, :pointer,
10
10
  :number_of_ids, :int,
11
11
  :name, :string,
12
+ :class_name, :string,
12
13
  :control_type_id, :int,
13
14
  :patterns, :pointer,
14
15
  :patterns_length, :int,
15
- :id, :string
16
+ :id, :string,
17
+ :is_enabled, :bool
16
18
 
17
19
  def id
18
20
  self[:id]
@@ -38,15 +40,26 @@ module Uia
38
40
  self[:patterns].read_array_of_int(self[:patterns_length])
39
41
  end
40
42
 
41
- def children(type=nil)
42
- elements = (type && Library.children_of_type(self, type)) || Library.children(self)
43
- elements.children
43
+ def children
44
+ Library.children(self).children
44
45
  end
45
46
 
46
47
  def descendants
47
48
  Library.descendants(self).children
48
49
  end
49
50
 
51
+ def empty?
52
+ to_ptr.address == 0
53
+ end
54
+
55
+ def enabled?
56
+ self[:is_enabled]
57
+ end
58
+
59
+ def class_name
60
+ self[:class_name]
61
+ end
62
+
50
63
  private
51
64
  def number_of_ids
52
65
  self[:number_of_ids]
@@ -72,7 +85,7 @@ module Uia
72
85
  :items, :pointer
73
86
 
74
87
  def children
75
- @children ||= self[:length].times.collect do |i|
88
+ self[:length].times.collect do |i|
76
89
  ElementCast.new(self[:items] + i * ElementCast.size)
77
90
  end
78
91
  end
@@ -81,5 +94,80 @@ module Uia
81
94
  Library.release_elements(pointer)
82
95
  end
83
96
  end
97
+
98
+ class ValueInformation < FFI::ManagedStruct
99
+ layout :is_read_only, :bool,
100
+ :value, :string
101
+
102
+ def read_only?
103
+ self[:is_read_only]
104
+ end
105
+
106
+ def value
107
+ self[:value]
108
+ end
109
+
110
+ def self.release(pointer)
111
+ Library.release_value_info(pointer)
112
+ end
113
+ end
114
+
115
+ class ToggleInformation < FFI::ManagedStruct
116
+ layout :state, :string
117
+
118
+ def state
119
+ self[:state]
120
+ end
121
+
122
+ def self.release(pointer)
123
+ Library.release_toggle_info(pointer)
124
+ end
125
+ end
126
+
127
+ class SelectionInformation < FFI::ManagedStruct
128
+ layout :can_multi_select, :bool,
129
+ :is_selection_required, :bool
130
+
131
+ def multi_select?
132
+ self[:can_multi_select]
133
+ end
134
+
135
+ def selection_required?
136
+ self[:is_selection_required]
137
+ end
138
+
139
+ def self.release(pointer)
140
+ Library.release_selection_info(pointer)
141
+ end
142
+ end
143
+
144
+ class SelectionItemInformation < FFI::ManagedStruct
145
+ layout :is_selected, :bool,
146
+ :container, ElementCast.ptr
147
+
148
+ def selected?
149
+ self[:is_selected]
150
+ end
151
+
152
+ def container
153
+ self[:container] unless self[:container].empty?
154
+ end
155
+
156
+ def self.release(pointer)
157
+ Library.release_selection_item_info(pointer)
158
+ end
159
+ end
160
+
161
+ class ExpandCollapseInformation < FFI::ManagedStruct
162
+ layout :expand_collapse_state, :string
163
+
164
+ def expand_collapse_state
165
+ self[:expand_collapse_state]
166
+ end
167
+
168
+ def self.release(pointer)
169
+ Library.release_expand_collapse_info(pointer)
170
+ end
171
+ end
84
172
  end
85
173
  end
@@ -0,0 +1,17 @@
1
+ module Uia
2
+ module Patterns
3
+ module ExpandCollapse
4
+ def expand
5
+ Library.expand(@element)
6
+ end
7
+
8
+ def collapse
9
+ Library.collapse(@element)
10
+ end
11
+
12
+ def expand_collapse_state
13
+ Library.expand_collapse_info(@element).expand_collapse_state.downcase.to_sym
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Uia
2
+ module Patterns
3
+ module Invoke
4
+ def invoke
5
+ Library.invoke @element
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module Uia
2
+ module Patterns
3
+ module Selection
4
+ def selection_items
5
+ descendants.map {|e| e.as :selection_item }
6
+ end
7
+
8
+ def multi_select?
9
+ Library.selection_info(@element).multi_select?
10
+ end
11
+
12
+ def selection_required?
13
+ Library.selection_info(@element).selection_required?
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ module Uia
2
+ module Patterns
3
+ module SelectionItem
4
+ def select
5
+ Library.select @element
6
+ end
7
+
8
+ def add_to_selection
9
+ Library.add_to_selection @element
10
+ end
11
+
12
+ def remove_from_selection
13
+ Library.remove_from_selection @element
14
+ end
15
+
16
+ def selected?
17
+ Library.selection_item_info(@element).selected?
18
+ end
19
+
20
+ def container
21
+ Uia::Element.new Library.selection_item_info(@element).container
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ module Uia
2
+ module Patterns
3
+ module Toggle
4
+ def toggle_state
5
+ Library.toggle_information(@element).state.downcase.to_sym
6
+ end
7
+
8
+ def toggle_state=(state)
9
+ toggle unless state == toggle_state
10
+ end
11
+
12
+ def toggle
13
+ Library.toggle @element
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Uia
2
+ module Patterns
3
+ module Value
4
+ def value=(value)
5
+ Library.set_value(@element, value)
6
+ end
7
+
8
+ def value
9
+ value_information.value
10
+ end
11
+
12
+ def read_only?
13
+ value_information.read_only?
14
+ end
15
+
16
+ private
17
+ def value_information
18
+ Library.value_info(@element)
19
+ end
20
+ end
21
+ end
22
+ end
data/lib/uia/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Uia
2
- VERSION = '0.0.5.1'
2
+ VERSION = '0.0.6'
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -7,8 +7,6 @@ require 'childprocess'
7
7
  require 'ffi'
8
8
  require 'uia'
9
9
 
10
- include Uia
11
-
12
10
  RSpec.configure do |config|
13
11
  config.before(:all) do
14
12
  @app = ChildProcess.build('spec/app/WindowsForms.exe').start
@@ -1,19 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Element do
4
- Given(:element) { find_element(id: 'MainFormWindow') }
5
- Given(:about_box) { find_element(id: 'AboutBox') }
3
+ describe Uia::Element do
4
+ Given(:element) { Uia.find_element(id: 'MainFormWindow') }
5
+ Given(:about_box) { Uia.find_element(id: 'AboutBox') }
6
6
 
7
7
  context 'properties' do
8
+ let(:raw_element) { element.instance_variable_get(:@element) }
8
9
  Then { element.handle != 0 }
9
10
  Then { element.name == 'MainFormWindow' }
10
11
  Then { element.id == 'MainFormWindow' }
12
+ Then { element.class_name =~ /Forms.*app/i }
13
+ Then { expect(element.find(id: 'textField')).to be_enabled }
11
14
 
12
15
  context '#control_type' do
13
16
  Then { element.control_type == :window }
14
17
 
15
18
  context 'unknown' do
16
- Given { element.stub(:control_type_id).and_return(777) }
19
+ Given { raw_element.stub(:control_type_id).and_return(777) }
17
20
  Then { element.control_type == :unknown }
18
21
  end
19
22
  end
@@ -22,10 +25,34 @@ describe Element do
22
25
  Then { element.patterns.should =~ [:transform, :window] }
23
26
 
24
27
  context 'unknown' do
25
- Given { element.stub(:pattern_ids).and_return([7777]) }
26
- Then { element.patterns.should == [:unknown]}
28
+ Given { raw_element.stub(:pattern_ids).and_return([7777]) }
29
+ Then { element.patterns.should == [:unknown] }
27
30
  end
28
31
  end
32
+
33
+ context '#refresh' do
34
+ Given!(:check_box) do
35
+ check_box = element.find(id: 'checkBox').as :toggle
36
+ check_box.toggle_state = :off
37
+ check_box
38
+ end
39
+
40
+ Given!(:label) { element.find(id: 'checkBoxLabel') }
41
+ When { check_box.toggle_state = :on; label.refresh }
42
+ Then { label.name == 'checkBox is on' }
43
+ end
44
+ end
45
+
46
+ context '#find' do
47
+ context 'id' do
48
+ Then { element.find(id: 'textField') != nil }
49
+ Then { element.find(id: 'does not exist') == nil }
50
+ end
51
+
52
+ context 'name' do
53
+ Then { element.find(name: 'No option selected') != nil }
54
+ Then { element.find(name: 'does not exist') == nil }
55
+ end
29
56
  end
30
57
 
31
58
  context '#click' do
@@ -46,11 +73,11 @@ describe Element do
46
73
 
47
74
  context '#children' do
48
75
  Then { element.children.count == 27 }
49
- Then { element.children.all? { |c| c.instance_of? Element } }
76
+ Then { element.children.all? { |c| c.instance_of? Uia::Element } }
50
77
  end
51
78
 
52
79
  context '#descendants' do
53
80
  Then { element.descendants.count > element.children.count }
54
- Then { element.descendants.all? { |c| c.instance_of? Element } }
81
+ Then { element.descendants.all? { |c| c.instance_of? Uia::Element } }
55
82
  end
56
83
  end