AXElements 0.7.8 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +1 -10
- data/README.markdown +7 -14
- data/ext/accessibility/key_coder/key_coder.c +7 -0
- data/lib/AXElements.rb +0 -2
- data/lib/accessibility/core.rb +180 -123
- data/lib/accessibility/dsl.rb +310 -191
- data/lib/accessibility/enumerators.rb +9 -8
- data/lib/accessibility/errors.rb +7 -8
- data/lib/accessibility/factory.rb +16 -9
- data/lib/accessibility/graph.rb +68 -22
- data/lib/accessibility/highlighter.rb +86 -0
- data/lib/accessibility/pp_inspector.rb +4 -4
- data/lib/accessibility/qualifier.rb +11 -9
- data/lib/accessibility/string.rb +12 -4
- data/lib/accessibility/translator.rb +19 -10
- data/lib/accessibility/version.rb +3 -1
- data/lib/accessibility.rb +42 -17
- data/lib/ax/application.rb +90 -30
- data/lib/ax/button.rb +5 -2
- data/lib/ax/element.rb +133 -149
- data/lib/ax/pop_up_button.rb +12 -0
- data/lib/ax/radio_button.rb +5 -2
- data/lib/ax/row.rb +2 -2
- data/lib/ax/static_text.rb +5 -2
- data/lib/ax/systemwide.rb +24 -12
- data/lib/ax_elements/awesome_print.rb +13 -0
- data/lib/ax_elements/exception_workaround.rb +5 -0
- data/lib/ax_elements/nsarray_compat.rb +1 -0
- data/lib/ax_elements.rb +2 -1
- data/lib/minitest/ax_elements.rb +60 -4
- data/lib/mouse.rb +47 -20
- data/lib/rspec/expectations/ax_elements.rb +180 -88
- data/rakelib/doc.rake +7 -0
- data/test/helper.rb +2 -1
- data/test/integration/accessibility/test_dsl.rb +126 -18
- data/test/integration/accessibility/test_errors.rb +1 -1
- data/test/integration/ax/test_element.rb +17 -0
- data/test/integration/minitest/test_ax_elements.rb +33 -38
- data/test/integration/rspec/expectations/test_ax_elements.rb +68 -19
- data/test/sanity/accessibility/test_core.rb +45 -37
- data/test/sanity/accessibility/test_highlighter.rb +56 -0
- data/test/sanity/ax/test_application.rb +8 -0
- data/test/sanity/ax/test_element.rb +7 -3
- data/test/sanity/minitest/test_ax_elements.rb +2 -0
- data/test/sanity/rspec/expectations/test_ax_elements.rb +3 -0
- data/test/sanity/test_accessibility.rb +9 -0
- data/test/sanity/test_mouse.rb +2 -2
- metadata +11 -38
- data/docs/AccessibilityTips.markdown +0 -119
- data/docs/Acting.markdown +0 -340
- data/docs/Debugging.markdown +0 -165
- data/docs/Inspecting.markdown +0 -261
- data/docs/KeyboardEvents.markdown +0 -122
- data/docs/NewBehaviour.markdown +0 -151
- data/docs/Notifications.markdown +0 -271
- data/docs/Searching.markdown +0 -250
- data/docs/TestingExtensions.markdown +0 -52
- data/docs/images/all_the_buttons.jpg +0 -0
- data/docs/images/next_version.png +0 -0
- data/docs/images/ui_hierarchy.dot +0 -34
- data/docs/images/ui_hierarchy.png +0 -0
- data/lib/accessibility/debug.rb +0 -164
- data/test/integration/accessibility/test_debug.rb +0 -44
- data/test/sanity/accessibility/test_debug.rb +0 -63
data/lib/accessibility.rb
CHANGED
@@ -1,29 +1,41 @@
|
|
1
|
+
require 'accessibility/version'
|
1
2
|
require 'ax/application'
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
class << Accessibility
|
5
|
+
|
6
|
+
# Initialize the DEBUG value
|
7
|
+
@debug = ENV.fetch 'AXDEBUG', $DEBUG
|
8
|
+
|
9
|
+
##
|
10
|
+
# Whether or not to turn on DEBUG features in AXElements. The
|
11
|
+
# value is initially inherited from `$DEBUG` but can be overridden
|
12
|
+
# by an environment variable named `AXDEBUG` or changed dynamically
|
13
|
+
# at runtime.
|
14
|
+
#
|
15
|
+
# @return [Boolean]
|
16
|
+
attr_accessor :debug
|
17
|
+
alias_method :debug?, :debug
|
18
|
+
|
7
19
|
|
8
20
|
# @group Finding an application object
|
9
21
|
|
10
22
|
##
|
11
23
|
# @todo Move to {AX::Aplication#initialize} eventually.
|
12
|
-
# @todo Find a way for this method to work without sleeping;
|
13
|
-
# consider looping begin/rescue/end until AX starts up
|
14
|
-
# @todo This needs to handle bad bundle identifier's gracefully
|
15
24
|
#
|
16
25
|
# This is the standard way of creating an application object. It will
|
17
26
|
# launch the app if it is not already running and then create the
|
18
27
|
# accessibility object.
|
19
28
|
#
|
20
|
-
# However, this method is a
|
29
|
+
# However, this method is a bit of a hack in cases where the app is not
|
21
30
|
# already running; I've tried to register for notifications, launch
|
22
31
|
# synchronously, etc., but there is always a problem with accessibility
|
23
|
-
# not being ready.
|
32
|
+
# not being ready right away.
|
24
33
|
#
|
25
34
|
# If this method fails to find an app with the appropriate bundle
|
26
|
-
# identifier then it will
|
35
|
+
# identifier then it will raise an exception. If the problem was not a
|
36
|
+
# typo, then it might mean that the bundle identifier has not been
|
37
|
+
# registered with the system yet and you should launch the app once
|
38
|
+
# manually.
|
27
39
|
#
|
28
40
|
# @example
|
29
41
|
#
|
@@ -33,17 +45,20 @@ class << self
|
|
33
45
|
# @param [String] bundle a bundle identifier
|
34
46
|
# @return [AX::Application,nil]
|
35
47
|
def application_with_bundle_identifier bundle
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
48
|
+
if app_running?(bundle) || launch_application(bundle)
|
49
|
+
10.times do
|
50
|
+
return AX::Application.new(bundle) if app_running? bundle
|
51
|
+
sleep 1
|
52
|
+
end
|
53
|
+
else
|
54
|
+
raise ArgumentError "Could not launch app matching bundle id `#{bundle}'"
|
41
55
|
end
|
42
56
|
nil
|
43
57
|
end
|
44
58
|
|
45
59
|
##
|
46
|
-
# @deprecated
|
60
|
+
# @deprecated Directly initialize an {AX::Application} instance instead
|
61
|
+
# (e.g. `AX::Application.new('Terminal')`).
|
47
62
|
#
|
48
63
|
# Get the accessibility object for an application given its localized
|
49
64
|
# name. This will only work if the application is already running.
|
@@ -55,6 +70,7 @@ class << self
|
|
55
70
|
# @param [String] name name of the application to launch
|
56
71
|
# @return [AX::Application,nil]
|
57
72
|
def application_with_name name
|
73
|
+
$stderr.puts 'DEPRECATED: Use AX::Application.new instead'
|
58
74
|
AX::Application.new name
|
59
75
|
end
|
60
76
|
|
@@ -63,6 +79,16 @@ class << self
|
|
63
79
|
|
64
80
|
private
|
65
81
|
|
82
|
+
##
|
83
|
+
# Find out if the app is running and if so, return the running application
|
84
|
+
# for that bundle.
|
85
|
+
#
|
86
|
+
# @param [String]
|
87
|
+
# @return [NSRunningApplication,nil]
|
88
|
+
def app_running? bundle
|
89
|
+
NSRunningApplication.runningApplicationsWithBundleIdentifier(bundle).first
|
90
|
+
end
|
91
|
+
|
66
92
|
##
|
67
93
|
# Asynchronously launch an application given the bundle identifier.
|
68
94
|
#
|
@@ -76,4 +102,3 @@ class << self
|
|
76
102
|
end
|
77
103
|
|
78
104
|
end
|
79
|
-
end
|
data/lib/ax/application.rb
CHANGED
@@ -2,28 +2,24 @@ require 'ax/element'
|
|
2
2
|
require 'accessibility/string'
|
3
3
|
|
4
4
|
##
|
5
|
-
#
|
5
|
+
# The accessibility object representing the running application. This
|
6
|
+
# class contains some additional constructors and conveniences for
|
7
|
+
# Application objects.
|
6
8
|
#
|
7
9
|
# As this class has evolved, it has gathered some functionality from
|
8
|
-
# the `NSRunningApplication`
|
10
|
+
# the `NSRunningApplication` and `NSBundle` classes.
|
9
11
|
class AX::Application < AX::Element
|
10
12
|
include Accessibility::String
|
11
13
|
|
12
14
|
##
|
13
|
-
#
|
14
|
-
# Cached reference to the system wide object.
|
15
|
-
#
|
16
|
-
# @return [AXUIElementRef]
|
17
|
-
SYSTEMWIDE = AXUIElementCreateSystemWide()
|
18
|
-
|
19
|
-
##
|
20
|
-
# Overridden so that we can also cache the `NSRunningApplication`
|
21
|
-
# instance for this object.
|
15
|
+
# Overridden so that we can more flexibly manipulate input.
|
22
16
|
#
|
23
17
|
# You can initialize an application object with either the process
|
24
18
|
# identifier (pid) of the application, the name of the application,
|
25
19
|
# an `NSRunningApplication` instance for the application, or an
|
26
20
|
# accessibility (`AXUIElementRef`) token.
|
21
|
+
#
|
22
|
+
# @param [Number,String,NSRunningApplication]
|
27
23
|
def initialize arg
|
28
24
|
case arg
|
29
25
|
when Fixnum
|
@@ -52,7 +48,7 @@ class AX::Application < AX::Element
|
|
52
48
|
# @group Attributes
|
53
49
|
|
54
50
|
##
|
55
|
-
# Overridden to handle the {Accessibility::DSL#
|
51
|
+
# Overridden to handle the {Accessibility::DSL#set_focus_to} case.
|
56
52
|
#
|
57
53
|
# (see AX::Element#attribute)
|
58
54
|
def attribute attr
|
@@ -74,7 +70,7 @@ class AX::Application < AX::Element
|
|
74
70
|
|
75
71
|
##
|
76
72
|
# Ask the app whether or not it is the active app. This is equivalent
|
77
|
-
# to the dynamic
|
73
|
+
# to the dynamic `#focused?` method, but might make more sense to use
|
78
74
|
# in some cases.
|
79
75
|
def active?
|
80
76
|
@ref.spin_run_loop
|
@@ -100,7 +96,7 @@ class AX::Application < AX::Element
|
|
100
96
|
##
|
101
97
|
# Overridden to handle the {Accessibility::Language#set_focus} case.
|
102
98
|
#
|
103
|
-
# (see AX::Element#set
|
99
|
+
# (see AX::Element#set)
|
104
100
|
def set attr, value
|
105
101
|
case attr
|
106
102
|
when :focused
|
@@ -142,30 +138,50 @@ class AX::Application < AX::Element
|
|
142
138
|
end
|
143
139
|
|
144
140
|
##
|
145
|
-
# Send keyboard input to
|
146
|
-
# has focus will receive the key presses.
|
141
|
+
# Send keyboard input to the receiver, the control in the app that
|
142
|
+
# currently has focus will receive the key presses.
|
147
143
|
#
|
148
144
|
# For details on how to format the string, check out the
|
149
|
-
#
|
145
|
+
# [Keyboarding documentation](http://github.com/Marketcircle/AXElements/wiki/Keyboarding).
|
150
146
|
#
|
151
147
|
# @return [Boolean]
|
152
148
|
def type string
|
153
149
|
@ref.post keyboard_events_for string
|
154
150
|
true
|
155
151
|
end
|
152
|
+
alias_method :type_string, :type
|
156
153
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
#
|
164
|
-
|
165
|
-
|
166
|
-
|
154
|
+
##
|
155
|
+
# Press the given modifier key and hold it down while yielding to
|
156
|
+
# the given block.
|
157
|
+
#
|
158
|
+
# @example
|
159
|
+
#
|
160
|
+
# hold_key "\\CONTROL" do
|
161
|
+
# drag_mouse_to point
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# @param [String]
|
165
|
+
# @return [Number,nil]
|
166
|
+
def hold_modifier key
|
167
|
+
code = EventGenerator::CUSTOM[key]
|
168
|
+
raise ArgumentError, "Invalid modifier `#{key}' given" unless code
|
169
|
+
@ref.post [[code, true]]
|
170
|
+
yield
|
171
|
+
ensure
|
172
|
+
@ref.post [[code,false]] if code
|
173
|
+
code
|
167
174
|
end
|
168
175
|
|
176
|
+
##
|
177
|
+
# Navigate the menu bar menus for the receiver and select the menu
|
178
|
+
# item at the end of the given path. This method will open each menu
|
179
|
+
# in the path.
|
180
|
+
#
|
181
|
+
# @example
|
182
|
+
#
|
183
|
+
# safari.select_menu_item 'Edit', 'Find', /Google/
|
184
|
+
#
|
169
185
|
# @return [AX::MenuItem]
|
170
186
|
def select_menu_item *path
|
171
187
|
target = navigate_menu *path
|
@@ -173,8 +189,15 @@ class AX::Application < AX::Element
|
|
173
189
|
target
|
174
190
|
end
|
175
191
|
|
192
|
+
##
|
193
|
+
# Navigate the menu bar menus for the receiver. This method will not
|
194
|
+
# select the last item, but it will open each menu along the path.
|
195
|
+
#
|
196
|
+
# You may also be interested in {#select_menu_item}.
|
197
|
+
#
|
176
198
|
# @return [AX::MenuItem]
|
177
199
|
def navigate_menu *path
|
200
|
+
# @todo CLEAN UP
|
178
201
|
perform :unhide # can't navigate menus unless the app is up front
|
179
202
|
current = attribute(:menu_bar).search(:menu_bar_item, title: path.shift)
|
180
203
|
path.each do |part|
|
@@ -220,15 +243,13 @@ class AX::Application < AX::Element
|
|
220
243
|
|
221
244
|
|
222
245
|
##
|
223
|
-
# @todo Include bundle identifier?
|
224
|
-
#
|
225
246
|
# Override the base class to make sure the pid is included.
|
226
247
|
def inspect
|
227
248
|
super.sub! />$/, "#{pp_checkbox(:focused)} pid=#{pid}>"
|
228
249
|
end
|
229
250
|
|
230
251
|
##
|
231
|
-
# Find the element in
|
252
|
+
# Find the element in the receiver that is at point given.
|
232
253
|
#
|
233
254
|
# `nil` will be returned if there was nothing at that point.
|
234
255
|
#
|
@@ -251,4 +272,43 @@ class AX::Application < AX::Element
|
|
251
272
|
@app.bundleIdentifier
|
252
273
|
end
|
253
274
|
|
275
|
+
##
|
276
|
+
# Return the `Info.plist` data for the application. This is a plist
|
277
|
+
# file that all bundles in OS X must contain.
|
278
|
+
#
|
279
|
+
# Many bits of metadata are stored in the plist, check the
|
280
|
+
# [reference](https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigFiles.html)
|
281
|
+
# for more details.
|
282
|
+
#
|
283
|
+
# @return [Hash]
|
284
|
+
def info_plist
|
285
|
+
bundle.infoDictionary
|
286
|
+
end
|
287
|
+
|
288
|
+
##
|
289
|
+
# Get the version string for the application.
|
290
|
+
#
|
291
|
+
# @example
|
292
|
+
#
|
293
|
+
# AX::Application.new("Safari").version # => "5.2"
|
294
|
+
# AX::Application.new("Terminal").version # => "2.2.2"
|
295
|
+
# AX::Application.new("Daylite").version # => "3.15 (build 3664)"
|
296
|
+
#
|
297
|
+
# @return [String]
|
298
|
+
def version
|
299
|
+
bundle.objectForInfoDictionaryKey 'CFBundleShortVersionString'
|
300
|
+
end
|
301
|
+
|
302
|
+
|
303
|
+
private
|
304
|
+
|
305
|
+
# @return [NSBundle]
|
306
|
+
def bundle
|
307
|
+
@bundle ||= NSBundle.bundleWithURL @app.bundleURL
|
308
|
+
end
|
309
|
+
|
310
|
+
# @private
|
311
|
+
# @return [AXUIElementRef]
|
312
|
+
SYSTEMWIDE = AXUIElementCreateSystemWide()
|
313
|
+
|
254
314
|
end
|
data/lib/ax/button.rb
CHANGED
@@ -13,8 +13,11 @@ class AX::Button < AX::Element
|
|
13
13
|
#
|
14
14
|
# @return [Boolean]
|
15
15
|
def == other
|
16
|
-
|
17
|
-
|
16
|
+
if other.kind_of? NSString
|
17
|
+
attribute(:title) == other
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
18
21
|
end
|
19
22
|
|
20
23
|
end
|