uia 0.0.6.1 → 0.0.7

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 (36) hide show
  1. data/ChangeLog +14 -0
  2. data/README.md +127 -46
  3. data/ext/UiaDll/Release/UIA.Helper.dll +0 -0
  4. data/ext/UiaDll/Release/UiaDll.dll +0 -0
  5. data/ext/UiaDll/UiaDll/ElementMethods.cpp +15 -11
  6. data/ext/UiaDll/UiaDll/ElementStructures.h +15 -1
  7. data/ext/UiaDll/UiaDll/PatternInformationStructures.h +77 -1
  8. data/ext/UiaDll/UiaDll/SelectionItemMethods.cpp +1 -0
  9. data/ext/UiaDll/UiaDll/TableItemMethods.cpp +16 -0
  10. data/ext/UiaDll/UiaDll/TableMethods.cpp +16 -0
  11. data/ext/UiaDll/UiaDll/UiaDll.vcxproj +4 -0
  12. data/ext/UiaDll/UiaDll/UiaDll.vcxproj.filters +9 -0
  13. data/ext/UiaDll/UiaDll/WindowMethods.cpp +24 -0
  14. data/ext/UiaDll/UiaDll.Test/ElementInformationTest.cpp +15 -0
  15. data/ext/UiaDll/UiaDll.Test/PatternInformationTest.cpp +14 -1
  16. data/ext/UiaDll/UiaDll.Test/UiaDll.Test.vcxproj +2 -0
  17. data/lib/core_ext/string.rb +5 -0
  18. data/lib/core_ext/symbol.rb +5 -0
  19. data/lib/uia/element.rb +26 -17
  20. data/lib/uia/library/element_structs.rb +95 -0
  21. data/lib/uia/library/pattern_structs.rb +155 -0
  22. data/lib/uia/library.rb +36 -12
  23. data/lib/uia/patterns/expand_collapse.rb +1 -1
  24. data/lib/uia/patterns/table.rb +32 -0
  25. data/lib/uia/patterns/table_item.rb +18 -0
  26. data/lib/uia/patterns/toggle.rb +1 -1
  27. data/lib/uia/patterns/window.rb +38 -0
  28. data/lib/uia/version.rb +1 -1
  29. data/lib/uia.rb +6 -2
  30. data/spec/uia/element_spec.rb +20 -0
  31. data/spec/uia/patterns/table_item_spec.rb +21 -0
  32. data/spec/uia/patterns/table_spec.rb +34 -0
  33. data/spec/uia/patterns/window_spec.rb +31 -0
  34. data/uia.gemspec +1 -0
  35. metadata +36 -5
  36. data/lib/uia/library/structs.rb +0 -174
@@ -0,0 +1,95 @@
1
+ require 'ffi'
2
+
3
+ module Uia
4
+ module Library
5
+ module ElementLayout
6
+ def self.included(base)
7
+ base.class_eval do
8
+ layout :handle, :int,
9
+ :runtime_id, :pointer,
10
+ :number_of_ids, :int,
11
+ :name, :string,
12
+ :class_name, :string,
13
+ :control_type_id, :int,
14
+ :patterns, :pointer,
15
+ :patterns_length, :int,
16
+ :id, :string,
17
+ :is_enabled, :bool
18
+
19
+ def id
20
+ self[:id]
21
+ end
22
+
23
+ def name
24
+ self[:name]
25
+ end
26
+
27
+ def handle
28
+ self[:handle]
29
+ end
30
+
31
+ def runtime_id
32
+ self[:runtime_id].read_array_of_int(number_of_ids)
33
+ end
34
+
35
+ def control_type_id
36
+ self[:control_type_id]
37
+ end
38
+
39
+ def pattern_ids
40
+ self[:patterns].read_array_of_int(self[:patterns_length])
41
+ end
42
+
43
+ def children
44
+ Library.children(self)
45
+ end
46
+
47
+ def descendants
48
+ Library.descendants(self)
49
+ end
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
+
63
+ private
64
+ def number_of_ids
65
+ self[:number_of_ids]
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ class ManagedElementStruct < FFI::ManagedStruct
72
+ include ElementLayout
73
+
74
+ def self.release(pointer)
75
+ Library.release_element(pointer)
76
+ end
77
+ end
78
+
79
+ class ElementStruct < FFI::Struct
80
+ include ElementLayout
81
+ end
82
+
83
+ class Elements < FFI::Struct
84
+ layout :length, :int,
85
+ :items, :pointer
86
+
87
+ def children
88
+ self[:length].times.collect do |i|
89
+ pointer = self[:items] + i * ElementStruct.size
90
+ Uia::Element.new(ElementStruct.new(pointer))
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,155 @@
1
+ module Uia
2
+ module Library
3
+ class ValueInformation < FFI::ManagedStruct
4
+ layout :is_read_only, :bool,
5
+ :value, :string
6
+
7
+ def read_only?
8
+ self[:is_read_only]
9
+ end
10
+
11
+ def value
12
+ self[:value]
13
+ end
14
+
15
+ def self.release(pointer)
16
+ Library.release_value_info(pointer)
17
+ end
18
+ end
19
+
20
+ class ToggleInformation < FFI::ManagedStruct
21
+ layout :state, :string
22
+
23
+ def state
24
+ self[:state]
25
+ end
26
+
27
+ def self.release(pointer)
28
+ Library.release_toggle_info(pointer)
29
+ end
30
+ end
31
+
32
+ class SelectionInformation < FFI::ManagedStruct
33
+ layout :can_multi_select, :bool,
34
+ :is_selection_required, :bool
35
+
36
+ def multi_select?
37
+ self[:can_multi_select]
38
+ end
39
+
40
+ def selection_required?
41
+ self[:is_selection_required]
42
+ end
43
+
44
+ def self.release(pointer)
45
+ Library.release_selection_info(pointer)
46
+ end
47
+ end
48
+
49
+ class SelectionItemInformation < FFI::ManagedStruct
50
+ layout :is_selected, :bool,
51
+ :container, ElementStruct.ptr
52
+
53
+ def selected?
54
+ self[:is_selected]
55
+ end
56
+
57
+ def container
58
+ self[:container] unless self[:container].empty?
59
+ end
60
+
61
+ def self.release(pointer)
62
+ Library.release_selection_item_info(pointer)
63
+ end
64
+ end
65
+
66
+ class ExpandCollapseInformation < FFI::ManagedStruct
67
+ layout :expand_collapse_state, :string
68
+
69
+ def expand_collapse_state
70
+ self[:expand_collapse_state]
71
+ end
72
+
73
+ def self.release(pointer)
74
+ Library.release_expand_collapse_info(pointer)
75
+ end
76
+ end
77
+
78
+ class WindowInformation < FFI::ManagedStruct
79
+ layout :visual_state, :string,
80
+ :interaction_state, :string,
81
+ :can_minimize, :bool,
82
+ :can_maximize, :bool,
83
+ :is_modal, :bool,
84
+ :is_topmost, :bool
85
+
86
+ def visual_state
87
+ self[:visual_state]
88
+ end
89
+
90
+ def can_minimize?
91
+ self[:can_minimize]
92
+ end
93
+
94
+ def can_maximize?
95
+ self[:can_maximize]
96
+ end
97
+
98
+ def modal?
99
+ self[:is_modal]
100
+ end
101
+
102
+ def topmost?
103
+ self[:is_topmost]
104
+ end
105
+
106
+ def interaction_state
107
+ self[:interaction_state]
108
+ end
109
+
110
+ def self.release(pointer)
111
+ Library.release_window_info(pointer)
112
+ end
113
+ end
114
+
115
+ class TableInformation < FFI::ManagedStruct
116
+ layout :row_count, :int,
117
+ :column_count, :int,
118
+ :headers, Elements.ptr
119
+
120
+ def row_count
121
+ self[:row_count]
122
+ end
123
+
124
+ def column_count
125
+ self[:column_count]
126
+ end
127
+
128
+ def headers
129
+ self[:headers].children
130
+ end
131
+
132
+ def self.release(pointer)
133
+ Library.release_table_info(pointer)
134
+ end
135
+ end
136
+
137
+ class TableItemInformation < FFI::ManagedStruct
138
+ layout :column, :int,
139
+ :row, :int
140
+
141
+ def column
142
+ self[:column]
143
+ end
144
+
145
+ def row
146
+ self[:row]
147
+ end
148
+
149
+ def self.release(pointer)
150
+ Library.release_table_item_info(pointer)
151
+ end
152
+ end
153
+
154
+ end
155
+ end
data/lib/uia/library.rb CHANGED
@@ -1,5 +1,6 @@
1
- require 'uia/library/structs'
2
1
  require 'ffi'
2
+ require 'uia/library/element_structs'
3
+ require 'uia/library/pattern_structs'
3
4
 
4
5
  module Uia
5
6
  module Library
@@ -24,36 +25,53 @@ module Uia
24
25
  end
25
26
  end
26
27
 
28
+ def self.elements_from(name_alias, name, arg_types, &block)
29
+ attach_function name, arg_types + [:pointer, :pointer, :int], :int
30
+ define_singleton_method(name_alias) do |*args|
31
+ elements_pointer = FFI::MemoryPointer.new :pointer
32
+ can_throw(name, *(args << elements_pointer)).times.collect do |which_element|
33
+ pointer = elements_pointer.read_pointer + which_element * ManagedElementStruct.size
34
+ Uia::Element.new(ManagedElementStruct.new(pointer))
35
+ end
36
+ end
37
+ end
38
+
27
39
  # returns nil rather than empty FFI::Struct for Uia::Element
28
40
  element_or_nil = lambda { |e| Uia::Element.new(e) unless e.empty? }
29
41
 
30
42
  # cleanup
31
43
  attach_function :release_element, :Element_Release, [:pointer], :void
32
- attach_function :release_elements, :Element_ReleaseMany, [:pointer], :void
44
+ attach_function :release_window_info, :Window_Release, [:pointer], :void
33
45
  attach_function :release_value_info, :Value_Release, [:pointer], :void
34
46
  attach_function :release_toggle_info, :Toggle_Release, [:pointer], :void
35
47
  attach_function :release_selection_info, :Selection_Release, [:pointer], :void
36
48
  attach_function :release_selection_item_info, :SelectionItem_Release, [:pointer], :void
49
+ attach_function :release_table_info, :Table_Release, [:pointer], :void
50
+ attach_function :release_table_item_info, :TableItem_Release, [:pointer], :void
37
51
  attach_function :release_expand_collapse_info, :ExpandCollapse_Release, [:pointer], :void
38
52
 
39
53
  # root methods
40
- attach_throwable_function :root_children, :Root_Children, [], ElementChildrenStruct.by_ref
54
+ elements_from :root_children, :Root_Children, []
41
55
 
42
56
  # finding elements
43
- attach_throwable_function :find_by_id, :Element_FindById, [:string], ElementStruct.by_ref, &element_or_nil
44
- attach_throwable_function :find_by_name, :Element_FindByName, [:string], ElementStruct.by_ref, &element_or_nil
45
- attach_throwable_function :find_by_pid, :Element_FindByProcessId, [:int], ElementStruct.by_ref, &element_or_nil
46
- attach_throwable_function :find_by_handle, :Element_FindByHandle, [:int], ElementStruct.by_ref, &element_or_nil
47
- attach_function :Element_FindByRuntimeId, [:pointer, :int, :pointer, :int], ElementStruct.by_ref
57
+ attach_throwable_function :find_by_id, :Element_FindById, [:string], ManagedElementStruct.by_ref, &element_or_nil
58
+ attach_throwable_function :find_by_name, :Element_FindByName, [:string], ManagedElementStruct.by_ref, &element_or_nil
59
+ attach_throwable_function :find_by_pid, :Element_FindByProcessId, [:int], ManagedElementStruct.by_ref, &element_or_nil
60
+ attach_throwable_function :find_by_handle, :Element_FindByHandle, [:int], ManagedElementStruct.by_ref, &element_or_nil
61
+ attach_function :Element_FindByRuntimeId, [:pointer, :int, :pointer, :int], ManagedElementStruct.by_ref
48
62
 
49
63
  # element methods
50
- attach_throwable_function :find_child_by_id, :Element_FindChildById, [:pointer, :string], ElementStruct.by_ref, &element_or_nil
51
- attach_throwable_function :find_child_by_name, :Element_FindChildByName, [:pointer, :string], ElementStruct.by_ref, &element_or_nil
52
- attach_throwable_function :children, :Element_Children, [:pointer], ElementChildrenStruct.by_ref
53
- attach_throwable_function :descendants, :Element_Descendants, [:pointer], ElementChildrenStruct.by_ref
64
+ attach_throwable_function :find_child_by_id, :Element_FindChildById, [:pointer, :string], ManagedElementStruct.by_ref, &element_or_nil
65
+ attach_throwable_function :find_child_by_name, :Element_FindChildByName, [:pointer, :string], ManagedElementStruct.by_ref, &element_or_nil
66
+ elements_from :children, :Element_Children, [:pointer]
67
+ elements_from :descendants, :Element_Descendants, [:pointer]
54
68
  attach_throwable_function :click, :Element_Click, [:pointer], :void
55
69
  attach_throwable_function :refresh, :Element_Refresh, [:pointer], :void
56
70
 
71
+ # WindowPattern methods
72
+ attach_throwable_function :window_information, :Window_Information, [:pointer], WindowInformation.by_ref
73
+ attach_throwable_function :set_visual_state, :Window_SetVisualState, [:pointer, :string], :void
74
+
57
75
  # ValuePattern methods
58
76
  attach_throwable_function :set_value, :Value_Set, [:pointer, :string], :void
59
77
  attach_throwable_function :value_info, :Value_Information, [:pointer], ValueInformation.by_ref
@@ -79,6 +97,12 @@ module Uia
79
97
  attach_throwable_function :add_to_selection, :SelectionItem_AddToSelection, [:pointer], :void
80
98
  attach_throwable_function :remove_from_selection, :SelectionItem_RemoveFromSelection, [:pointer], :void
81
99
 
100
+ # TablePattern methods
101
+ attach_throwable_function :table_info, :Table_Information, [:pointer], TableInformation.by_ref
102
+
103
+ # TableItemPattern methods
104
+ attach_throwable_function :table_item_info, :TableItem_Information, [:pointer], TableItemInformation.by_ref
105
+
82
106
  def self.find_by_runtime_id(id)
83
107
  p = FFI::MemoryPointer.new :int, id.count
84
108
  p.write_array_of_int(id)
@@ -10,7 +10,7 @@ module Uia
10
10
  end
11
11
 
12
12
  def expand_collapse_state
13
- Library.expand_collapse_info(@element).expand_collapse_state.downcase.to_sym
13
+ Library.expand_collapse_info(@element).expand_collapse_state.to_snake_case_sym
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,32 @@
1
+ module Uia
2
+ module Patterns
3
+ module Table
4
+ module Row
5
+ def items
6
+ select(pattern: :table_item).each { |e| e.as :table_item }
7
+ end
8
+ end
9
+
10
+ def row_count
11
+ table_info.row_count
12
+ end
13
+
14
+ def column_count
15
+ table_info.column_count
16
+ end
17
+
18
+ def headers
19
+ table_info.headers
20
+ end
21
+
22
+ def rows
23
+ select(control_type: :data_item).each { |e| e.extend Row }
24
+ end
25
+
26
+ private
27
+ def table_info
28
+ Library.table_info(@element)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,18 @@
1
+ module Uia
2
+ module Patterns
3
+ module TableItem
4
+ def column
5
+ table_item_info.column
6
+ end
7
+
8
+ def row
9
+ table_item_info.row
10
+ end
11
+
12
+ private
13
+ def table_item_info
14
+ Library.table_item_info(@element)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -2,7 +2,7 @@ module Uia
2
2
  module Patterns
3
3
  module Toggle
4
4
  def toggle_state
5
- Library.toggle_information(@element).state.downcase.to_sym
5
+ Library.toggle_information(@element).state.to_snake_case_sym
6
6
  end
7
7
 
8
8
  def toggle_state=(state)
@@ -0,0 +1,38 @@
1
+ module Uia
2
+ module Patterns
3
+ module Window
4
+ def visual_state
5
+ window_information.visual_state.to_snake_case_sym
6
+ end
7
+
8
+ def can_minimize?
9
+ window_information.can_minimize?
10
+ end
11
+
12
+ def can_maximize?
13
+ window_information.can_maximize?
14
+ end
15
+
16
+ def modal?
17
+ window_information.modal?
18
+ end
19
+
20
+ def topmost?
21
+ window_information.topmost?
22
+ end
23
+
24
+ def visual_state=(state)
25
+ Library.set_visual_state(@element, state.to_camelized_s)
26
+ end
27
+
28
+ def interaction_state
29
+ window_information.interaction_state.to_snake_case_sym
30
+ end
31
+
32
+ private
33
+ def window_information
34
+ Library.window_information(@element)
35
+ end
36
+ end
37
+ end
38
+ end
data/lib/uia/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Uia
2
- VERSION = '0.0.6.1'
2
+ VERSION = '0.0.7'
3
3
  end
data/lib/uia.rb CHANGED
@@ -1,15 +1,19 @@
1
+ require 'require_all'
1
2
  require 'uia/version'
2
3
  require 'uia/library'
3
4
  require 'uia/library/constants'
4
- require 'uia/element'
5
5
  require 'uia/finder'
6
+ require 'uia/element'
7
+
8
+ require_rel 'uia/patterns'
9
+ require_rel 'core_ext'
6
10
 
7
11
  module Uia
8
12
  class BadLocator < StandardError; end
9
13
  extend Finder
10
14
 
11
15
  def self.children
12
- Library.root_children.children
16
+ Library.root_children
13
17
  end
14
18
 
15
19
  def self.find_element(how)
@@ -28,6 +28,11 @@ describe Uia::Element do
28
28
  Given { raw_element.stub(:pattern_ids).and_return([7777]) }
29
29
  Then { element.patterns.should == [:unknown] }
30
30
  end
31
+
32
+ context '#as' do
33
+ When(:cast) { element.as :toggle }
34
+ Then { cast.should have_failed UnsupportedPattern, "Pattern toggle not found in [:window, :transform]" }
35
+ end
31
36
  end
32
37
 
33
38
  context '#refresh' do
@@ -55,6 +60,21 @@ describe Uia::Element do
55
60
  end
56
61
  end
57
62
 
63
+ context '#select' do
64
+ context 'control_type' do
65
+ When(:buttons) { element.select(control_type: :radio_button) }
66
+ Then { buttons.map(&:control_type) == [:radio_button] * 3 }
67
+ end
68
+
69
+ context 'pattern' do
70
+ Then { element.select(pattern: :value).count == 4 }
71
+ end
72
+
73
+ context 'combinations' do
74
+ Then { element.select(control_type: :button, name: 'About')[0].id == 'aboutButton' }
75
+ end
76
+ end
77
+
58
78
  context '#click' do
59
79
  Given(:about) { element.children.find { |c| c.name == 'About' } }
60
80
  Given(:disabled_checkbox) { element.children.find { |c| c.name == 'checkBoxDisabled' } }
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Uia::Patterns::TableItem do
4
+ Given(:data_grid) do
5
+ until (form = Uia.find_element(name: 'DataEntryForm'))
6
+ Uia.find_element(id: /MainForm/).find(name: 'Data Entry Form').as(:invoke).invoke
7
+ end
8
+
9
+ form.find(name: 'Add Many').as(:invoke).invoke
10
+ form.find(id: 'personListView').as(:table)
11
+ end
12
+ Given(:first_row) { data_grid.rows.first }
13
+
14
+ context 'properties' do
15
+ Given(:first_item) { first_row.items.first }
16
+ Given(:last_item) { first_row.items.last }
17
+
18
+ Then { last_item.column == 2 }
19
+ Then { first_item.row == 0 }
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Uia::Patterns::Table do
4
+ Given(:data_grid) do
5
+ until (form = Uia.find_element(name: 'DataEntryForm'))
6
+ Uia.find_element(id: /MainForm/).find(name: 'Data Entry Form').as(:invoke).invoke
7
+ end
8
+
9
+ form.find(name: 'Add Many').as(:invoke).invoke
10
+ form.find(id: 'personListView').as(:table)
11
+ end
12
+
13
+ context 'properties' do
14
+ Then { data_grid.row_count == 52 }
15
+ Then { data_grid.column_count == 3 }
16
+ end
17
+
18
+ context '#headers' do
19
+ Then { data_grid.headers.map(&:control_type) == [:header_item] * 3 }
20
+ Then { data_grid.headers.map(&:name) == ['Name', 'Date of birth', 'State'] }
21
+ end
22
+
23
+ context '#rows' do
24
+ Then { data_grid.rows.count == data_grid.row_count }
25
+
26
+ context 'row' do
27
+ Given(:row) { data_grid.rows[0] }
28
+
29
+ context '#items' do
30
+ Then { row.items.map(&:name) == ['John Doe', '12/15/1967', 'FL'] }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Uia::Patterns::Window do
4
+ Given(:window) { Uia.find_element(id: /MainFormWindow/).as(:window) }
5
+
6
+ context 'properties' do
7
+ Then { window.visual_state == :normal }
8
+ Then { window.interaction_state == :ready_for_user_interaction }
9
+ Then { window.can_minimize? == true }
10
+ Then { window.can_maximize? == true }
11
+ Then { window.modal? == false }
12
+ Then { window.topmost? == false }
13
+ end
14
+
15
+ context '#visual_state=' do
16
+ context 'minimized' do
17
+ When { window.visual_state = :minimized }
18
+ Then { window.visual_state == :minimized }
19
+ end
20
+
21
+ context 'maximized' do
22
+ When { window.visual_state = :maximized }
23
+ Then { window.visual_state == :maximized }
24
+ end
25
+
26
+ context 'normal' do
27
+ When { window.visual_state = :normal }
28
+ Then { window.visual_state == :normal }
29
+ end
30
+ end
31
+ end
data/uia.gemspec CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.require_paths = ['lib']
27
27
 
28
28
  spec.add_runtime_dependency 'ffi'
29
+ spec.add_runtime_dependency 'require_all'
29
30
 
30
31
  spec.add_development_dependency 'bundler', '~> 1.3'
31
32
  spec.add_development_dependency 'rake'