uia 0.0.6.1 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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'