AXElements 0.6.0beta2 → 0.7.5

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 (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
@@ -0,0 +1,553 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'test/helper'
4
+ require 'accessibility/core'
5
+
6
+
7
+ class TestAccessibilityCore < MiniTest::Unit::TestCase
8
+
9
+ def window
10
+ @@window ||= REF.attribute(KAXMainWindowAttribute)
11
+ end
12
+
13
+ def child name
14
+ window.children.find { |item|
15
+ (block_given? ? yield(item) : true) if item.role == name
16
+ }
17
+ end
18
+
19
+ def slider; @@slider ||= child KAXSliderRole; end
20
+ def check_box; @@check_box ||= child KAXCheckBoxRole; end
21
+ def pop_up; @@pop_up ||= child KAXPopUpButtonRole; end
22
+ def search_box; @@search_box ||= child KAXTextFieldRole; end
23
+ def static_text; @@static_text ||= child(KAXStaticTextRole) { |x| x.value.match /My Little Pony/ } end
24
+ def yes_button; @@yes_button ||= child(KAXButtonRole) { |x| x.attribute(KAXTitleAttribute) == 'Yes' } end
25
+ def bye_button; @@bye_button ||= child(KAXButtonRole) { |x| x.attribute(KAXTitleAttribute) == 'Bye!' } end
26
+ def no_button; @@no_button ||= child(KAXButtonRole) { |x| x.attribute(KAXTitleAttribute) == 'No' } end
27
+ def web_area; @@web_area ||= child("AXScrollArea") { |x| x.attribute("AXDescription" ) == 'Test Web Area' }.children.first end
28
+ def text_area; @@text_area ||= child("AXScrollArea") { |x| x.attributes.include?("AXIdentifier") && x.attribute("AXIdentifier") == 'Text Area' }.children.first end
29
+
30
+ def invalid_ref
31
+ bye_button # guarantee that it is cached
32
+ @@dead ||= no_button.perform KAXPressAction
33
+ bye_button
34
+ end
35
+
36
+ def app
37
+ @@app ||= Regexp.new(Regexp.escape(REF.inspect))
38
+ end
39
+
40
+ def assert_error args, should_raise: klass, with_fragments: msgs
41
+ e = assert_raises(klass) { (@derp || REF).handle_error *args }
42
+ assert_match /test_core.rb:41/, e.backtrace.first unless RUNNING_COMPILED
43
+ msgs.each { |msg| assert_match msg, e.message }
44
+ end
45
+
46
+
47
+
48
+ ##
49
+ # AFAICT every accessibility object **MUST** have attributes, so
50
+ # there are no tests to check what happens when they do not exist;
51
+ # though I am quite sure that AXElements will raise an exception.
52
+
53
+ def test_attributes
54
+ attrs = REF.attributes
55
+ assert_includes attrs, KAXRoleAttribute
56
+ assert_includes attrs, KAXChildrenAttribute
57
+ assert_includes attrs, KAXTitleAttribute
58
+ assert_includes attrs, KAXMenuBarAttribute
59
+ end
60
+
61
+ def test_attributes_is_empty_for_dead_elements
62
+ assert_empty invalid_ref.attributes
63
+ end
64
+
65
+ def test_attribute
66
+ assert_equal 'AXElementsTester', window.attribute(KAXTitleAttribute )
67
+ assert_equal false, window.attribute(KAXFocusedAttribute)
68
+ assert_equal CGSizeMake(555,483), window.attribute(KAXSizeAttribute )
69
+ assert_equal REF, window.attribute(KAXParentAttribute )
70
+ assert_equal 10..19, window.attribute("AXPie" )
71
+ end
72
+
73
+ def test_attribute_is_nil_when_no_value
74
+ assert_nil window.attribute(KAXGrowAreaAttribute)
75
+ end
76
+
77
+ def test_attribute_when_dead
78
+ assert_nil invalid_ref.attribute(KAXRoleAttribute)
79
+ assert_empty invalid_ref.attribute(KAXChildrenAttribute)
80
+ end
81
+
82
+ def test_attribute_handles_errors
83
+ assert_raises(ArgumentError) { REF.attribute 'MADEUPATTRIBUTE' }
84
+ end
85
+
86
+ def test_role
87
+ assert_equal KAXApplicationRole, REF.role
88
+ end
89
+
90
+ def test_subrole
91
+ assert_equal KAXStandardWindowSubrole, window.subrole
92
+ assert_nil web_area.subrole
93
+ end
94
+
95
+ def test_children
96
+ assert_equal REF.attribute(KAXChildrenAttribute), REF.children
97
+ assert_equal slider.attribute(KAXChildrenAttribute), slider.children
98
+ end
99
+
100
+ def test_value
101
+ assert_equal check_box.attribute(KAXValueAttribute), check_box.value
102
+ assert_equal slider.attribute(KAXValueAttribute), slider.value
103
+ end
104
+
105
+ def test_size_of
106
+ assert_equal REF.children.size, REF.size_of(KAXChildrenAttribute)
107
+ assert_equal 0, pop_up.size_of(KAXChildrenAttribute)
108
+ end
109
+
110
+ def test_size_of_0_for_dead_element
111
+ assert_equal 0, invalid_ref.size_of(KAXChildrenAttribute)
112
+ end
113
+
114
+ def test_writable
115
+ refute REF.writable? KAXTitleAttribute
116
+ assert window.writable? KAXMainAttribute
117
+ end
118
+
119
+ def test_writable_false_for_dead_cases
120
+ refute invalid_ref.writable? KAXRoleAttribute
121
+ end
122
+
123
+ def test_writable_handles_errors
124
+ assert_raises(ArgumentError) { REF.writable? 'FAKE' }
125
+ end
126
+
127
+ def test_set_number
128
+ [25, 75, 50].each do |number|
129
+ assert_equal number, slider.set(KAXValueAttribute, number)
130
+ assert_equal number, slider.value
131
+ end
132
+ end
133
+
134
+ def test_set_string
135
+ [Time.now.to_s, ''].each do |string|
136
+ assert_equal string, search_box.set(KAXValueAttribute, string)
137
+ assert_equal string, search_box.value
138
+ end
139
+ end
140
+
141
+ def test_set_wrapped
142
+ text_area.set KAXValueAttribute, 'hey-o'
143
+
144
+ text_area.set KAXSelectedTextRangeAttribute, 0..3
145
+ assert_equal 0..3, text_area.attribute(KAXSelectedTextRangeAttribute)
146
+
147
+ text_area.set KAXSelectedTextRangeAttribute, 1...4
148
+ assert_equal 1..3, text_area.attribute(KAXSelectedTextRangeAttribute)
149
+ ensure
150
+ text_area.set KAXValueAttribute, ''
151
+ end
152
+
153
+ def test_set_attr_handles_errors
154
+ assert_raises(ArgumentError) { REF.set 'FAKE', true }
155
+ end
156
+
157
+ def test_parameterized_attributes
158
+ assert_empty REF.parameterized_attributes
159
+
160
+ attrs = static_text.parameterized_attributes
161
+ assert_includes attrs, KAXStringForRangeParameterizedAttribute
162
+ assert_includes attrs, KAXLineForIndexParameterizedAttribute
163
+ assert_includes attrs, KAXBoundsForRangeParameterizedAttribute
164
+ end
165
+
166
+ def test_parameterized_attributes_empty_for_dead_elements
167
+ assert_empty invalid_ref.parameterized_attributes
168
+ end
169
+
170
+ def test_attribute_for_parameter
171
+ expected = 'My Li'
172
+
173
+ attr = static_text.attribute KAXStringForRangeParameterizedAttribute,
174
+ for_parameter: 0..4
175
+ assert_equal expected, attr
176
+
177
+ attr = static_text.attribute KAXAttributedStringForRangeParameterizedAttribute,
178
+ for_parameter: 0..4
179
+ assert_equal expected, attr.string
180
+ end
181
+
182
+ def test_attribute_for_parameter_handles_dead_elements_and_no_value
183
+ assert_nil invalid_ref.attribute(KAXStringForRangeParameterizedAttribute, for_parameter: 0..0)
184
+
185
+ # Should add a test case to test the no value case, but it will have
186
+ # to be fabricated in the test app.
187
+ end
188
+
189
+ def test_attribute_for_parameter_handles_errors
190
+ assert_raises(ArgumentError) {
191
+ REF.attribute(KAXStringForRangeParameterizedAttribute, for_parameter: 0..1)
192
+ }
193
+ end
194
+
195
+ def test_action_names
196
+ assert_empty REF.actions
197
+ assert_equal [KAXPressAction], yes_button.actions
198
+ end
199
+
200
+ def test_perform_action
201
+ 2.times do # twice so it should be back where it started
202
+ val = check_box.value
203
+ check_box.perform KAXPressAction
204
+ refute_equal val, check_box.value
205
+ end
206
+
207
+ val = slider.value
208
+ slider.perform KAXIncrementAction
209
+ assert slider.value > val
210
+
211
+ val = slider.value
212
+ slider.perform KAXDecrementAction
213
+ assert slider.value < val
214
+ end
215
+
216
+ def test_action_handles_errors
217
+ assert_raises(ArgumentError) { REF.perform nil }
218
+ end
219
+
220
+ ##
221
+ # The keyboard simulation stuff is a bit weird...
222
+
223
+ def test_post_events_to
224
+ events = [[0x56,true], [0x56,false], [0x54,true], [0x54,false]]
225
+ string = '42'
226
+
227
+ search_box.set KAXFocusedAttribute, true
228
+ REF.post events
229
+
230
+ assert_equal string, search_box.value
231
+
232
+ ensure # reset for next test
233
+ search_box.set KAXValueAttribute, ''
234
+ end
235
+
236
+ ##
237
+ # Kind of a bad test right now because the method itself
238
+ # lacks certain functionality that needs to be added...
239
+
240
+ def test_element_at
241
+ point = no_button.attribute(KAXPositionAttribute)
242
+
243
+ element = REF.element_at point
244
+ assert_equal no_button, element, "#{no_button.inspect} and #{element.inspect}"
245
+
246
+ element = REF.system_wide.element_at point
247
+ assert_equal no_button, element, "#{no_button.inspect} and #{element.inspect}"
248
+
249
+ assert_respond_to element, :role
250
+ end
251
+
252
+ # def test_element_at_returns_nil_on_empty_space
253
+ # skip 'How do I guarantee an empty space on screen?'
254
+ # # btw, [0,0] returns something
255
+ # end
256
+
257
+ def test_application_for
258
+ # @note Should call CFEqual() under the hood, which is what we want
259
+ assert_equal REF, REF.application_for(PID)
260
+ end
261
+
262
+ def test_application_for_raises_for_bad_pid
263
+ assert_raises(ArgumentError) { REF.application_for 0 }
264
+ end
265
+
266
+ def test_observer
267
+ assert_equal AXObserverGetTypeID(), CFGetTypeID(REF.observer { })
268
+ end
269
+
270
+ def test_observer_handles_errors
271
+ assert_raises(ArgumentError) { REF.observer }
272
+ end
273
+
274
+ def test_run_loop_source_for
275
+ obsrvr = REF.observer { |_,_,_| }
276
+ assert_equal CFRunLoopSourceGetTypeID(), CFGetTypeID(REF.run_loop_source_for(obsrvr))
277
+ end
278
+
279
+ # # more than meets the eye
280
+ def test_notification_registration_and_unregistration
281
+ obsrvr = REF.observer { |_,_,_| }
282
+ assert REF.register(obsrvr, to_receive: KAXWindowCreatedNotification)
283
+ assert REF.unregister(obsrvr, from_receiving: KAXWindowCreatedNotification)
284
+ end
285
+
286
+ # integration-y
287
+ def test_notification_registers_everything_correctly
288
+ obsrvr = REF.observer do |observer, element, notif|
289
+ @notif_triple = [observer, element, notif]
290
+ end
291
+ REF.register observer, to_receive: 'Cheezburger'
292
+ source = REF.run_loop_source_for observer
293
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), source, KCFRunLoopDefaultMode)
294
+
295
+ yes_button.perform KAXPressAction
296
+ yes_button.spin_run_loop
297
+
298
+ assert_equal [obsrvr, yes_button, 'Cheezburger'], @notif_triple
299
+
300
+ ensure
301
+ return unless source
302
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, KCFRunLoopDefaultMode)
303
+ end
304
+
305
+ def test_register_handles_errors
306
+ assert_raises(ArgumentError) {
307
+ REF.register(nil, to_receive: KAXWindowCreatedNotification)
308
+ }
309
+ assert_raises(ArgumentError) {
310
+ obsrvr = REF.observer { |_,_,_| }
311
+ REF.register(obsrvr, to_receive: nil)
312
+ }
313
+ end
314
+
315
+ def test_unregister_handles_errors
316
+ assert_raises(ArgumentError) {
317
+ REF.unregister(nil, from_receiving: KAXWindowCreatedNotification)
318
+ }
319
+ assert_raises(ArgumentError) {
320
+ obsrvr = REF.observer { |_,_,_| }
321
+ REF.unregister(obsrvr, from_receiving: nil)
322
+ }
323
+ end
324
+
325
+ def test_enabled?
326
+ assert REF.enabled?
327
+ # @todo I guess that's good enough?
328
+ end
329
+
330
+ def test_pid
331
+ assert_equal PID, REF.pid
332
+ assert_equal PID, window.pid
333
+ end
334
+
335
+ def test_pid_is_zero_for_system_wide
336
+ assert_equal 0, REF.system_wide.pid
337
+ end
338
+
339
+ def test_system_wide
340
+ assert_equal AXUIElementCreateSystemWide(), REF.system_wide
341
+ end
342
+
343
+ def test_application
344
+ assert_equal REF, REF.application
345
+ assert_equal REF, window.application
346
+ end
347
+
348
+ def test_set_timeout_for
349
+ assert_equal 10, REF.set_timeout_to(10)
350
+ assert_equal 0, REF.set_timeout_to(0)
351
+ end
352
+
353
+ def test_handle_error_failsafe
354
+ assert_error [99],
355
+ should_raise: RuntimeError,
356
+ with_fragments: [/never reach this line/, /99/]
357
+ end
358
+
359
+ def test_handle_failure
360
+ assert_error [KAXErrorFailure],
361
+ should_raise: RuntimeError,
362
+ with_fragments: [/system failure/, app]
363
+ end
364
+
365
+ def test_handle_illegal_argument
366
+ assert_error [KAXErrorIllegalArgument],
367
+ should_raise: ArgumentError,
368
+ with_fragments: [/is not an AXUIElementRef/, app]
369
+
370
+ assert_error [KAXErrorIllegalArgument, :cake],
371
+ should_raise: ArgumentError,
372
+ with_fragments: [/is not a legal argument/, /the element/, app, /cake/]
373
+
374
+ assert_error [KAXErrorIllegalArgument, 'cake', 'chocolate'],
375
+ should_raise: ArgumentError,
376
+ with_fragments: [/can't get\/set "cake" with\/to "chocolate"/, app]
377
+
378
+ p = CGPointMake(1,3)
379
+ assert_error [KAXErrorIllegalArgument, p, nil, nil],
380
+ should_raise: ArgumentError,
381
+ with_fragments: [/The point #{p.inspect}/, app]
382
+
383
+ assert_error [KAXErrorIllegalArgument, 'cheezburger', 'cake', nil, nil],
384
+ should_raise: ArgumentError,
385
+ with_fragments: [/the observer "cake"/,app,/the notification "cheezburger"/]
386
+ end
387
+
388
+ def test_handle_invalid_element
389
+ assert_error [KAXErrorInvalidUIElement],
390
+ should_raise: ArgumentError,
391
+ with_fragments: [/no longer a valid reference/, app]
392
+ end
393
+
394
+ def test_handle_invalid_observer
395
+ assert_error [KAXErrorInvalidUIElementObserver, :pie, :cake],
396
+ should_raise: ArgumentError,
397
+ with_fragments: [/no longer a valid observer/, /or was never valid/, app, /cake/]
398
+ end
399
+
400
+ def test_handle_cannot_complete
401
+ assert_error [KAXErrorCannotComplete],
402
+ should_raise: RuntimeError,
403
+ with_fragments: [/An unspecified error/, app, /:\(/]
404
+
405
+ @derp = REF.application_for pid_for 'com.apple.finder'
406
+ def @derp.pid; false end
407
+ assert_error [KAXErrorCannotComplete],
408
+ should_raise: RuntimeError,
409
+ with_fragments: [/Application for pid/, /Maybe it crashed\?/]
410
+ end
411
+
412
+ def test_attr_unsupported
413
+ assert_error [KAXErrorAttributeUnsupported, :cake],
414
+ should_raise: ArgumentError,
415
+ with_fragments: [/does not have/, /:cake attribute/, app]
416
+ end
417
+
418
+ def test_action_unsupported
419
+ assert_error [KAXErrorActionUnsupported, :pie],
420
+ should_raise: ArgumentError,
421
+ with_fragments: [/does not have/, /:pie action/, app]
422
+ end
423
+
424
+ def test_notif_unsupported
425
+ assert_error [KAXErrorNotificationUnsupported, :cheese],
426
+ should_raise: ArgumentError,
427
+ with_fragments: [/does not support/, /:cheese notification/, app]
428
+ end
429
+
430
+ def test_not_implemented
431
+ assert_error [KAXErrorNotImplemented],
432
+ should_raise: NotImplementedError,
433
+ with_fragments: [/does not work with AXAPI/, app]
434
+ end
435
+
436
+ def test_notif_registered
437
+ assert_error [KAXErrorNotificationAlreadyRegistered, :lamp],
438
+ should_raise: ArgumentError,
439
+ with_fragments: [/already registered/, /:lamp/, app]
440
+ end
441
+
442
+ def test_notif_not_registered
443
+ assert_error [KAXErrorNotificationNotRegistered, :peas],
444
+ should_raise: RuntimeError,
445
+ with_fragments: [/not registered/, /:peas/, app]
446
+ end
447
+
448
+ def test_api_disabled
449
+ assert_error [KAXErrorAPIDisabled],
450
+ should_raise: RuntimeError,
451
+ with_fragments: [/AXAPI has been disabled/]
452
+ end
453
+
454
+ def test_param_attr_unsupported
455
+ assert_error [KAXErrorParameterizedAttributeUnsupported, :oscar],
456
+ should_raise: ArgumentError,
457
+ with_fragments: [/does not have/, /:oscar parameterized attribute/, app]
458
+ end
459
+
460
+ def test_not_enough_precision
461
+ assert_error [KAXErrorNotEnoughPrecision],
462
+ should_raise: RuntimeError,
463
+ with_fragments: [/not enough precision/, '¯\(°_o)/¯']
464
+ end
465
+
466
+ end
467
+
468
+
469
+ class TestToAXToRubyHooks < MiniTest::Unit::TestCase
470
+
471
+ def test_to_ax
472
+ # point_makes_a_value
473
+ value = CGPointZero.to_ax
474
+ ptr = Pointer.new CGPoint.type
475
+ AXValueGetValue(value, 1, ptr)
476
+ assert_equal CGPointZero, ptr.value
477
+
478
+ # size_makes_a_value
479
+ value = CGSizeZero.to_ax
480
+ ptr = Pointer.new CGSize.type
481
+ AXValueGetValue(value, 2, ptr)
482
+ assert_equal CGSizeZero, ptr.value
483
+
484
+ # rect_makes_a_value
485
+ value = CGRectZero.to_ax
486
+ ptr = Pointer.new CGRect.type
487
+ AXValueGetValue(value, 3, ptr)
488
+ assert_equal CGRectZero, ptr.value
489
+
490
+ # range_makes_a_value
491
+ range = CFRange.new(5, 4)
492
+ value = range.to_ax
493
+ ptr = Pointer.new CFRange.type
494
+ AXValueGetValue(value, 4, ptr)
495
+ assert_equal range, ptr.value
496
+ end
497
+
498
+ def test_to_axvalue_raises_for_unsupported_boxes
499
+ assert_raises(NotImplementedError) { NSEdgeInsets.new.to_ax }
500
+ end
501
+
502
+ def test_to_ax_for_ranges
503
+ assert_equal CFRangeMake(1,10).to_ax, (1..10 ).to_ax
504
+ assert_equal CFRangeMake(1, 9).to_ax, (1...10 ).to_ax
505
+ assert_equal CFRangeMake(0, 3).to_ax, (0..2 ).to_ax
506
+ end
507
+
508
+ def test_to_axvalue_for_ranges_raises_for_bad_ranges
509
+ assert_raises(ArgumentError) { (1..-10).to_ax }
510
+ assert_raises(ArgumentError) { (-5...10).to_ax }
511
+ end
512
+
513
+ def test_to_ax_on_other_objects
514
+ obj = Object.new
515
+ assert_equal obj.self, obj.to_ax
516
+ end
517
+
518
+
519
+ def test_to_ruby
520
+ assert_equal CGPointZero, CGPointZero .to_ax.to_ruby
521
+ assert_equal CGSizeMake(10,10), CGSizeMake(10,10).to_ax.to_ruby
522
+ assert_equal Range.new(1,10), CFRange.new(1,10).to_ax.to_ruby
523
+ end
524
+
525
+ def test_to_ruby_on_non_boxes
526
+ obj = Object.new
527
+ assert_equal obj.self, obj.to_ruby
528
+ end
529
+
530
+ end
531
+
532
+
533
+ class TestMiscCoreExtensions < MiniTest::Unit::TestCase
534
+
535
+ def test_to_point
536
+ p = CGPointMake(2,3)
537
+ assert_equal p, p.to_point
538
+
539
+ p = CGPointMake(1,3)
540
+ assert_equal p, p.to_a.to_point
541
+ end
542
+
543
+ def test_to_size
544
+ s = CGSizeMake(2,4)
545
+ assert_equal s, s.to_a.to_size
546
+ end
547
+
548
+ def test_to_rect
549
+ r = CGRectMake(6,7,8,9)
550
+ assert_equal r, r.to_a.map(&:to_a).flatten.to_rect
551
+ end
552
+
553
+ end
@@ -0,0 +1,63 @@
1
+ require 'test/runner'
2
+ require 'accessibility/debug'
3
+
4
+ class TestAccessibilityDebug < MiniTest::Unit::TestCase
5
+
6
+ def mock_element
7
+ @mock = Object.new
8
+ def @mock.bounds; CGRectMake(100,100,100,100); end
9
+ @mock
10
+ end
11
+
12
+ def test_debug_setting
13
+ assert_respond_to Accessibility::Debug, :on?
14
+ assert_respond_to Accessibility::Debug, :on=
15
+ end
16
+
17
+ def test_highlight_returns_created_window
18
+ w = Accessibility::Debug.highlight mock_element
19
+ assert_kind_of NSWindow, w
20
+ assert_respond_to w, :stop
21
+ ensure
22
+ w.close if w
23
+ end
24
+
25
+ def test_highlight_can_take_a_timeout
26
+ w = Accessibility::Debug.highlight mock_element, timeout: 0.1
27
+ assert w.visible?
28
+ sleep 0.15
29
+ refute w.visible? # Not exactly the assertion I want, but close enough
30
+ ensure
31
+ w.close if w
32
+ end
33
+
34
+ def test_highlight_can_have_custom_colour
35
+ w = Accessibility::Debug.highlight mock_element, color: NSColor.cyanColor
36
+ assert w.backgroundColor == NSColor.cyanColor
37
+ w.close
38
+
39
+ # test both spellings of colour
40
+ w = Accessibility::Debug.highlight mock_element, colour: NSColor.purpleColor
41
+ assert w.backgroundColor == NSColor.purpleColor
42
+ end
43
+
44
+ def test_highlight_highlights_correct_rect
45
+ w = Accessibility::Debug.highlight mock_element
46
+ assert_equal w.frame, mock_element.bounds.flip!
47
+ end
48
+
49
+ end
50
+
51
+ class TestCGRectExtensions < MiniTest::Unit::TestCase
52
+
53
+ def test_flipping
54
+ size = NSScreen.mainScreen.frame.size
55
+ assert_equal CGRectMake( 0, size.height, 0, 0), CGRectZero.dup.flip!
56
+ assert_equal CGRectMake(100, size.height-200,100,100), CGRectMake(100,100,100,100).flip!
57
+ end
58
+
59
+ def test_flipping_twice_returns_to_original
60
+ assert_equal CGRectZero.dup, CGRectZero.dup.flip!.flip!
61
+ end
62
+
63
+ end
@@ -0,0 +1,75 @@
1
+ require 'test/helper'
2
+ require 'accessibility/dsl'
3
+
4
+
5
+ class TestAccessibilityDSL < MiniTest::Unit::TestCase
6
+
7
+ # LSP FTW
8
+ class DSL
9
+ include Accessibility::DSL
10
+ end
11
+
12
+ class LanguageTest < AX::Element
13
+ attr_reader :called_action
14
+ def actions= value; @actions = value end
15
+ def actions; @actions end
16
+ def perform action; @called_action = action end
17
+ def search *args; @search_args = args end
18
+ end
19
+
20
+ def dsl
21
+ @dsl ||= DSL.new
22
+ end
23
+
24
+ def element
25
+ @element ||= LanguageTest.new REF
26
+ end
27
+
28
+
29
+ def test_static_actions
30
+ def static_action action
31
+ dsl.send action, element
32
+ assert_equal action, element.called_action
33
+ end
34
+
35
+ static_action :press
36
+ static_action :show_menu
37
+ static_action :pick
38
+ static_action :decrement
39
+ static_action :confirm
40
+ static_action :increment
41
+ static_action :delete
42
+ static_action :cancel
43
+ static_action :hide
44
+ static_action :unhide
45
+ static_action :terminate
46
+ static_action :raise
47
+ end
48
+
49
+ def test_method_missing_forwards
50
+ element.actions = [:purple_rain]
51
+ dsl.purple_rain element
52
+ assert_equal :purple_rain, element.called_action
53
+
54
+ e = assert_raises(ArgumentError) { dsl.hack element }
55
+ assert_match /.hack. is not an action/, e.message
56
+
57
+ e = assert_raises(NoMethodError) { dsl.purple_rain 'A string' }
58
+ assert_match /undefined method/, e.message
59
+ end
60
+
61
+ def test_raise_can_still_raise_exception
62
+ assert_raises(ArgumentError) { dsl.raise ArgumentError }
63
+ assert_raises(NoMethodError) { dsl.raise NoMethodError }
64
+ end
65
+
66
+ def test_wait_for_demands_a_parent_or_ancestor
67
+ assert_raises(ArgumentError) { dsl.wait_for :bacon }
68
+ end
69
+
70
+ def test_wait_for_allows_filtering_by_parent
71
+ result = dsl.wait_for(:dude, parent: :hippie, ancestor: element)
72
+ assert_equal [:dude, { parent: :hippie }], result
73
+ end
74
+
75
+ end
@@ -0,0 +1,10 @@
1
+ require 'test/runner'
2
+ require 'accessibility/errors'
3
+
4
+ class TestAccessibilityErrors < MiniTest::Unit::TestCase
5
+
6
+ def test_search_failure_is_kind_of_no_method_error
7
+ assert_includes Accessibility::SearchFailure.ancestors, NoMethodError
8
+ end
9
+
10
+ end