AXElements 0.7.8 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.yardopts +1 -10
  2. data/README.markdown +7 -14
  3. data/ext/accessibility/key_coder/key_coder.c +7 -0
  4. data/lib/AXElements.rb +0 -2
  5. data/lib/accessibility/core.rb +180 -123
  6. data/lib/accessibility/dsl.rb +310 -191
  7. data/lib/accessibility/enumerators.rb +9 -8
  8. data/lib/accessibility/errors.rb +7 -8
  9. data/lib/accessibility/factory.rb +16 -9
  10. data/lib/accessibility/graph.rb +68 -22
  11. data/lib/accessibility/highlighter.rb +86 -0
  12. data/lib/accessibility/pp_inspector.rb +4 -4
  13. data/lib/accessibility/qualifier.rb +11 -9
  14. data/lib/accessibility/string.rb +12 -4
  15. data/lib/accessibility/translator.rb +19 -10
  16. data/lib/accessibility/version.rb +3 -1
  17. data/lib/accessibility.rb +42 -17
  18. data/lib/ax/application.rb +90 -30
  19. data/lib/ax/button.rb +5 -2
  20. data/lib/ax/element.rb +133 -149
  21. data/lib/ax/pop_up_button.rb +12 -0
  22. data/lib/ax/radio_button.rb +5 -2
  23. data/lib/ax/row.rb +2 -2
  24. data/lib/ax/static_text.rb +5 -2
  25. data/lib/ax/systemwide.rb +24 -12
  26. data/lib/ax_elements/awesome_print.rb +13 -0
  27. data/lib/ax_elements/exception_workaround.rb +5 -0
  28. data/lib/ax_elements/nsarray_compat.rb +1 -0
  29. data/lib/ax_elements.rb +2 -1
  30. data/lib/minitest/ax_elements.rb +60 -4
  31. data/lib/mouse.rb +47 -20
  32. data/lib/rspec/expectations/ax_elements.rb +180 -88
  33. data/rakelib/doc.rake +7 -0
  34. data/test/helper.rb +2 -1
  35. data/test/integration/accessibility/test_dsl.rb +126 -18
  36. data/test/integration/accessibility/test_errors.rb +1 -1
  37. data/test/integration/ax/test_element.rb +17 -0
  38. data/test/integration/minitest/test_ax_elements.rb +33 -38
  39. data/test/integration/rspec/expectations/test_ax_elements.rb +68 -19
  40. data/test/sanity/accessibility/test_core.rb +45 -37
  41. data/test/sanity/accessibility/test_highlighter.rb +56 -0
  42. data/test/sanity/ax/test_application.rb +8 -0
  43. data/test/sanity/ax/test_element.rb +7 -3
  44. data/test/sanity/minitest/test_ax_elements.rb +2 -0
  45. data/test/sanity/rspec/expectations/test_ax_elements.rb +3 -0
  46. data/test/sanity/test_accessibility.rb +9 -0
  47. data/test/sanity/test_mouse.rb +2 -2
  48. metadata +11 -38
  49. data/docs/AccessibilityTips.markdown +0 -119
  50. data/docs/Acting.markdown +0 -340
  51. data/docs/Debugging.markdown +0 -165
  52. data/docs/Inspecting.markdown +0 -261
  53. data/docs/KeyboardEvents.markdown +0 -122
  54. data/docs/NewBehaviour.markdown +0 -151
  55. data/docs/Notifications.markdown +0 -271
  56. data/docs/Searching.markdown +0 -250
  57. data/docs/TestingExtensions.markdown +0 -52
  58. data/docs/images/all_the_buttons.jpg +0 -0
  59. data/docs/images/next_version.png +0 -0
  60. data/docs/images/ui_hierarchy.dot +0 -34
  61. data/docs/images/ui_hierarchy.png +0 -0
  62. data/lib/accessibility/debug.rb +0 -164
  63. data/test/integration/accessibility/test_debug.rb +0 -44
  64. 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
- # The main AXElements namespace.
5
- module Accessibility
6
- class << self
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 _HUGE_ hack in cases where the app is not
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 return nil, eventually.
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
- 10.times do
37
- app = NSRunningApplication.runningApplicationsWithBundleIdentifier(bundle).first
38
- return AX::Application.new(app) if app
39
- launch_application bundle
40
- sleep 2
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 Use {AX::Application.new} instead.
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
@@ -2,28 +2,24 @@ require 'ax/element'
2
2
  require 'accessibility/string'
3
3
 
4
4
  ##
5
- # Some additional constructors and conveniences for Application objects.
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` class.
10
+ # the `NSRunningApplication` and `NSBundle` classes.
9
11
  class AX::Application < AX::Element
10
12
  include Accessibility::String
11
13
 
12
14
  ##
13
- # @private
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#set_focus} case.
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 #focused? method, but might make more sense to use
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:to:)
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 `self`, the control in the app that currently
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
- # {file:docs/KeyboardEvents.markdown Keyboard} documentation.
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
- # @todo doc and cleanup
158
- def keydown key
159
- @ref.post [[EventGenerator::CUSTOM[key], true]]
160
- true
161
- end
162
-
163
- # @todo doc and cleanup
164
- def keyup key
165
- @ref.post [[EventGenerator::CUSTOM[key], false]]
166
- true
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 `self` that is present at point given.
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
- return super unless other.kind_of? NSString
17
- return attribute(:title) == other
16
+ if other.kind_of? NSString
17
+ attribute(:title) == other
18
+ else
19
+ super
20
+ end
18
21
  end
19
22
 
20
23
  end