uia 0.0.5.1 → 0.0.6

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