AXElements 0.7.8 → 0.8.0

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 (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