AXElements 0.9.0 → 1.0.0.alpha

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 (60) hide show
  1. data/.yardopts +0 -4
  2. data/README.markdown +22 -17
  3. data/Rakefile +1 -1
  4. data/ext/accessibility/key_coder/extconf.rb +1 -1
  5. data/ext/accessibility/key_coder/key_coder.c +2 -4
  6. data/lib/accessibility.rb +3 -3
  7. data/lib/accessibility/core.rb +948 -0
  8. data/lib/accessibility/dsl.rb +30 -186
  9. data/lib/accessibility/enumerators.rb +1 -0
  10. data/lib/accessibility/factory.rb +78 -134
  11. data/lib/accessibility/graph.rb +5 -9
  12. data/lib/accessibility/highlighter.rb +86 -0
  13. data/lib/accessibility/{pretty_printer.rb → pp_inspector.rb} +4 -3
  14. data/lib/accessibility/qualifier.rb +3 -5
  15. data/lib/accessibility/screen_recorder.rb +217 -0
  16. data/lib/accessibility/statistics.rb +57 -0
  17. data/lib/accessibility/translator.rb +23 -32
  18. data/lib/accessibility/version.rb +2 -22
  19. data/lib/ax/application.rb +20 -159
  20. data/lib/ax/element.rb +42 -32
  21. data/lib/ax/scroll_area.rb +5 -6
  22. data/lib/ax/systemwide.rb +1 -33
  23. data/lib/ax_elements.rb +1 -9
  24. data/lib/ax_elements/core_graphics_workaround.rb +5 -0
  25. data/lib/ax_elements/nsarray_compat.rb +17 -97
  26. data/lib/ax_elements/vendor/inflection_data.rb +66 -0
  27. data/lib/ax_elements/vendor/inflections.rb +176 -0
  28. data/lib/ax_elements/vendor/inflector.rb +306 -0
  29. data/lib/minitest/ax_elements.rb +180 -0
  30. data/lib/mouse.rb +227 -0
  31. data/lib/rspec/expectations/ax_elements.rb +234 -0
  32. data/rakelib/gem.rake +3 -12
  33. data/rakelib/test.rake +15 -0
  34. data/test/helper.rb +20 -10
  35. data/test/integration/accessibility/test_core.rb +18 -0
  36. data/test/integration/accessibility/test_dsl.rb +40 -38
  37. data/test/integration/accessibility/test_enumerators.rb +1 -0
  38. data/test/integration/accessibility/test_graph.rb +0 -1
  39. data/test/integration/accessibility/test_qualifier.rb +2 -2
  40. data/test/integration/ax/test_application.rb +2 -9
  41. data/test/integration/ax/test_element.rb +0 -40
  42. data/test/integration/minitest/test_ax_elements.rb +89 -0
  43. data/test/integration/rspec/expectations/test_ax_elements.rb +102 -0
  44. data/test/sanity/accessibility/test_factory.rb +2 -2
  45. data/test/sanity/accessibility/test_highlighter.rb +56 -0
  46. data/test/sanity/accessibility/{test_pretty_printer.rb → test_pp_inspector.rb} +9 -9
  47. data/test/sanity/accessibility/test_statistics.rb +57 -0
  48. data/test/sanity/ax/test_application.rb +1 -16
  49. data/test/sanity/ax/test_element.rb +2 -2
  50. data/test/sanity/ax_elements/test_nsobject_inspect.rb +2 -4
  51. data/test/sanity/minitest/test_ax_elements.rb +17 -0
  52. data/test/sanity/rspec/expectations/test_ax_elements.rb +15 -0
  53. data/test/sanity/test_mouse.rb +22 -0
  54. data/test/test_core.rb +454 -0
  55. metadata +44 -69
  56. data/History.markdown +0 -41
  57. data/lib/accessibility/system_info.rb +0 -230
  58. data/lib/ax_elements/active_support_selections.rb +0 -10
  59. data/lib/ax_elements/mri.rb +0 -57
  60. data/test/sanity/accessibility/test_version.rb +0 -15
@@ -5,13 +5,13 @@ require 'ax/application'
5
5
  class TestAccessibilityElementFactory < MiniTest::Unit::TestCase
6
6
 
7
7
  def window
8
- @@window ||= REF.to_ruby.children.find { |x| x.role == KAXWindowRole }
8
+ @@window ||= REF.children.find { |x| x.role == KAXWindowRole }
9
9
  end
10
10
 
11
11
  def scroll_area
12
12
  @@scroll_area ||= window.children.find { |x|
13
13
  x.attributes.include?(:description) &&
14
- x.description == 'Test Web Area'
14
+ x.attribute(:description) == 'Test Web Area'
15
15
  }
16
16
  end
17
17
 
@@ -0,0 +1,56 @@
1
+ require 'test/runner'
2
+ require 'accessibility/highlighter'
3
+
4
+ class TestHighlighter < MiniTest::Unit::TestCase
5
+
6
+ def bounds
7
+ CGRectMake(100, 100, 100, 100)
8
+ end
9
+
10
+ def test_highlight_returns_created_window
11
+ w = Accessibility::Highlighter.new bounds
12
+ assert_kind_of NSWindow, w
13
+ assert_respond_to w, :stop
14
+ ensure
15
+ w.close if w
16
+ end
17
+
18
+ def test_highlight_can_take_a_timeout
19
+ w = Accessibility::Highlighter.new bounds, timeout: 0.1
20
+ assert w.visible?
21
+ sleep 0.15
22
+ refute w.visible? # Not exactly the assertion I want, but close enough
23
+ ensure
24
+ w.close if w
25
+ end
26
+
27
+ def test_highlight_can_have_custom_colour
28
+ w = Accessibility::Highlighter.new bounds, color: NSColor.cyanColor
29
+ assert w.backgroundColor == NSColor.cyanColor
30
+ w.close
31
+
32
+ # test both spellings of colour
33
+ w = Accessibility::Highlighter.new bounds, colour: NSColor.purpleColor
34
+ assert w.backgroundColor == NSColor.purpleColor
35
+ end
36
+
37
+ def test_highlight_highlights_correct_rect
38
+ w = Accessibility::Highlighter.new bounds
39
+ assert_equal w.frame, bounds.flip!
40
+ end
41
+
42
+ end
43
+
44
+ class TestCGRectExtensions < MiniTest::Unit::TestCase
45
+
46
+ def test_flipping
47
+ size = NSScreen.mainScreen.frame.size
48
+ assert_equal CGRectMake(0, size.height, 0, 0), CGRectZero.dup.flip!
49
+ assert_equal CGRectMake(100, size.height-200, 100, 100), CGRectMake(100,100,100,100).flip!
50
+ end
51
+
52
+ def test_flipping_twice_returns_to_original
53
+ assert_equal CGRectZero.dup, CGRectZero.dup.flip!.flip!
54
+ end
55
+
56
+ end
@@ -1,9 +1,9 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- class TestAccessibilityPrettyPrinter < MiniTest::Unit::TestCase
4
- include Accessibility::PrettyPrinter
3
+ class TestAccessibilityPPInspector < MiniTest::Unit::TestCase
4
+ include Accessibility::PPInspector
5
5
 
6
- # expected API for PrettyPrinter module
6
+ # expected API for PPInspector module
7
7
  attr_reader :attributes
8
8
  def attribute attr; @attribute; end
9
9
  def size_of attr; @size_of; end
@@ -18,10 +18,10 @@ class TestAccessibilityPrettyPrinter < MiniTest::Unit::TestCase
18
18
  assert_match /value=3.14/, pp_identifier
19
19
 
20
20
  @attribute = ''
21
- assert_match EMPTY_STRING, pp_identifier.to_s
21
+ assert_match EMPTY_STRING, pp_identifier
22
22
 
23
23
  @attribute = nil
24
- assert_match EMPTY_STRING, pp_identifier.to_s
24
+ assert_match EMPTY_STRING, pp_identifier
25
25
  end
26
26
 
27
27
  def test_identifier_using_title
@@ -43,8 +43,8 @@ class TestAccessibilityPrettyPrinter < MiniTest::Unit::TestCase
43
43
  @attribute = 'roflcopter'
44
44
  assert_match /roflcopter/, pp_identifier
45
45
 
46
- @attribute = ''
47
- assert_equal EMPTY_STRING, pp_identifier.to_s
46
+ @attribute = NSString.string
47
+ assert_equal EMPTY_STRING, pp_identifier
48
48
 
49
49
  @attribute = 26
50
50
  assert_match /26/, pp_identifier
@@ -62,11 +62,11 @@ class TestAccessibilityPrettyPrinter < MiniTest::Unit::TestCase
62
62
 
63
63
  def test_identifier_empty_string_as_final_fallback
64
64
  @attributes = NSArray.array
65
- assert_equal EMPTY_STRING, pp_identifier.to_s
65
+ assert_equal EMPTY_STRING, pp_identifier
66
66
  end
67
67
 
68
68
  def test_position
69
- @attribute = CGPoint.new
69
+ @attribute = CGPointZero
70
70
  assert_match /\(0\.0, 0\.0\)/, pp_position
71
71
 
72
72
  @attribute = CGPoint.new(3.14, -5)
@@ -0,0 +1,57 @@
1
+ require 'test/runner'
2
+ require 'accessibility/statistics'
3
+
4
+ class TestAccessibilityStatistics < MiniTest::Unit::TestCase
5
+
6
+ def setup
7
+ @stats = Accessibility::Statistics.new
8
+ @q = @stats.instance_variable_get :@q
9
+ @s = @stats.instance_variable_get :@stats
10
+ end
11
+
12
+ def test_global_constant
13
+ assert_instance_of Accessibility::Statistics, STATS
14
+ end
15
+
16
+ def test_collects_stats
17
+ assert_equal 0, @s[:pie]
18
+ assert_equal 0, @s[:cake]
19
+ @stats.increment :cake
20
+ @q.sync { }
21
+ assert_equal 1, @s[:cake]
22
+ assert_equal 0, @s[:pie]
23
+ @stats.increment :cake
24
+ @q.sync { }
25
+ assert_equal 2, @s[:cake]
26
+ assert_equal 0, @s[:pie]
27
+ @stats.increment :pie
28
+ @q.sync { }
29
+ assert_equal 2, @s[:cake]
30
+ assert_equal 1, @s[:pie]
31
+ end
32
+
33
+ def test_concurrency
34
+ loops = 1_000_000
35
+ loops.times do @stats.increment :pie end
36
+ @q.sync { }
37
+ assert_equal loops, @s[:pie]
38
+ end
39
+
40
+ def test_to_s
41
+ 100.times do @stats.increment :cake end
42
+ 2.times do @stats.increment :pie end
43
+ 50.times do @stats.increment :long_attribute_name end
44
+ expected = <<-EOS
45
+ ######################
46
+ # AX Call Statistics #
47
+ ######################
48
+ cake...................100
49
+ long_attribute_name.....50
50
+ pie......................2
51
+ EOS
52
+ assert_equal expected, @stats.to_s
53
+ end
54
+
55
+ # @todo bench_increment
56
+
57
+ end
@@ -24,17 +24,6 @@ class TestAXApplication < MiniTest::Unit::TestCase
24
24
  assert_match /\sfocused\[(?:✔|✘)\]/, app.inspect
25
25
  end
26
26
 
27
- def tset_action_methods_forward_to_perform
28
- def app.perform name
29
- [:cracker_jacks, name]
30
- end
31
-
32
- assert_equal [:cracker_jacks, :terminate], app.terminate
33
- assert_equal [:cracker_jacks, :force_terminate], app.terminate!
34
- assert_equal [:cracker_jacks, :hide], app.hide
35
- assert_equal [:cracker_jacks, :unhide], app.unhide
36
- end
37
-
38
27
  def test_terminate
39
28
  got_called = false
40
29
  mock = Object.new
@@ -100,15 +89,11 @@ class TestAXApplication < MiniTest::Unit::TestCase
100
89
  end
101
90
 
102
91
  def test_info_plist
103
- assert_equal 'transmute', app.info_plist['CFBundleIconFile']
92
+ assert_equal 'hmmmmm.icns', app.info_plist['CFBundleIconFile']
104
93
  end
105
94
 
106
95
  def test_version
107
96
  assert_equal '1.0', app.version
108
97
  end
109
98
 
110
- def test_ancestor
111
- assert_nil app.ancestor(:menu)
112
- end
113
-
114
99
  end
@@ -51,7 +51,7 @@ class TestAXElement < MiniTest::Unit::TestCase
51
51
  end
52
52
 
53
53
  # the nil case
54
- center_test CGPoint.new, CGSize.new, CGPoint.new
54
+ center_test CGPointZero, CGSizeZero, CGPointZero
55
55
  # simple square with origin at zero
56
56
  center_test [0,0], [2,2], [1,1]
57
57
  # simple square in positive positive quadrant
@@ -71,7 +71,7 @@ class TestSearchResultBlankness < MiniTest::Unit::TestCase
71
71
 
72
72
  def test_array_blank
73
73
  [
74
- Array.new,
74
+ NSArray.array,
75
75
  [true]
76
76
  ].each do |ary|
77
77
  assert_equal ary.empty?, ary.blank?
@@ -1,13 +1,11 @@
1
1
  require 'test/runner'
2
2
 
3
- if on_macruby?
4
3
  class TestNSObjectExtensions < MiniTest::Unit::TestCase
5
4
  # keeping this test just to make sure the version of MacRuby
6
5
  # being used is new enough
7
6
  def test_inspecting
8
- url = NSURL.URLWithString('http://marketcircle.com/')
9
- assert_equal url.description, url.inspect
7
+ url = NSURL.URLWithString('http://marketcircle.com/')
8
+ assert_equal url.description, url.inspect
10
9
  end
11
10
  end
12
- end
13
11
 
@@ -0,0 +1,17 @@
1
+ require 'test/runner'
2
+ require 'minitest/ax_elements'
3
+
4
+ class TestMiniTestAssertions < MiniTest::Unit::TestCase
5
+
6
+ def test_methods_are_loaded
7
+ assert_respond_to self, :assert_has_child
8
+ assert_respond_to self, :assert_has_descendent
9
+ assert_respond_to self, :assert_has_descendant
10
+ assert_respond_to self, :assert_shortly_has
11
+ assert_respond_to self, :refute_has_child
12
+ assert_respond_to self, :refute_has_descendent
13
+ assert_respond_to self, :refute_has_descendant
14
+ assert_respond_to self, :refute_shortly_has
15
+ end
16
+
17
+ end
@@ -0,0 +1,15 @@
1
+ require 'test/runner'
2
+ require 'rspec/expectations/ax_elements'
3
+
4
+ class TestRSpecMatchers < MiniTest::Unit::TestCase
5
+
6
+ def test_defined
7
+ assert TopLevel.method(:have_child)
8
+ assert TopLevel.method(:have_descendent)
9
+ assert TopLevel.method(:have_descendant)
10
+ assert TopLevel.method(:shortly_have_child)
11
+ assert TopLevel.method(:shortly_have_descendent)
12
+ assert TopLevel.method(:shortly_have_descendant)
13
+ end
14
+
15
+ end
@@ -0,0 +1,22 @@
1
+ require 'test/runner'
2
+ require 'mouse'
3
+
4
+ class TestMouseModule < MiniTest::Unit::TestCase
5
+
6
+ def distance point1, point2
7
+ x = point1.x - point2.x
8
+ y = point1.y - point2.y
9
+ Math.sqrt((x**2) + (y**2))
10
+ end
11
+
12
+ def test_move_to
13
+ point = CGPoint.new(100, 100)
14
+ Mouse.move_to point
15
+ assert_in_delta 0, distance(point,Mouse.current_position), 1.0
16
+
17
+ point = CGPoint.new(rand(700)+150, rand(500)+100)
18
+ Mouse.move_to point
19
+ assert_in_delta 0, distance(point,Mouse.current_position), 1.0
20
+ end
21
+
22
+ end
data/test/test_core.rb ADDED
@@ -0,0 +1,454 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'test/helper'
4
+ require 'accessibility/core'
5
+
6
+
7
+ class TestAccessibilityElement < MiniTest::Unit::TestCase
8
+
9
+ def child name
10
+ window.children.find { |item|
11
+ (block_given? ? yield(item) : true) if item.role == name
12
+ }
13
+ end
14
+
15
+ def app; @@app ||= REF end
16
+ def window; @@window ||= app.attribute(KAXWindowsAttribute).first end
17
+ def slider; @@slider ||= child KAXSliderRole end
18
+ def check_box; @@check_box ||= child KAXCheckBoxRole end
19
+ def pop_up; @@pop_up ||= child KAXPopUpButtonRole end
20
+ def search_box; @@search_box ||= child KAXTextFieldRole end
21
+ def static_text; @@static_text ||= child(KAXStaticTextRole) { |x| x.value.match /My Little Pony/ } end
22
+ def yes_button; @@yes_button ||= child(KAXButtonRole) { |x| x.attribute(KAXTitleAttribute) == 'Yes' } end
23
+ def bye_button; @@bye_button ||= child(KAXButtonRole) { |x| x.attribute(KAXTitleAttribute) == 'Bye!' } end
24
+ def no_button; @@no_button ||= child(KAXButtonRole) { |x| x.attribute(KAXTitleAttribute) == 'No' } end
25
+ def web_area; @@web_area ||= child("AXScrollArea") { |x| x.attribute('AXDescription' ) == 'Test Web Area' }.children.first end
26
+ def text_area; @@text_area ||= child("AXScrollArea") { |x| x.attributes.include?('AXIdentifier') && x.attribute('AXIdentifier') == 'Text Area' }.children.first end
27
+
28
+ def invalid_ref
29
+ bye_button # guarantee that it is cached
30
+ @@dead ||= no_button.perform KAXPressAction
31
+ bye_button
32
+ end
33
+
34
+
35
+ def test_equality
36
+ assert_equal app, app
37
+ assert_equal app, REF
38
+ assert_equal window, window
39
+ refute_equal app, window
40
+ end
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 raise an exception.
46
+
47
+ def test_attributes
48
+ attrs = app.attributes
49
+ assert_includes attrs, KAXRoleAttribute
50
+ assert_includes attrs, KAXChildrenAttribute
51
+ assert_includes attrs, KAXTitleAttribute
52
+ assert_includes attrs, KAXMenuBarAttribute
53
+
54
+ assert_empty invalid_ref.attributes, 'Dead elements should have no attrs'
55
+ end
56
+
57
+ def test_attribute
58
+ assert_equal 'AXElementsTester', window.attribute(KAXTitleAttribute )
59
+ assert_equal false, window.attribute(KAXFocusedAttribute)
60
+ assert_equal CGSize.new(555,529), window.attribute(KAXSizeAttribute )
61
+ assert_equal app, window.attribute(KAXParentAttribute )
62
+ assert_equal 10..19, window.attribute("AXPie" )
63
+
64
+ assert_nil window.attribute(KAXGrowAreaAttribute), 'KAXErrorNoValue == nil'
65
+
66
+ assert_nil invalid_ref.attribute(KAXRoleAttribute), 'Dead element == nil'
67
+ assert_empty invalid_ref.attribute(KAXChildrenAttribute)
68
+
69
+ assert_raises(ArgumentError) { app.attribute('MADE_UP_ATTR') }
70
+ end
71
+
72
+ def test_role
73
+ assert_equal KAXApplicationRole, app.role
74
+ assert_equal KAXWindowRole, window.role
75
+ end
76
+
77
+ def test_subrole
78
+ assert_equal KAXStandardWindowSubrole, window.subrole
79
+ assert_nil web_area.subrole
80
+ end
81
+
82
+ def test_children
83
+ assert_equal app.attribute(KAXChildrenAttribute), app.children
84
+ end
85
+
86
+ def test_value
87
+ assert_equal check_box.attribute(KAXValueAttribute), check_box.value
88
+ assert_equal slider.attribute(KAXValueAttribute), slider.value
89
+ end
90
+
91
+ def test_pid
92
+ assert_equal PID, app.pid
93
+ assert_equal PID, window.pid
94
+ assert_equal 0, Accessibility::Element.system_wide.pid # special case
95
+ end
96
+
97
+ def test_invalid?
98
+ assert_equal false, app.invalid?
99
+ assert_equal true, invalid_ref.invalid?
100
+ assert_equal false, window.invalid?
101
+ end
102
+
103
+ def test_size_of
104
+ assert_equal app.children.size, app.size_of(KAXChildrenAttribute)
105
+ assert_equal 0, pop_up.size_of(KAXChildrenAttribute)
106
+
107
+ assert_equal 0, invalid_ref.size_of(KAXChildrenAttribute), 'Dead == 0'
108
+ end
109
+
110
+ def test_writable?
111
+ refute app.writable? KAXTitleAttribute
112
+ assert window.writable? KAXMainAttribute
113
+
114
+ refute invalid_ref.writable?(KAXRoleAttribute), 'Dead is always false'
115
+ end
116
+
117
+ def test_set_number
118
+ [25, 75, 50].each do |number|
119
+ assert_equal number, slider.set(KAXValueAttribute, number)
120
+ assert_equal number, slider.value
121
+ end
122
+ end
123
+
124
+ def test_set_string
125
+ [Time.now.to_s, ''].each do |string|
126
+ assert_equal string, search_box.set(KAXValueAttribute, string)
127
+ assert_equal string, search_box.value
128
+ end
129
+ end
130
+
131
+ def test_set_wrapped
132
+ text_area.set KAXValueAttribute, 'hey-o'
133
+
134
+ text_area.set KAXSelectedTextRangeAttribute, 0..3
135
+ assert_equal 0..3, text_area.attribute(KAXSelectedTextRangeAttribute)
136
+
137
+ text_area.set KAXSelectedTextRangeAttribute, 1...4
138
+ assert_equal 1..3, text_area.attribute(KAXSelectedTextRangeAttribute)
139
+ ensure
140
+ text_area.set KAXValueAttribute, ''
141
+ end
142
+
143
+ def test_set_attr_handles_errors
144
+ assert_raises(ArgumentError) { app.set 'FAKE', true }
145
+ assert_raises(ArgumentError) { invalid_ref.set KAXTitleAttribute, 'hi' }
146
+ end
147
+
148
+ def test_parameterized_attributes
149
+ assert_empty app.parameterized_attributes
150
+
151
+ attrs = static_text.parameterized_attributes
152
+ assert_includes attrs, KAXStringForRangeParameterizedAttribute
153
+ assert_includes attrs, KAXLineForIndexParameterizedAttribute
154
+ assert_includes attrs, KAXBoundsForRangeParameterizedAttribute
155
+
156
+ assert_empty invalid_ref.parameterized_attributes, 'Dead should always be empty'
157
+ end
158
+
159
+ def test_parameterized_attribute
160
+ expected = 'My Li'
161
+
162
+ attr = static_text.parameterized_attribute(KAXStringForRangeParameterizedAttribute, 0..4)
163
+ assert_equal expected, attr
164
+
165
+ attr = static_text.parameterized_attribute(KAXAttributedStringForRangeParameterizedAttribute, 0..4)
166
+ assert_equal expected, attr.string
167
+
168
+ assert_nil invalid_ref.parameterized_attribute(KAXStringForRangeParameterizedAttribute, 0..0),
169
+ 'dead elements should return nil for any parameterized attribute'
170
+
171
+ # Should add a test case to test the no value case, but it will have
172
+ # to be fabricated in the test app.
173
+
174
+ assert_raises(ArgumentError) {
175
+ app.parameterized_attribute(KAXStringForRangeParameterizedAttribute, 0..1)
176
+ }
177
+ end
178
+
179
+ def test_actions
180
+ assert_empty app.actions
181
+ assert_equal [KAXPressAction], yes_button.actions
182
+ end
183
+
184
+ def test_perform_action
185
+ 2.times do # twice so it should be back where it started
186
+ val = check_box.value
187
+ check_box.perform KAXPressAction
188
+ refute_equal val, check_box.value
189
+ end
190
+
191
+ val = slider.value
192
+ slider.perform KAXIncrementAction
193
+ assert slider.value > val
194
+
195
+ val = slider.value
196
+ slider.perform KAXDecrementAction
197
+ assert slider.value < val
198
+
199
+ assert_raises(ArgumentError) { app.perform nil }
200
+ end
201
+
202
+ ##
203
+ # The keyboard simulation stuff is a bit weird...
204
+
205
+ def test_post
206
+ events = [[0x56,true], [0x56,false], [0x54,true], [0x54,false]]
207
+ string = '42'
208
+
209
+ search_box.set KAXFocusedAttribute, true
210
+ app.post events
211
+
212
+ assert_equal string, search_box.value
213
+
214
+ ensure # reset for next test
215
+ search_box.set KAXValueAttribute, ''
216
+ end
217
+
218
+ def test_key_rate
219
+ assert_equal 0.009, Accessibility::Element.key_rate
220
+ [
221
+ [0.9, :very_slow],
222
+ [0.09, :slow],
223
+ [0.009, :normal],
224
+ [0.0009, :fast],
225
+ [0.00009, :zomg]
226
+ ].each do |num, name|
227
+ Accessibility::Element.key_rate = name
228
+ assert_equal num, Accessibility::Element.key_rate
229
+ end
230
+ ensure
231
+ Accessibility::Element.key_rate = :default
232
+ end
233
+
234
+ def test_element_at
235
+ point = no_button.attribute(KAXPositionAttribute)
236
+
237
+ element = app.element_at point
238
+ assert_equal no_button, element, "#{no_button.inspect} and #{element.inspect}"
239
+
240
+ element = Accessibility::Element.system_wide.element_at point
241
+ assert_equal no_button, element, "#{no_button.inspect} and #{element.inspect}"
242
+
243
+ assert_respond_to element, :role
244
+
245
+ # skip 'Need to find a way to guarantee an empty spot on the screen to return nil'
246
+ # test manually for now :(
247
+ end
248
+
249
+ def test_application_for
250
+ assert_equal app, Accessibility::Element.application_for(PID)
251
+
252
+ assert_raises(ArgumentError) { Accessibility::Element.application_for 0 }
253
+ end
254
+
255
+ def test_system_wide
256
+ assert_equal AXUIElementCreateSystemWide(), Accessibility::Element.system_wide
257
+ end
258
+
259
+ def test_application
260
+ assert_equal app, app.application
261
+ assert_equal app, window.application
262
+ end
263
+
264
+ def test_set_timeout_for
265
+ assert_equal 10, app.set_timeout_to(10)
266
+ assert_equal 0, app.set_timeout_to(0)
267
+ end
268
+
269
+
270
+
271
+ def assert_error args, should_raise: klass, with_fragments: msgs
272
+ e = assert_raises(klass) { (@derp || app).handle_error *args }
273
+ assert_match /test_core.rb:272/, e.backtrace.first unless RUNNING_COMPILED
274
+ msgs.each { |msg| assert_match msg, e.message }
275
+ end
276
+
277
+ def test_handle_errors
278
+ app_inspect = Regexp.new(Regexp.escape(app.inspect))
279
+
280
+ assert_error [KAXErrorFailure],
281
+ should_raise: RuntimeError,
282
+ with_fragments: [/system failure/, app_inspect]
283
+
284
+ assert_error [KAXErrorIllegalArgument],
285
+ should_raise: ArgumentError,
286
+ with_fragments: [/is not an AXUIElementRef/, app_inspect]
287
+
288
+ assert_error [KAXErrorIllegalArgument, :cake],
289
+ should_raise: ArgumentError,
290
+ with_fragments: [/is not a legal argument/, /the element/, app_inspect, /cake/]
291
+
292
+ assert_error [KAXErrorIllegalArgument, 'cake', 'chocolate'],
293
+ should_raise: ArgumentError,
294
+ with_fragments: [/can't get\/set "cake" with\/to "chocolate"/, app_inspect]
295
+
296
+ p = CGPoint.new(1,3)
297
+ assert_error [KAXErrorIllegalArgument, p, nil, nil],
298
+ should_raise: ArgumentError,
299
+ with_fragments: [/The point #{p.inspect}/, app_inspect]
300
+
301
+ assert_error [KAXErrorInvalidUIElement],
302
+ should_raise: ArgumentError,
303
+ with_fragments: [/no longer a valid reference/, app_inspect]
304
+
305
+ assert_error [KAXErrorInvalidUIElementObserver, :pie, :cake],
306
+ should_raise: ArgumentError,
307
+ with_fragments: [/no longer support/]
308
+
309
+ assert_error [KAXErrorCannotComplete],
310
+ should_raise: RuntimeError,
311
+ with_fragments: [/An unspecified error/, app_inspect, /:\(/]
312
+
313
+ @derp = Accessibility::Element.application_for pid_for 'com.apple.finder'
314
+ def @derp.pid; false end
315
+ assert_error [KAXErrorCannotComplete],
316
+ should_raise: RuntimeError,
317
+ with_fragments: [/Application for pid/, /Maybe it crashed\?/]
318
+ @derp = nil
319
+
320
+ assert_error [KAXErrorAttributeUnsupported, :cake],
321
+ should_raise: ArgumentError,
322
+ with_fragments: [/does not have/, /:cake attribute/, app_inspect]
323
+
324
+ assert_error [KAXErrorActionUnsupported, :pie],
325
+ should_raise: ArgumentError,
326
+ with_fragments: [/does not have/, /:pie action/, app_inspect]
327
+
328
+ assert_error [KAXErrorNotificationUnsupported, :cheese],
329
+ should_raise: ArgumentError,
330
+ with_fragments: [/no longer support/]
331
+
332
+ assert_error [KAXErrorNotImplemented],
333
+ should_raise: NotImplementedError,
334
+ with_fragments: [/does not work with AXAPI/, app_inspect]
335
+
336
+ assert_error [KAXErrorNotificationAlreadyRegistered, :lamp],
337
+ should_raise: ArgumentError,
338
+ with_fragments: [/no longer support/]
339
+
340
+ assert_error [KAXErrorNotificationNotRegistered, :peas],
341
+ should_raise: RuntimeError,
342
+ with_fragments: [/no longer support/]
343
+
344
+ assert_error [KAXErrorAPIDisabled],
345
+ should_raise: RuntimeError,
346
+ with_fragments: [/AXAPI has been disabled/]
347
+
348
+ assert_error [KAXErrorNoValue],
349
+ should_raise: RuntimeError,
350
+ with_fragments: [/internal error/]
351
+
352
+ assert_error [KAXErrorParameterizedAttributeUnsupported, :oscar],
353
+ should_raise: ArgumentError,
354
+ with_fragments: [/does not have/, /:oscar parameterized attribute/, app_inspect]
355
+
356
+ assert_error [KAXErrorNotEnoughPrecision],
357
+ should_raise: RuntimeError,
358
+ with_fragments: [/not enough precision/, '¯\(°_o)/¯']
359
+
360
+ exception = assert_raises(RuntimeError) { app.send(:handle_error, 0) }
361
+ assert_match /assertion failed/, exception.message
362
+
363
+ assert_error [99],
364
+ should_raise: RuntimeError,
365
+ with_fragments: [/unknown error code/, /99/, app_inspect]
366
+ end
367
+
368
+ end
369
+
370
+
371
+ class TestToAXToRubyHooks < MiniTest::Unit::TestCase
372
+
373
+ def test_to_ax
374
+ value = CGPointZero.to_ax
375
+ ptr = Pointer.new CGPoint.type
376
+ AXValueGetValue(value, 1, ptr)
377
+ assert_equal CGPointZero, ptr.value, 'point makes a value'
378
+
379
+ value = CGSizeZero.to_ax
380
+ ptr = Pointer.new CGSize.type
381
+ AXValueGetValue(value, 2, ptr)
382
+ assert_equal CGSizeZero, ptr.value, 'size makes a value'
383
+
384
+ value = CGRectZero.to_ax
385
+ ptr = Pointer.new CGRect.type
386
+ AXValueGetValue(value, 3, ptr)
387
+ assert_equal CGRectZero, ptr.value, 'rect makes a value'
388
+
389
+ range = CFRange.new(5, 4)
390
+ value = range.to_ax
391
+ ptr = Pointer.new CFRange.type
392
+ AXValueGetValue(value, 4, ptr)
393
+ assert_equal range, ptr.value, 'range makes a value'
394
+
395
+ assert_equal CFRangeMake(1,10).to_ax, (1..10 ).to_ax
396
+ assert_equal CFRangeMake(1, 9).to_ax, (1...10 ).to_ax
397
+ assert_equal CFRangeMake(0, 3).to_ax, (0..2 ).to_ax
398
+
399
+ assert_raises(ArgumentError) { (1..-10).to_ax }
400
+ assert_raises(ArgumentError) { (-5...10).to_ax }
401
+ assert_raises(NotImplementedError) { NSEdgeInsets.new.to_ax }
402
+
403
+ assert_equal Object, Object.to_ax
404
+ assert_equal 10, 10.to_ax
405
+ end
406
+
407
+ def test_to_ruby
408
+ assert_equal CGPointZero, CGPointZero .to_ax.to_ruby
409
+ assert_equal CGSize.new(10,10), CGSize.new(10,10) .to_ax.to_ruby
410
+ assert_equal CGRectMake(1,2,3,4), CGRectMake(1,2,3,4).to_ax.to_ruby
411
+ assert_equal 1..10, CFRange.new(1, 10) .to_ax.to_ruby
412
+ assert_equal Range.new(1,10), CFRange.new(1,10) .to_ax.to_ruby
413
+ assert_equal Object, Object.to_ruby
414
+ end
415
+
416
+ end
417
+
418
+
419
+ class TestMiscCoreExtensions < MiniTest::Unit::TestCase
420
+
421
+ def test_to_point
422
+ p = CGPoint.new(2,3)
423
+ assert_equal p, p.to_point
424
+
425
+ p = CGPoint.new(1,3)
426
+ assert_equal p, p.to_a.to_point
427
+ end
428
+
429
+ def test_to_size
430
+ s = CGSize.new(2,4)
431
+ assert_equal s, s.to_a.to_size
432
+ end
433
+
434
+ def test_to_rect
435
+ r = CGRectMake(6, 7, 8, 9)
436
+ assert_equal r, r.to_a.map(&:to_a).flatten.to_rect
437
+ end
438
+
439
+ def test_to_url
440
+ site = 'http://marketcircle.com/'
441
+ url = site.to_url
442
+ refute_nil url
443
+ assert_equal NSURL.URLWithString(site), url
444
+
445
+ file = 'file://localhost/Applications/Calculator.app/'
446
+ url = file.to_url
447
+ refute_nil url
448
+ assert_equal NSURL.fileURLWithPath('/Applications/Calculator.app/'), url
449
+
450
+ void = "not a url at all"
451
+ assert_nil void.to_url
452
+ end
453
+
454
+ end