bewildr 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -1
- data/lib/bewildr.rb +4 -0
- data/lib/bewildr/application.rb +38 -4
- data/lib/bewildr/control_patterns/toggle_pattern.rb +6 -2
- data/lib/bewildr/control_type_additions/combo_box_additions.rb +1 -1
- data/lib/bewildr/control_type_additions/list_additions.rb +2 -1
- data/lib/bewildr/control_type_additions/scroll_additions.rb +1 -1
- data/lib/bewildr/element.rb +50 -30
- data/lib/bewildr/finder.rb +28 -1
- data/lib/bewildr/mouse.rb +99 -0
- metadata +22 -6
data/Rakefile
CHANGED
@@ -10,7 +10,7 @@ require 'cucumber/rake/task'
|
|
10
10
|
|
11
11
|
spec = Gem::Specification.new do |s|
|
12
12
|
s.name = 'bewildr'
|
13
|
-
s.version = '0.1.
|
13
|
+
s.version = '0.1.9'
|
14
14
|
s.has_rdoc = true
|
15
15
|
s.extra_rdoc_files = ['README.rdoc', 'LICENSE']
|
16
16
|
s.summary = 'Test WPF UI apps with IronRuby'
|
@@ -21,6 +21,7 @@ spec = Gem::Specification.new do |s|
|
|
21
21
|
s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{bin,lib}/**/*")
|
22
22
|
s.require_path = "lib"
|
23
23
|
s.bindir = "bin"
|
24
|
+
s.add_dependency('activesupport', '>= 3.0.0')
|
24
25
|
end
|
25
26
|
|
26
27
|
Rake::GemPackageTask.new(spec) do |p|
|
data/lib/bewildr.rb
CHANGED
@@ -11,6 +11,7 @@ load_assembly 'UIAutomationClient, Version=3.0.0.0, Culture=neutral, PublicKeyTo
|
|
11
11
|
load_assembly 'UIAutomationTypes, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
|
12
12
|
load_assembly 'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
|
13
13
|
load_assembly 'System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
|
14
|
+
load_assembly 'WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
|
14
15
|
|
15
16
|
#require BewildrClickr - stopgap until the ffi gem works under ironruby
|
16
17
|
require File.join(File.expand_path(File.dirname(__FILE__)), "bewildr", "ext", "BewildrClickr.dll")
|
@@ -18,6 +19,8 @@ CLICKR = Bewildr::Clickr::Clickr.new
|
|
18
19
|
|
19
20
|
#load plain old ruby libraries
|
20
21
|
require 'timeout'
|
22
|
+
require 'singleton'
|
23
|
+
require 'active_support/core_ext/string/inflections'
|
21
24
|
|
22
25
|
#require ruby extensions
|
23
26
|
require 'bewildr/ext/extensions'
|
@@ -30,6 +33,7 @@ require 'bewildr/finder'
|
|
30
33
|
require 'bewildr/application'
|
31
34
|
require 'bewildr/windows'
|
32
35
|
require 'bewildr/element'
|
36
|
+
require 'bewildr/mouse'
|
33
37
|
|
34
38
|
require 'bewildr/control_patterns/window_pattern'
|
35
39
|
require 'bewildr/control_patterns/value_pattern'
|
data/lib/bewildr/application.rb
CHANGED
@@ -1,35 +1,52 @@
|
|
1
1
|
#Copyright (c) 2010, Nathaniel Ritmeyer. All rights reserved.
|
2
2
|
|
3
3
|
module Bewildr
|
4
|
+
#Wraps System::Diagnostics::Process object and provides class methods to interrogate currently running
|
5
|
+
#processes, attach to processes or start new ones
|
4
6
|
class Application
|
7
|
+
#Returns a new instance of Bewildr::Application that wraps the process passed as the argument
|
5
8
|
def initialize(proc)
|
6
9
|
@proc = proc
|
7
10
|
@proc.wait_for_input_idle(10)
|
8
|
-
@proc_id = proc.id
|
9
11
|
end
|
10
12
|
private :initialize
|
11
13
|
|
14
|
+
#Returns the id of the underlying process
|
15
|
+
def proc_id
|
16
|
+
running? ? @proc.id.to_i : nil
|
17
|
+
end
|
18
|
+
|
19
|
+
#Return the name of the underlying process
|
20
|
+
def name
|
21
|
+
running? ? @proc.process_name.to_s : nil
|
22
|
+
end
|
23
|
+
|
24
|
+
#kills this process. Uses taskkill to perform the butchery
|
12
25
|
def kill
|
13
|
-
`taskkill /f /t /pid #{
|
26
|
+
`taskkill /f /t /pid #{proc_id}`
|
14
27
|
Timeout::timeout(5) do
|
15
28
|
sleep 0.1 while running?
|
16
29
|
end
|
17
30
|
end
|
18
31
|
|
32
|
+
#Returns true if this process is running, false if it isn't
|
19
33
|
def running?
|
20
34
|
@proc.nil? ? false : !@proc.has_exited
|
21
35
|
end
|
22
36
|
|
37
|
+
#Waits for up to 30 seconds for the process to terminate
|
23
38
|
def wait_for_termination
|
24
39
|
Timeout::timeout(30) do
|
25
40
|
sleep 0.2 while running?
|
26
41
|
end
|
27
42
|
end
|
28
43
|
|
44
|
+
#Returns a list of windows associated with this application
|
29
45
|
def windows
|
30
|
-
Bewildr::Windows.windows_by_process_id(
|
46
|
+
Bewildr::Windows.windows_by_process_id(proc_id)
|
31
47
|
end
|
32
48
|
|
49
|
+
#Returns a window whose title matches the string or regex passed in as the argument
|
33
50
|
def window_by_name(input)
|
34
51
|
case input
|
35
52
|
when String then return windows.select{|window| window.name.strip == input.strip}.first
|
@@ -40,6 +57,9 @@ module Bewildr
|
|
40
57
|
end
|
41
58
|
alias :window :window_by_name
|
42
59
|
|
60
|
+
#Waits for up to 30 seconds for a window belonging to this application to appear. When it does, a
|
61
|
+
#Bewildr::Element representing the window is returned. This method allows you in one line to wait
|
62
|
+
#for a window to appear and also return it
|
43
63
|
def wait_for_window(input, wait_time = 30)
|
44
64
|
begin
|
45
65
|
Timeout::timeout(wait_time) do
|
@@ -57,12 +77,15 @@ module Bewildr
|
|
57
77
|
end
|
58
78
|
end
|
59
79
|
|
60
|
-
#
|
80
|
+
#Returns a new instance of Bewildr::Application wrapping the process created when the specified exe file is invoked.
|
81
|
+
# Application.start("notepad.exe")
|
82
|
+
# Application.start("c:\some\path\my_app.exe")
|
61
83
|
def self.start(process_name)
|
62
84
|
raise "Can't find: #{process_name}" unless File.exist?(process_name)
|
63
85
|
Bewildr::Application.new(System::Diagnostics::Process.start(process_name))
|
64
86
|
end
|
65
87
|
|
88
|
+
#Same as the start method but allows the passing of arguments
|
66
89
|
def self.start_with_settings(path_to_exe, settings_hash)
|
67
90
|
start_info = System::Diagnostics::ProcessStartInfo.new(path_to_exe)
|
68
91
|
unless settings_hash[:args].nil? and settings_hash[:args].size > 0
|
@@ -71,14 +94,18 @@ module Bewildr
|
|
71
94
|
Bewildr::Application.new(System::Diagnostics::Process.start(start_info))
|
72
95
|
end
|
73
96
|
|
97
|
+
#Returns a Bewildr::Application wrapping a process already in memory where the argument is the process name
|
74
98
|
def self.attach_to_process_name(process_name)
|
75
99
|
Bewildr::Application.new(System::Diagnostics::Process.get_processes_by_name(process_name).first)
|
76
100
|
end
|
77
101
|
|
102
|
+
#Returns a Bewildr::Application wrapping a process already in memory where the argument is the process object to be wrapped
|
78
103
|
def self.attach_to_process(process)
|
79
104
|
Bewildr::Application.new(process)
|
80
105
|
end
|
81
106
|
|
107
|
+
#Given an exe name/path this method will return a new Bewildr::Application based either on a process already in memory
|
108
|
+
#or the process started by executing the app at the path specified in the argument
|
82
109
|
def self.attach_or_launch(path_to_exe)
|
83
110
|
#if app is already open attach to it
|
84
111
|
potential_process_name = File.basename(path_to_exe, ".exe")
|
@@ -91,12 +118,15 @@ module Bewildr
|
|
91
118
|
end
|
92
119
|
end
|
93
120
|
|
121
|
+
#Starts the app specified in the first argument and then waits and returns the window that matches the
|
122
|
+
#string or regex passed as the second argument
|
94
123
|
def self.start_app_and_wait_for_window(path, window_name)
|
95
124
|
app = Bewildr::Application.start(path)
|
96
125
|
window = app.wait_for_window(window_name)
|
97
126
|
return app, window
|
98
127
|
end
|
99
128
|
|
129
|
+
#Kills all processes that match the name passed as an argument
|
100
130
|
def self.kill_all_processes_with_name(input)
|
101
131
|
System::Diagnostics::Process.get_processes_by_name(input).each do |p|
|
102
132
|
p.kill
|
@@ -104,10 +134,14 @@ module Bewildr
|
|
104
134
|
end
|
105
135
|
end
|
106
136
|
|
137
|
+
#Returns an array of Bewildr::Application instances wrapping processes whose name match the string
|
138
|
+
#passed as an argument
|
107
139
|
def self.processes_with_name(input)
|
108
140
|
System::Diagnostics::Process.get_processes_by_name(input).collect {|p| Bewildr::Application.attach_to_process(p) }
|
109
141
|
end
|
110
142
|
|
143
|
+
#Waits for and eventually returns a Bewildr::Application wrapping the process whose name matches
|
144
|
+
#the string passed as an argument
|
111
145
|
def self.wait_for_process_with_name(input)
|
112
146
|
Timeout.timeout(30) {sleep 0.1 until System::Diagnostics::Process.get_processes_by_name(input).size > 0}
|
113
147
|
self.attach_to_process_name(input)
|
@@ -43,10 +43,11 @@ module Bewildr
|
|
43
43
|
|
44
44
|
def select_by_index(input)
|
45
45
|
raise "Index must be 0 or greater" if input < 0
|
46
|
-
|
46
|
+
list_items[input].select
|
47
47
|
end
|
48
48
|
|
49
49
|
def selected
|
50
|
+
return nil if get_selection.empty?
|
50
51
|
get_selection.first.name
|
51
52
|
end
|
52
53
|
end
|
data/lib/bewildr/element.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
#Copyright (c) 2010, Nathaniel Ritmeyer. All rights reserved.
|
2
2
|
|
3
3
|
module Bewildr
|
4
|
+
#All MS UI Automation elements accessed using bewildr are represented as instances of Bewildr::Element.
|
4
5
|
class Element
|
5
6
|
attr_reader :automation_element
|
6
7
|
attr_reader :control_type
|
7
8
|
|
9
|
+
#Returns a new instance of Bewildr::Element wrapping the MS UI Automation Element passed in as an
|
10
|
+
#argument. You probably won't be calling this method directly - if you are then you probably know
|
11
|
+
#what your'e doing
|
8
12
|
def initialize(input)
|
9
13
|
@automation_element = input
|
10
14
|
case input
|
@@ -17,16 +21,19 @@ module Bewildr
|
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
24
|
+
#Returns the underlying automation element's name property
|
20
25
|
def name
|
21
26
|
existence_check
|
22
27
|
@automation_element.current.name.to_s
|
23
28
|
end
|
24
29
|
|
30
|
+
#Returns the underlying automation element's automation id property
|
25
31
|
def automation_id
|
26
32
|
existence_check
|
27
33
|
@automation_element.current.automation_id.to_s
|
28
34
|
end
|
29
35
|
|
36
|
+
#Returns true if the element exists, false if it doesn't
|
30
37
|
def exists?
|
31
38
|
return false if @control_type == :non_existent
|
32
39
|
begin
|
@@ -38,21 +45,26 @@ module Bewildr
|
|
38
45
|
end
|
39
46
|
alias :exist? :exists?
|
40
47
|
|
48
|
+
#Returns true if the element is visible, false if it isn't
|
41
49
|
def visible?
|
42
50
|
existence_check
|
43
51
|
!@automation_element.current.is_offscreen
|
44
52
|
end
|
45
53
|
|
54
|
+
#Returns true if this element has a descendant that meets the search criteria, false if it doesn't
|
55
|
+
#Takes a condition hash
|
46
56
|
def contains?(condition_hash)
|
47
57
|
get(condition_hash).exists?
|
48
58
|
end
|
49
59
|
alias :contain? :contains?
|
50
60
|
|
61
|
+
#Returns true if the underlying automation element is enabled, false if it's not
|
51
62
|
def enabled?
|
52
63
|
existence_check
|
53
64
|
@automation_element.current.is_enabled
|
54
65
|
end
|
55
66
|
|
67
|
+
#Waits up to 30 seconds for a descendant of this element to exist that meets the search criteria
|
56
68
|
def wait_for_existence_of(condition_hash)
|
57
69
|
Timeout.timeout(30) do
|
58
70
|
sleep 0.1 until contains?(condition_hash)
|
@@ -60,26 +72,31 @@ module Bewildr
|
|
60
72
|
get(condition_hash)
|
61
73
|
end
|
62
74
|
|
75
|
+
#Waits for up to 30 seconds for this element to no longer have a descendant element that meest the criteria
|
63
76
|
def wait_for_non_existence_of(condition_hash)
|
64
77
|
Timeout.timeout(30) do
|
65
78
|
sleep 0.1 unless contains?(condition_hash)
|
66
79
|
end
|
67
80
|
end
|
68
81
|
|
82
|
+
#Gives this element focus
|
69
83
|
def focus
|
70
84
|
@automation_element.set_focus
|
71
85
|
end
|
72
86
|
|
87
|
+
#Returns true if this element is scrollable, false if it isn't.
|
73
88
|
def scrollable?
|
74
89
|
return false unless @automation_element.get_supported_patterns.collect {|pattern| pattern.programmatic_name.to_s }.include?("ScrollPatternIdentifiers.Pattern")
|
75
90
|
vertically_scrollable
|
76
91
|
end
|
77
92
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
93
|
+
#Returns the result of searching this element's children or descendants for the first or all matches of
|
94
|
+
#a particular id, name or type (or combination!). See Bewildr::Finder for more details
|
95
|
+
# :id => "id"
|
96
|
+
# :name => "bob"
|
97
|
+
# :type => :button
|
98
|
+
# :scope => :children / :descendants
|
99
|
+
# :how_many => :first / :all
|
83
100
|
def get(condition_hash)
|
84
101
|
existence_check
|
85
102
|
condition = Finder.condition_for(condition_hash)
|
@@ -99,12 +116,12 @@ module Bewildr
|
|
99
116
|
end
|
100
117
|
end
|
101
118
|
|
102
|
-
#
|
103
|
-
|
119
|
+
#Returns true if this element is the root element of the UI element tree
|
104
120
|
def root?
|
105
121
|
@automation_element == System::Windows::Automation::AutomationElement.root_element
|
106
122
|
end
|
107
123
|
|
124
|
+
#Returns a new element representing this element's parent in the UI element tree
|
108
125
|
def parent
|
109
126
|
return nil if root?
|
110
127
|
walker = System::Windows::Automation::TreeWalker.RawViewWalker
|
@@ -112,11 +129,13 @@ module Bewildr
|
|
112
129
|
Bewildr::Element.new(potential_parent)
|
113
130
|
end
|
114
131
|
|
132
|
+
#Returns true if this element has descendants, false if it doesn't
|
115
133
|
def has_children?
|
116
134
|
walker = System::Windows::Automation::TreeWalker.RawViewWalker
|
117
135
|
!walker.get_first_child(@automation_element).nil?
|
118
136
|
end
|
119
137
|
|
138
|
+
#Returns an array containing this element's (direct) children
|
120
139
|
def children
|
121
140
|
return [] unless has_children?
|
122
141
|
walker = System::Windows::Automation::TreeWalker.ControlViewWalker #RawViewWalker
|
@@ -129,24 +148,22 @@ module Bewildr
|
|
129
148
|
bewildred_children
|
130
149
|
end
|
131
150
|
|
151
|
+
#Clicks this element - this is done by actual mouse moves and clicks. The automation element's underlying InvokePattern is not used.
|
132
152
|
def click
|
133
|
-
|
153
|
+
Bewildr::Mouse.click(clickable_point)
|
134
154
|
end
|
135
155
|
|
156
|
+
#Double clicks this element - this is done by actual mouse moves and clicks. The automation element's underlying InvokePattern is not used.
|
136
157
|
def double_click
|
137
|
-
|
158
|
+
Bewildr::Mouse.double_click(clickable_point)
|
138
159
|
end
|
139
160
|
|
161
|
+
#Right clicks this element - this is done by actual mouse moves and clicks. The automation element's underlying InvokePattern is not used.
|
140
162
|
def right_click
|
141
|
-
|
142
|
-
end
|
143
|
-
|
144
|
-
private
|
145
|
-
|
146
|
-
def existence_check
|
147
|
-
raise Bewildr::ElementDoesntExist unless exists?
|
163
|
+
Bewildr::Mouse.right_click(clickable_point)
|
148
164
|
end
|
149
165
|
|
166
|
+
#Returns the underlying automation element's clickable point property - this is used by the various click methods
|
150
167
|
def clickable_point
|
151
168
|
existence_check
|
152
169
|
Timeout.timeout(30) do
|
@@ -160,14 +177,24 @@ module Bewildr
|
|
160
177
|
end
|
161
178
|
end
|
162
179
|
|
180
|
+
private
|
181
|
+
|
182
|
+
#Raises an exception if this element no longer exists
|
183
|
+
def existence_check
|
184
|
+
raise Bewildr::ElementDoesntExist unless exists?
|
185
|
+
end
|
186
|
+
|
187
|
+
#Used to add scrollability to an element if it becomes scrollable subsequent to its initial discovery
|
163
188
|
def prepare_element
|
164
189
|
load_all_items_hack if scrollable?
|
165
190
|
end
|
166
191
|
|
192
|
+
#Figures out this element's control type
|
167
193
|
def set_control_type
|
168
194
|
@control_type = Bewildr::ControlType.symbol_for_enum(@automation_element.current.control_type)
|
169
195
|
end
|
170
196
|
|
197
|
+
#Mixes in bewildr control patterns dynamically based on what patterns the underlying automation element has
|
171
198
|
def build_element
|
172
199
|
@automation_element.get_supported_patterns.each do |pattern|
|
173
200
|
pattern_name = pattern.programmatic_name.to_s.gsub(/Identifiers\.Pattern/, "")
|
@@ -176,22 +203,15 @@ module Bewildr
|
|
176
203
|
end
|
177
204
|
end
|
178
205
|
|
206
|
+
#Adds control type addition modules to this element based on the underlying automation element's control type.
|
179
207
|
#list of control types comes from http://msdn.microsoft.com/en-us/library/ms750574.aspx
|
180
208
|
def include_additions
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
when :menu then extend Bewildr::ControlTypeAdditions::MenuAdditions
|
188
|
-
when :menu_item then extend Bewildr::ControlTypeAdditions::MenuItemAdditions
|
189
|
-
when :tab then extend Bewildr::ControlTypeAdditions::TabAdditions
|
190
|
-
when :text then extend Bewildr::ControlTypeAdditions::TextAdditions
|
191
|
-
when :tree then extend Bewildr::ControlTypeAdditions::TreeAdditions
|
192
|
-
when :tree_item then extend Bewildr::ControlTypeAdditions::TreeItemAdditions
|
193
|
-
when :window then extend Bewildr::ControlTypeAdditions::WindowAdditions
|
194
|
-
end
|
209
|
+
#the following looks like could have performance issues, but the tests don't take any longer to execute...
|
210
|
+
potential_addition = Bewildr::ControlTypeAdditions.submodules.select {|mod| mod.name.demodulize == "#{@control_type.to_s.classify}Additions"}.first
|
211
|
+
extend potential_addition unless potential_addition.nil?
|
212
|
+
|
213
|
+
#non-standards - move this to another method in the near future
|
214
|
+
extend Bewildr::ControlTypeAdditions::TextAdditions if @control_type == :hyperlink
|
195
215
|
|
196
216
|
#add scrolling capability if relevant - TODO: this ugliness will be fixed later
|
197
217
|
if @automation_element.get_supported_patterns.collect {|pattern| pattern.programmatic_name.to_s }.include?("ScrollPatternIdentifiers.Pattern")
|
data/lib/bewildr/finder.rb
CHANGED
@@ -1,10 +1,20 @@
|
|
1
1
|
#Copyright (c) 2010, Nathaniel Ritmeyer. All rights reserved.
|
2
2
|
|
3
3
|
module Bewildr
|
4
|
+
#Wraps the Search Condition paradigm used by MS UI Automation to find objects in the UI Element tree. It contains various
|
5
|
+
#methods that build up the parts needed for to search for elements. These methods are called by Bewildr::Element#get in
|
6
|
+
#order to get and test for existence of descendent elements in the UI element tree. All of the methods take a condition
|
7
|
+
#hash, this is made up of at least one, but can be several key/value pairs.
|
4
8
|
class Finder
|
5
9
|
extend Bewildr::BewildrHelpers
|
6
10
|
|
7
11
|
class << self
|
12
|
+
#Returns a System::Windows::Automation::PropertyCondition or AndCondition based on a translation of the condition hash.
|
13
|
+
#The condition hash must contain at least one of the following keys:
|
14
|
+
# :id => "some id"
|
15
|
+
# :name => "some name"
|
16
|
+
# :type => :some_control_type
|
17
|
+
#These keys can be combined to create more complex search criteria
|
8
18
|
def condition_for(condition_hash)
|
9
19
|
conditions = condition_hash.select {|key, value| [:id, :name, :type].include?(key) }
|
10
20
|
conditions = Hash[*conditions.flatten]
|
@@ -15,6 +25,11 @@ module Bewildr
|
|
15
25
|
end
|
16
26
|
end
|
17
27
|
|
28
|
+
#Returns whether the seach should interrogate only children, or all descendants. If neither is specified, the default
|
29
|
+
#is to search the descendants. The condition hash should contain one of the following options:
|
30
|
+
# :scope => :children
|
31
|
+
# :scope => :descendants
|
32
|
+
# nothing
|
18
33
|
def scope_for(condition_hash)
|
19
34
|
case condition_hash[:scope]
|
20
35
|
when :children then System::Windows::Automation::TreeScope.Children
|
@@ -22,7 +37,12 @@ module Bewildr
|
|
22
37
|
end
|
23
38
|
end
|
24
39
|
|
25
|
-
#
|
40
|
+
#Returns whether the seach should stop at the first element that matches the criteria or whether it should find all
|
41
|
+
#elements that match. If neither is specified, the default is to find only the first element that matches. The condition
|
42
|
+
#hash should contain one of the following options:
|
43
|
+
# :how_many => :first
|
44
|
+
# :how_many => :all
|
45
|
+
# nothing
|
26
46
|
def how_many_for(condition_hash)
|
27
47
|
case condition_hash[:how_many]
|
28
48
|
when :first, nil then :find_first
|
@@ -33,12 +53,16 @@ module Bewildr
|
|
33
53
|
|
34
54
|
private
|
35
55
|
|
56
|
+
#Used to create an AndCondition that takes into account all of the search conditions. This is done by
|
57
|
+
#creating a single condition for each of the conditions and then creating an AndCondition combining
|
58
|
+
#the individual search conditions
|
36
59
|
def multiple_conditions(condition_hash)
|
37
60
|
condition_array = []
|
38
61
|
condition_hash.keys.each {|key| condition_array << single_condition({key => condition_hash[key]}) }
|
39
62
|
System::Windows::Automation::AndCondition.new(r_array_to_cs_array_of_conditions(condition_array))
|
40
63
|
end
|
41
64
|
|
65
|
+
#Calls the condition hash's corresponding condition creation method
|
42
66
|
def single_condition(condition_hash)
|
43
67
|
case condition_hash.keys.first
|
44
68
|
when :id then id_condition(condition_hash)
|
@@ -47,16 +71,19 @@ module Bewildr
|
|
47
71
|
end
|
48
72
|
end
|
49
73
|
|
74
|
+
#Returns a PropertyCondition when :id has been specified as the condition type
|
50
75
|
def id_condition(condition_hash)
|
51
76
|
value = r_string_to_c_string(condition_hash[:id].to_s)
|
52
77
|
System::Windows::Automation::PropertyCondition.new(System::Windows::Automation::AutomationElement.automation_id_property, value)
|
53
78
|
end
|
54
79
|
|
80
|
+
#Returns a PropertyCondition when :name has been specified as the condition type
|
55
81
|
def name_condition(condition_hash)
|
56
82
|
value = r_string_to_c_string(condition_hash[:name])
|
57
83
|
System::Windows::Automation::PropertyCondition.new(System::Windows::Automation::AutomationElement.name_property, value)
|
58
84
|
end
|
59
85
|
|
86
|
+
#Returns a PropertyCondition when :type has been specified as the condition type
|
60
87
|
def type_condition(condition_hash)
|
61
88
|
value = Bewildr::ControlType.enum_for_symbol(condition_hash[:type])
|
62
89
|
System::Windows::Automation::PropertyCondition.new(System::Windows::Automation::AutomationElement.control_type_property, value)
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#Represents and gives access to mouse actions. Implemented as a singleton, but accessed through class methods to make the API more contained.
|
2
|
+
class Bewildr::Mouse
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@clickr = Bewildr::Clickr::Clickr.new
|
7
|
+
end
|
8
|
+
|
9
|
+
#Returns the BewildrClickr instance - don't use!
|
10
|
+
def clickr
|
11
|
+
@clickr
|
12
|
+
end
|
13
|
+
|
14
|
+
#Clicks the mouse at the supplied point
|
15
|
+
def self.click(point)
|
16
|
+
instance.clickr.click(point)
|
17
|
+
end
|
18
|
+
|
19
|
+
#Double clicks the mouse at the supplied point
|
20
|
+
def self.double_click(point)
|
21
|
+
instance.clickr.double_click(point)
|
22
|
+
end
|
23
|
+
|
24
|
+
#Right clicks the mouse at the supplied point
|
25
|
+
def self.right_click(point)
|
26
|
+
instance.clickr.right_click(point)
|
27
|
+
end
|
28
|
+
|
29
|
+
#Press the left mouse button down at the current point
|
30
|
+
def self.left_down
|
31
|
+
instance.clickr.left_mouse_button_down
|
32
|
+
end
|
33
|
+
|
34
|
+
#Release the left mouse button
|
35
|
+
def self.left_up
|
36
|
+
instance.clickr.left_mouse_button_up
|
37
|
+
end
|
38
|
+
|
39
|
+
#Moves the mouse to the supplied point
|
40
|
+
def self.move_to(point)
|
41
|
+
instance.clickr.set_location(point)
|
42
|
+
end
|
43
|
+
|
44
|
+
#Drags the mouse from one location to another, and optionally, via another! Pass a has containing the following keys:
|
45
|
+
# :from
|
46
|
+
# :to
|
47
|
+
# :via (optional)
|
48
|
+
# :wait_at_via_for
|
49
|
+
#The value for each of those keys should be either a point or an element apart from :wait_at_via_for which
|
50
|
+
#takes seconds (eg: 0.2, 3, etc). If an element is supplied, the drag point will be the element's clickable point
|
51
|
+
def self.drag(args)
|
52
|
+
raise "Can't drag without a :from key in the hash" unless args.keys.include?(:from)
|
53
|
+
raise "Can't drag without a :to key in the hash" unless args.keys.include?(:to)
|
54
|
+
|
55
|
+
point_from = args[:from]
|
56
|
+
point_to = args[:to]
|
57
|
+
point_via = args[:via]
|
58
|
+
args[:wait_at_via_for].nil? ? wait_time = 2 : wait_time = args[:wait_at_via_for]
|
59
|
+
|
60
|
+
point_from = point_from.clickable_point if point_from.instance_of?(Bewildr::Element)
|
61
|
+
point_to = point_to.clickable_point if point_to.instance_of?(Bewildr::Element)
|
62
|
+
point_via = point_via.clickable_point if !point_via.nil? && point_via.instance_of?(Bewildr::Element)
|
63
|
+
|
64
|
+
raise "Supplied :from was not an Element or a Point" unless point_from.class == System::Windows::Point
|
65
|
+
raise "Supplied :to was not an Element or a Point" unless point_to.class == System::Windows::Point
|
66
|
+
raise "Supplied :via was not an Element or a Point" unless point_via.nil? || point_via.class == System::Windows::Point
|
67
|
+
|
68
|
+
self.move_to(point_from)
|
69
|
+
self.left_down
|
70
|
+
sleep 0.2
|
71
|
+
|
72
|
+
if point_via.instance_of?(System::Windows::Point)
|
73
|
+
almost_midway = System::Windows::Point.new
|
74
|
+
almost_midway.x = point_via.x + 1
|
75
|
+
almost_midway.y = point_via.y + 1
|
76
|
+
self.move_to(almost_midway)
|
77
|
+
sleep 0.3
|
78
|
+
self.move_to(point_via)
|
79
|
+
sleep wait_time
|
80
|
+
end
|
81
|
+
|
82
|
+
almost_there = System::Windows::Point.new
|
83
|
+
almost_there.x = point_to.x + 1
|
84
|
+
almost_there.y = point_to.y + 1
|
85
|
+
|
86
|
+
self.move_to(almost_there)
|
87
|
+
sleep 0.2
|
88
|
+
self.move_to(point_to)
|
89
|
+
self.left_up
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
=begin
|
94
|
+
Methods in BewildrClickr dll that need wrapping here...
|
95
|
+
|
96
|
+
public void Drag(Point startingLocation, Point targetLocation)
|
97
|
+
public void RightMouseButtonDown()
|
98
|
+
public void RightMouseButtonUp()
|
99
|
+
=end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bewildr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 9
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 9
|
10
|
+
version: 0.1.9
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Nat Ritmeyer
|
@@ -15,10 +15,25 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-01-20 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activesupport
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 3.0.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
22
37
|
description: Test WPF UI apps with IronRuby
|
23
38
|
email: nat@natontesting.com
|
24
39
|
executables: []
|
@@ -64,6 +79,7 @@ files:
|
|
64
79
|
- lib/bewildr/ext/BewildrClickr.dll
|
65
80
|
- lib/bewildr/ext/extensions.rb
|
66
81
|
- lib/bewildr/finder.rb
|
82
|
+
- lib/bewildr/mouse.rb
|
67
83
|
- lib/bewildr/windows.rb
|
68
84
|
- lib/bewildr.rb
|
69
85
|
has_rdoc: true
|