AXElements 0.9.0 → 1.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +0 -4
- data/README.markdown +22 -17
- data/Rakefile +1 -1
- data/ext/accessibility/key_coder/extconf.rb +1 -1
- data/ext/accessibility/key_coder/key_coder.c +2 -4
- data/lib/accessibility.rb +3 -3
- data/lib/accessibility/core.rb +948 -0
- data/lib/accessibility/dsl.rb +30 -186
- data/lib/accessibility/enumerators.rb +1 -0
- data/lib/accessibility/factory.rb +78 -134
- data/lib/accessibility/graph.rb +5 -9
- data/lib/accessibility/highlighter.rb +86 -0
- data/lib/accessibility/{pretty_printer.rb → pp_inspector.rb} +4 -3
- data/lib/accessibility/qualifier.rb +3 -5
- data/lib/accessibility/screen_recorder.rb +217 -0
- data/lib/accessibility/statistics.rb +57 -0
- data/lib/accessibility/translator.rb +23 -32
- data/lib/accessibility/version.rb +2 -22
- data/lib/ax/application.rb +20 -159
- data/lib/ax/element.rb +42 -32
- data/lib/ax/scroll_area.rb +5 -6
- data/lib/ax/systemwide.rb +1 -33
- data/lib/ax_elements.rb +1 -9
- data/lib/ax_elements/core_graphics_workaround.rb +5 -0
- data/lib/ax_elements/nsarray_compat.rb +17 -97
- data/lib/ax_elements/vendor/inflection_data.rb +66 -0
- data/lib/ax_elements/vendor/inflections.rb +176 -0
- data/lib/ax_elements/vendor/inflector.rb +306 -0
- data/lib/minitest/ax_elements.rb +180 -0
- data/lib/mouse.rb +227 -0
- data/lib/rspec/expectations/ax_elements.rb +234 -0
- data/rakelib/gem.rake +3 -12
- data/rakelib/test.rake +15 -0
- data/test/helper.rb +20 -10
- data/test/integration/accessibility/test_core.rb +18 -0
- data/test/integration/accessibility/test_dsl.rb +40 -38
- data/test/integration/accessibility/test_enumerators.rb +1 -0
- data/test/integration/accessibility/test_graph.rb +0 -1
- data/test/integration/accessibility/test_qualifier.rb +2 -2
- data/test/integration/ax/test_application.rb +2 -9
- data/test/integration/ax/test_element.rb +0 -40
- data/test/integration/minitest/test_ax_elements.rb +89 -0
- data/test/integration/rspec/expectations/test_ax_elements.rb +102 -0
- data/test/sanity/accessibility/test_factory.rb +2 -2
- data/test/sanity/accessibility/test_highlighter.rb +56 -0
- data/test/sanity/accessibility/{test_pretty_printer.rb → test_pp_inspector.rb} +9 -9
- data/test/sanity/accessibility/test_statistics.rb +57 -0
- data/test/sanity/ax/test_application.rb +1 -16
- data/test/sanity/ax/test_element.rb +2 -2
- data/test/sanity/ax_elements/test_nsobject_inspect.rb +2 -4
- data/test/sanity/minitest/test_ax_elements.rb +17 -0
- data/test/sanity/rspec/expectations/test_ax_elements.rb +15 -0
- data/test/sanity/test_mouse.rb +22 -0
- data/test/test_core.rb +454 -0
- metadata +44 -69
- data/History.markdown +0 -41
- data/lib/accessibility/system_info.rb +0 -230
- data/lib/ax_elements/active_support_selections.rb +0 -10
- data/lib/ax_elements/mri.rb +0 -57
- 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.
|
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
|
4
|
-
include Accessibility::
|
3
|
+
class TestAccessibilityPPInspector < MiniTest::Unit::TestCase
|
4
|
+
include Accessibility::PPInspector
|
5
5
|
|
6
|
-
# expected API for
|
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
|
21
|
+
assert_match EMPTY_STRING, pp_identifier
|
22
22
|
|
23
23
|
@attribute = nil
|
24
|
-
assert_match EMPTY_STRING, pp_identifier
|
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
|
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
|
65
|
+
assert_equal EMPTY_STRING, pp_identifier
|
66
66
|
end
|
67
67
|
|
68
68
|
def test_position
|
69
|
-
@attribute =
|
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 '
|
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
|
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
|
-
|
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
|
-
|
9
|
-
|
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
|