AXElements 0.6.0beta2 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/.yardopts +1 -2
  2. data/README.markdown +152 -88
  3. data/Rakefile +8 -103
  4. data/docs/Debugging.markdown +9 -2
  5. data/docs/KeyboardEvents.markdown +114 -49
  6. data/docs/Setting.markdown +1 -0
  7. data/docs/images/next_version.png +0 -0
  8. data/ext/accessibility/key_coder/extconf.rb +22 -0
  9. data/ext/accessibility/key_coder/key_coder.c +113 -0
  10. data/lib/AXElements.rb +2 -0
  11. data/lib/accessibility/core.rb +897 -0
  12. data/lib/accessibility/debug.rb +168 -0
  13. data/lib/accessibility/dsl.rb +697 -0
  14. data/lib/accessibility/enumerators.rb +104 -0
  15. data/lib/accessibility/errors.rb +32 -0
  16. data/lib/accessibility/factory.rb +153 -0
  17. data/lib/accessibility/graph.rb +150 -0
  18. data/lib/{ax_elements/inspector.rb → accessibility/pp_inspector.rb} +39 -28
  19. data/lib/accessibility/qualifier.rb +158 -0
  20. data/lib/accessibility/string.rb +494 -0
  21. data/lib/accessibility/translator.rb +178 -0
  22. data/lib/accessibility/version.rb +7 -0
  23. data/lib/accessibility.rb +79 -0
  24. data/lib/ax/application.rb +234 -0
  25. data/lib/{ax_elements/elements → ax}/button.rb +2 -0
  26. data/lib/ax/element.rb +518 -0
  27. data/lib/{ax_elements/elements → ax}/radio_button.rb +2 -0
  28. data/lib/ax/row.rb +37 -0
  29. data/lib/{ax_elements/elements → ax}/static_text.rb +2 -0
  30. data/lib/ax/systemwide.rb +86 -0
  31. data/lib/ax_elements/awesome_print.rb +25 -0
  32. data/lib/ax_elements/exception_workaround.rb +8 -0
  33. data/lib/ax_elements/nsarray_compat.rb +64 -0
  34. data/lib/ax_elements/vendor/inflection_data.rb +65 -0
  35. data/lib/ax_elements/vendor/inflections.rb +172 -0
  36. data/lib/ax_elements/vendor/inflector.rb +306 -0
  37. data/lib/ax_elements.rb +14 -25
  38. data/lib/minitest/ax_elements.rb +112 -12
  39. data/lib/mouse.rb +72 -46
  40. data/lib/rspec/expectations/ax_elements.rb +133 -6
  41. data/rakelib/doc.rake +13 -0
  42. data/rakelib/ext.rake +61 -0
  43. data/rakelib/gem.rake +28 -0
  44. data/rakelib/test.rake +53 -0
  45. data/test/helper.rb +11 -97
  46. data/test/integration/accessibility/test_core.rb +18 -0
  47. data/test/integration/accessibility/test_debug.rb +44 -0
  48. data/test/integration/accessibility/test_dsl.rb +225 -0
  49. data/test/integration/accessibility/test_enumerators.rb +122 -0
  50. data/test/integration/accessibility/test_errors.rb +38 -0
  51. data/test/integration/accessibility/test_notifications.rb +22 -0
  52. data/test/integration/accessibility/test_qualifier.rb +148 -0
  53. data/test/integration/ax/test_application.rb +56 -0
  54. data/test/integration/ax/test_element.rb +46 -0
  55. data/test/integration/ax/test_row.rb +23 -0
  56. data/test/integration/ax_elements/test_nsarray_compat.rb +43 -0
  57. data/test/integration/minitest/test_ax_elements.rb +98 -0
  58. data/test/integration/rspec/expectations/test_ax_elements.rb +58 -0
  59. data/test/integration/test_mouse.rb +35 -0
  60. data/test/sanity/accessibility/test_core.rb +553 -0
  61. data/test/sanity/accessibility/test_debug.rb +63 -0
  62. data/test/sanity/accessibility/test_dsl.rb +75 -0
  63. data/test/sanity/accessibility/test_errors.rb +10 -0
  64. data/test/sanity/accessibility/test_factory.rb +88 -0
  65. data/test/sanity/accessibility/test_pp_inspector.rb +110 -0
  66. data/test/sanity/accessibility/test_qualifier.rb +13 -0
  67. data/test/sanity/accessibility/test_string.rb +238 -0
  68. data/test/sanity/accessibility/test_translator.rb +145 -0
  69. data/test/sanity/ax/test_application.rb +90 -0
  70. data/test/sanity/ax/test_element.rb +80 -0
  71. data/test/sanity/ax/test_systemwide.rb +66 -0
  72. data/test/sanity/ax_elements/test_nsarray_compat.rb +16 -0
  73. data/test/sanity/ax_elements/test_nsobject_inspect.rb +11 -0
  74. data/test/sanity/minitest/test_ax_elements.rb +15 -0
  75. data/test/sanity/rspec/expectations/test_ax_elements.rb +12 -0
  76. data/test/sanity/test_ax_elements.rb +10 -0
  77. data/test/sanity/test_mouse.rb +19 -0
  78. metadata +111 -93
  79. data/LICENSE.txt +0 -25
  80. data/ext/key_coder/extconf.rb +0 -6
  81. data/ext/key_coder/key_coder.m +0 -77
  82. data/lib/ax_elements/accessibility/enumerators.rb +0 -104
  83. data/lib/ax_elements/accessibility/graph.rb +0 -118
  84. data/lib/ax_elements/accessibility/language.rb +0 -347
  85. data/lib/ax_elements/accessibility/qualifier.rb +0 -73
  86. data/lib/ax_elements/accessibility.rb +0 -166
  87. data/lib/ax_elements/core.rb +0 -541
  88. data/lib/ax_elements/element.rb +0 -593
  89. data/lib/ax_elements/elements/application.rb +0 -88
  90. data/lib/ax_elements/elements/row.rb +0 -30
  91. data/lib/ax_elements/elements/systemwide.rb +0 -46
  92. data/lib/ax_elements/macruby_extensions.rb +0 -255
  93. data/lib/ax_elements/notification.rb +0 -37
  94. data/lib/ax_elements/version.rb +0 -9
  95. data/test/elements/test_application.rb +0 -72
  96. data/test/elements/test_row.rb +0 -27
  97. data/test/elements/test_systemwide.rb +0 -38
  98. data/test/test_accessibility.rb +0 -127
  99. data/test/test_blankness.rb +0 -26
  100. data/test/test_core.rb +0 -448
  101. data/test/test_element.rb +0 -939
  102. data/test/test_enumerators.rb +0 -81
  103. data/test/test_inspector.rb +0 -130
  104. data/test/test_language.rb +0 -157
  105. data/test/test_macruby_extensions.rb +0 -303
  106. data/test/test_mouse.rb +0 -5
  107. data/test/test_search_semantics.rb +0 -143
data/test/test_core.rb DELETED
@@ -1,448 +0,0 @@
1
- class TestCore < TestAX
2
-
3
- WINDOW = attribute_for REF, KAXMainWindowAttribute
4
-
5
- def child name
6
- children_for(WINDOW).find do |item|
7
- attribute_for(item, KAXRoleAttribute) == name
8
- end
9
- end
10
-
11
- def slider
12
- @@slider ||= child KAXSliderRole
13
- end
14
-
15
- def check_box
16
- @@check_box ||= child KAXCheckBoxRole
17
- end
18
-
19
- def search_box
20
- @@search_box ||= child KAXTextFieldRole
21
- end
22
-
23
- def button
24
- @@button ||= child KAXButtonRole
25
- end
26
-
27
- def static_text
28
- @@static_text ||= child KAXStaticTextRole
29
- end
30
-
31
- def yes_button
32
- @@yes_button ||= children_for(WINDOW).find do |item|
33
- if attribute_for(item, KAXRoleAttribute) == KAXButtonRole
34
- attribute_for(item, KAXTitleAttribute) == 'Yes'
35
- end
36
- end
37
- end
38
-
39
- end
40
-
41
-
42
- ##
43
- # AFAICT every accessibility object **MUST** have attributes, so
44
- # there are no tests to check what happens when they do not exist;
45
- # though I am quite sure that AXElements will explode.
46
- class TestAttrsOfElement < TestCore
47
-
48
- def attrs
49
- @@attrs ||= AX.attrs_of_element REF
50
- end
51
-
52
- def test_returns_array_of_strings
53
- assert_instance_of String, attrs.first
54
- end
55
-
56
- def test_make_sure_certain_attributes_are_provided
57
- assert_includes attrs, KAXRoleAttribute
58
- assert_includes attrs, KAXRoleDescriptionAttribute
59
- end
60
-
61
- def test_other_attributes_that_the_app_should_have
62
- assert_includes attrs, KAXChildrenAttribute
63
- assert_includes attrs, KAXTitleAttribute
64
- assert_includes attrs, KAXMenuBarAttribute
65
- end
66
-
67
- end
68
-
69
-
70
- class TestAttrCountOfElement < TestCore
71
-
72
- def test_returns_number_of_children
73
- assert_equal children_for(REF).size, AX.attr_count_of_element(REF, KAXChildrenAttribute)
74
- assert_equal 0, AX.attr_count_of_element(button, KAXChildrenAttribute)
75
- end
76
-
77
- # @todo there are things we care about?
78
-
79
- end
80
-
81
-
82
- class TestAttrOfElementGetsCorrectAttribute < TestCore
83
-
84
- def test_title_is_title
85
- assert_equal 'AXElementsTester', AX.attr_of_element(REF, KAXTitleAttribute)
86
- end
87
-
88
- # @note the app gives CGRectZero in screen coordinates, and then they are
89
- # flipped for us to, so we need to flip them again
90
- def test_custom_lol_is_rect
91
- point = CGPointZero.dup
92
- point.y = NSScreen.mainScreen.frame.size.height
93
- expected_rect = CGRect.new point, CGSizeZero
94
- ret = AX.attr_of_element WINDOW, 'AXLol'
95
- ptr = Pointer.new CGRect.type
96
- AXValueGetValue(ret, 3, ptr)
97
- assert_equal expected_rect, ptr[0]
98
- end
99
-
100
- def test_hidden_is_hidden_value
101
- assert_equal false, AX.attr_of_element(REF, KAXHiddenAttribute)
102
- end
103
-
104
- end
105
-
106
-
107
- class TestAttrOfElementErrors < TestCore
108
- include LoggingCapture
109
-
110
- def test_logs_message_for_non_existant_attributes
111
- with_logging do AX.attr_of_element REF, 'MADEUPATTRIBUTE' end
112
- assert_match /#{KAXErrorAttributeUnsupported}/, @log_output.string
113
- end
114
-
115
- end
116
-
117
-
118
- class TestAttrOfElementWritable < TestCore
119
-
120
- def test_true_for_writable_attribute
121
- assert AX.attr_of_element_writable?(WINDOW, KAXMainAttribute)
122
- end
123
-
124
- def test_false_for_non_writable_attribute
125
- refute AX.attr_of_element_writable?(REF, KAXTitleAttribute)
126
- end
127
-
128
- def test_false_for_non_existante_attribute
129
- refute AX.attr_of_element_writable?(REF, 'FAKE')
130
- end
131
-
132
- end
133
-
134
-
135
- class TestSetAttrOfElement < TestCore
136
-
137
- def test_set_a_slider
138
- [25, 75, 50].each do |value|
139
- AX.set_attr_of_element slider, KAXValueAttribute, value
140
- assert_equal value, value_for(slider)
141
- end
142
- end
143
-
144
- def test_set_a_text_field
145
- [Time.now.to_s, ''].each do |value|
146
- AX.set_attr_of_element(search_box, KAXValueAttribute, value)
147
- assert_equal value, value_for(search_box)
148
- end
149
- end
150
-
151
- end
152
-
153
-
154
- class TestActionsOfElement < TestCore
155
-
156
- def test_works_when_there_are_no_actions
157
- assert_empty AX.actions_of_element REF
158
- end
159
-
160
- def test_returns_array_of_strings
161
- assert_instance_of String, AX.actions_of_element(yes_button).first
162
- end
163
-
164
- def test_make_sure_certain_actions_are_present
165
- actions = AX.actions_of_element slider
166
- assert_includes actions, KAXIncrementAction
167
- assert_includes actions, KAXDecrementAction
168
- end
169
-
170
- end
171
-
172
-
173
- class TestActionOfElement < TestCore
174
-
175
- def test_check_a_check_box
176
- 2.times do # twice so it should be back where it started
177
- value = value_for check_box
178
- AX.action_of_element check_box, KAXPressAction
179
- refute_equal value, value_for(check_box)
180
- end
181
- end
182
-
183
- def test_sliding_the_slider
184
- value = attribute_for slider, KAXValueAttribute
185
- AX.action_of_element slider, KAXIncrementAction
186
- assert attribute_for(slider, KAXValueAttribute) > value
187
-
188
- value = attribute_for slider, KAXValueAttribute
189
- AX.action_of_element slider, KAXDecrementAction
190
- assert attribute_for(slider, KAXValueAttribute) < value
191
- end
192
-
193
- end
194
-
195
-
196
- class TestKeyboardAction < TestCore
197
-
198
- SYSTEM = AXUIElementCreateSystemWide()
199
-
200
- def post string
201
- set_attribute_for search_box, KAXFocusedAttribute, true
202
- AX.keyboard_action REF, string
203
- # sleep 0.01
204
- assert_equal string, attribute_for(search_box, KAXValueAttribute)
205
- ensure
206
- button = children_for(search_box).find { |x| x.class == AX::Button }
207
- action_for button, KAXPressAction
208
- end
209
-
210
- def test_uppercase_letters
211
- post 'HELLO, WORLD'
212
- end
213
-
214
- def test_numbers
215
- post '42'
216
- end
217
-
218
- def test_letters
219
- post 'the cake is a lie'
220
- end
221
-
222
- def test_escape_sequences
223
- post "\s"
224
- end
225
-
226
- end
227
-
228
-
229
- class TestAXParamAttrsOfElement < TestCore
230
-
231
- def test_empty_for_dock
232
- assert_empty AX.param_attrs_of_element REF
233
- end
234
-
235
- def test_not_empty_for_search_field
236
- assert_includes AX.param_attrs_of_element(static_text), KAXStringForRangeParameterizedAttribute
237
- assert_includes AX.param_attrs_of_element(static_text), KAXLineForIndexParameterizedAttribute
238
- assert_includes AX.param_attrs_of_element(static_text), KAXBoundsForRangeParameterizedAttribute
239
- end
240
-
241
- end
242
-
243
-
244
- class TestAXParamAttrOfElement < TestCore
245
-
246
- def test_contains_proper_info
247
- attr = AX.param_attr_of_element(static_text,
248
- KAXStringForRangeParameterizedAttribute,
249
- CFRange.new(0, 5).to_axvalue)
250
- assert_equal 'AXEle', attr
251
- end
252
-
253
- def test_get_attributed_string
254
- attr = AX.param_attr_of_element(static_text,
255
- # this is why we need name tranformers
256
- KAXAttributedStringForRangeParameterizedAttribute,
257
- CFRange.new(0, 5).to_axvalue)
258
- assert_kind_of NSAttributedString, attr
259
- assert_equal 'AXEle', attr.string
260
- end
261
-
262
- end
263
-
264
-
265
- # be very careful about cleaning up state after notification tests
266
- class TestAXNotifications < TestCore
267
-
268
- # custom notification name
269
- CHEEZBURGER = 'Cheezburger'
270
-
271
- SHORT_TIMEOUT = 0.1
272
- TIMEOUT = 1.0
273
-
274
- def radio_group
275
- @radio_group ||= child KAXRadioGroupRole
276
- end
277
-
278
- def radio_gaga
279
- @@gaga ||= children_for(radio_group).find do |item|
280
- attribute_for(item, KAXTitleAttribute) == 'Gaga'
281
- end
282
- end
283
-
284
- def radio_flyer
285
- @@flyer ||= children_for(radio_group).find do |item|
286
- attribute_for(item, KAXTitleAttribute) == 'Flyer'
287
- end
288
- end
289
-
290
- def teardown
291
- if attribute_for(radio_gaga, KAXValueAttribute) == 1
292
- action_for radio_flyer, KAXPressAction
293
- end
294
- AX.unregister_notifs
295
- end
296
-
297
- def test_break_if_ignoring
298
- AX.register_for_notif(radio_gaga, KAXValueChangedNotification) { |_,_| true }
299
- AX.instance_variable_set :@ignore_notifs, true
300
-
301
- action_for radio_gaga, KAXPressAction
302
-
303
- start = Time.now
304
- refute AX.wait_for_notif(SHORT_TIMEOUT)
305
- done = Time.now
306
-
307
- refute_in_delta done, start, SHORT_TIMEOUT
308
- end
309
-
310
- def test_break_if_block_returns_falsey
311
- AX.register_for_notif(radio_gaga, KAXValueChangedNotification) { |_,_| false }
312
- action_for radio_gaga, KAXPressAction
313
-
314
- start = Time.now
315
- refute AX.wait_for_notif(SHORT_TIMEOUT)
316
- done = Time.now
317
-
318
- refute_in_delta done, start, SHORT_TIMEOUT
319
- end
320
-
321
- def test_stops_if_block_returns_truthy
322
- AX.register_for_notif(radio_gaga, KAXValueChangedNotification) { |_,_| true }
323
- action_for radio_gaga, KAXPressAction
324
- assert AX.wait_for_notif(SHORT_TIMEOUT)
325
- end
326
-
327
- def test_returns_triple
328
- ret = AX.register_for_notif(radio_gaga, KAXValueChangedNotification) { |_,_| true }
329
- assert_equal AXObserverGetTypeID(), CFGetTypeID(ret[0])
330
- assert_equal radio_gaga, ret[1]
331
- assert_equal KAXValueChangedNotification, ret[2]
332
- end
333
-
334
- def test_wait_stops_waiting_when_notif_received
335
- AX.register_for_notif(radio_gaga, KAXValueChangedNotification) { |_,_| true }
336
- action_for radio_gaga, KAXPressAction
337
-
338
- start = Time.now
339
- AX.wait_for_notif(SHORT_TIMEOUT)
340
- done = Time.now
341
-
342
- msg = 'Might fail if your machine is under heavy load'
343
- assert_in_delta done, start, 0.02, msg
344
- end
345
-
346
- def test_works_with_custom_notifs
347
- got_callback = false
348
- AX.register_for_notif(yes_button, CHEEZBURGER) do |_,_|
349
- got_callback = true
350
- end
351
- action_for yes_button, KAXPressAction
352
- AX.wait_for_notif(SHORT_TIMEOUT)
353
- assert got_callback, 'did not get callback'
354
- end
355
-
356
- def test_unregistering_clears_notif
357
- AX.register_for_notif(yes_button, CHEEZBURGER) { |_,_| true }
358
- action_for yes_button, KAXPressAction
359
- end
360
-
361
- def test_unregistering_noops_if_not_registered
362
- assert_block do
363
- AX.unregister_notifs
364
- AX.unregister_notifs
365
- AX.unregister_notifs
366
- end
367
- end
368
-
369
- def test_unregistering_sets_ignore_to_true
370
- AX.register_for_notif(yes_button, CHEEZBURGER) { |_,_| true }
371
- refute AX.instance_variable_get(:@ignore_notifs)
372
- AX.unregister_notifs
373
- assert AX.instance_variable_get(:@ignore_notifs)
374
- end
375
-
376
- def test_listening_to_app_catches_everything
377
- got_callback = false
378
- AX.register_for_notif(REF, KAXValueChangedNotification) do |_,_|
379
- got_callback = true
380
- end
381
- action_for radio_gaga, KAXPressAction
382
- AX.wait_for_notif(TIMEOUT)
383
- assert got_callback
384
- end
385
-
386
- end
387
-
388
-
389
- class TestElementAtPosition < TestCore
390
-
391
- def test_returns_a_button_when_i_give_the_coordinates_of_a_button
392
- point = attribute_for button, KAXPositionAttribute
393
- ptr = Pointer.new CGPoint.type
394
- AXValueGetValue(point, KAXValueCGPointType, ptr)
395
- point = ptr[0]
396
- element = AX.element_at_point(*point.to_a)
397
- assert_equal button, element
398
- end
399
-
400
- end
401
-
402
-
403
- class TestAXPIDThings < TestCore
404
-
405
- def test_app_for_pid_returns_raw_element
406
- ret = AX.application_for_pid PID
407
- role = attribute_for ret, KAXRoleAttribute
408
- assert_equal KAXApplicationRole, role
409
- end
410
-
411
- def test_app_for_pid_raises_if_pid_is_zero
412
- assert_raises ArgumentError do
413
- AX.application_for_pid 0
414
- end
415
- assert_raises ArgumentError do
416
- AX.application_for_pid -1
417
- end
418
- end
419
-
420
- def test_pid_for_app
421
- assert_equal PID, AX.pid_of_element(REF)
422
- end
423
-
424
- def test_pid_for_dock_app_is_docks_pid
425
- assert_equal PID, AX.pid_of_element(WINDOW)
426
- end
427
-
428
- end
429
-
430
-
431
- # I'd prefer to not to be directly calling the log method bypassing
432
- # the fact that it is a private method
433
- class TestLogAXCall < TestCore
434
- include LoggingCapture
435
-
436
- def test_looks_up_code_properly
437
- with_logging { AX.send(:log_error, REF, KAXErrorAPIDisabled) }
438
- assert_match /API Disabled/, @log_output.string
439
- with_logging { AX.send(:log_error, REF, KAXErrorNotImplemented) }
440
- assert_match /Not Implemented/, @log_output.string
441
- end
442
-
443
- def test_handles_unknown_error_codes
444
- with_logging { AX.send(:log_error, REF, 1234567) }
445
- assert_match /UNKNOWN ERROR CODE/, @log_output.string
446
- end
447
-
448
- end