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
@@ -0,0 +1,148 @@
1
+ require 'test/integration/helper'
2
+
3
+ class TestAccessibilityQualifier < MiniTest::Unit::TestCase
4
+
5
+ def app
6
+ AX::Application.new REF
7
+ end
8
+
9
+ def qualifier klass, criteria, &block
10
+ Accessibility::Qualifier.new(klass, criteria, &block)
11
+ end
12
+
13
+ def dock
14
+ AX::DOCK
15
+ end
16
+
17
+ def list
18
+ @@list ||= dock.children.first
19
+ end
20
+
21
+ def items
22
+ @@items ||= list.children
23
+ end
24
+
25
+ def test_qualifies_based_on_class
26
+ q = qualifier(:List, {})
27
+ assert q.qualifies? list
28
+ refute q.qualifies? items.sample
29
+ end
30
+
31
+ def test_qualifies_based_on_superclass
32
+ q = qualifier(:DockItem, {})
33
+ items.each do |item|
34
+ assert q.qualifies? item
35
+ end
36
+
37
+ refute q.qualifies? dock
38
+ end
39
+
40
+ def test_qualifies_based_on_non_existant_class
41
+ q = qualifier(:Derp, {})
42
+ refute q.qualifies? dock
43
+ refute q.qualifies? list
44
+ refute q.qualifies? items.sample
45
+ end
46
+
47
+ def test_qualifies_based_on_attribute_filter
48
+ q = qualifier(:DockItem, title: 'Trash')
49
+ trash = items.find { |item| item.class == AX::TrashDockItem }
50
+ others = items.select { |item| item != trash }
51
+
52
+ assert q.qualifies? trash
53
+ others.each do |other|
54
+ refute q.qualifies? other
55
+ end
56
+ end
57
+
58
+ def test_qualifies_based_on_regexp_match
59
+ q = qualifier(:DockItem, title: /Trash/)
60
+ trash = items.find { |item| item.class == AX::TrashDockItem }
61
+ others = items.select { |item| item != trash }
62
+
63
+ assert q.qualifies? trash
64
+ others.each do |other|
65
+ refute q.qualifies? other
66
+ end
67
+ end
68
+
69
+ def test_qualifies_based_on_subsearch
70
+ q = qualifier(:List, dock_item: { title: 'Trash' })
71
+ assert q.qualifies? list
72
+ refute q.qualifies? dock
73
+ refute q.qualifies? items.sample
74
+ end
75
+
76
+ def test_qualifies_based_on_param_attribute_value
77
+ e = app.attribute(:main_window).attribute(:title_ui_element)
78
+ q = qualifier(:StaticText, [:string_for_range, CFRange.new(0,2)] => 'AX' )
79
+ assert q.qualifies? e
80
+ refute q.qualifies? list
81
+ end
82
+
83
+ def test_qualifies_based_on_param_attribute_regexp_match
84
+ e = app.attribute(:main_window).attribute(:title_ui_element)
85
+ q = qualifier(:StaticText, [:string_for_range, CFRange.new(0,2)] => /AX/ )
86
+ assert q.qualifies? e
87
+ refute q.qualifies? items.sample
88
+ end
89
+
90
+ def test_qualifies_based_on_multiple_filters
91
+ q = qualifier(:DockItem, title: 'Trash', role_description: /trash dock item/)
92
+ assert q.qualifies? items.last
93
+ refute q.qualifies? items.first
94
+
95
+ q = qualifier(:List, role_description: /list/, trash_dock_item: { title: 'Trash' })
96
+ assert q.qualifies? list
97
+ refute q.qualifies? items.sample
98
+ end
99
+
100
+ def test_qualifiers_can_use_aliased_attributes
101
+ id = "I'm a little teapot"
102
+ q = qualifier(:Button, id: id)
103
+ app.main_window.children.each do |kid|
104
+ if kid.attributes.include?(:identifier) && kid.id == id
105
+ assert q.qualifies? kid
106
+ else
107
+ refute q.qualifies? kid
108
+ end
109
+ end
110
+ end
111
+
112
+ def test_qualifies_based_on_given_block
113
+ @got_called = 0
114
+ q = qualifier(:DockItem, {}) do |element|
115
+ @got_called += 1
116
+ element.subrole == KAXTrashDockItemSubrole
117
+ end
118
+ refute q.qualifies?(items.first), @got_called
119
+ assert q.qualifies?(items.last), @got_called
120
+ assert_equal 2, @got_called
121
+ end
122
+
123
+ def test_describe
124
+ def describe expected, qualifier
125
+ assert_equal expected, qualifier.describe
126
+ end
127
+ describe 'DockItem', qualifier(:DockItem,{ } )
128
+ describe 'Button(title: "Free Bacon")', qualifier(:Button, { title: 'Free Bacon' } )
129
+ describe 'Element[✔]', qualifier(:Element, { }, &proc { |_| })
130
+ describe 'Window(id: /FindMe/, button(title: "Press"))', qualifier(:Window, { id: /FindMe/, button: { title: 'Press' }} )
131
+ describe 'Row(id: "Yo")[✔]', qualifier(:Row, id: 'Yo', &proc { |_| })
132
+ end
133
+
134
+ # n is the number of filters used
135
+ def bench_filters
136
+ skip 'TODO'
137
+ end
138
+
139
+ # n is the number of elements compared
140
+ def bench_similar_elements
141
+ skip 'TODO'
142
+ q = qualifier(:DockItem, title: 'Finder')
143
+ assert_performance_linear do |n|
144
+ (items * n).each { |item| q.qualifies? item }
145
+ end
146
+ end
147
+
148
+ end
@@ -0,0 +1,56 @@
1
+ require 'test/integration/helper'
2
+
3
+ class TestAXApplication < MiniTest::Unit::TestCase
4
+
5
+ def app
6
+ @app ||= AX::Application.new REF
7
+ end
8
+
9
+ def running_app
10
+ @app.instance_variable_get :@app
11
+ end
12
+
13
+ def test_initialize_args
14
+ assert_equal app, AX::Application.new(PID)
15
+ assert_equal app, AX::Application.new(running_app.localizedName)
16
+ assert_equal app, AX::Application.new(running_app)
17
+ end
18
+
19
+ def test_can_set_focus_and_blur_app # lol, blur
20
+ assert app.set :focused, to: false
21
+ refute app.active?
22
+ refute app.attribute :focused
23
+ refute app.attribute :focused?
24
+
25
+ assert app.set :focused, to: true
26
+ assert app.active?
27
+ assert app.attribute :focused
28
+ assert app.attribute :focused?
29
+
30
+ ensure
31
+ running_app.activateWithOptions NSApplicationActivateIgnoringOtherApps
32
+ end
33
+
34
+ def test_can_hide_and_unhide_app
35
+ assert app.set :hidden, to: true
36
+ assert app.hidden?
37
+
38
+ assert app.set :hidden, to: false
39
+ refute app.hidden?
40
+
41
+ assert app.perform :hide
42
+ assert app.hidden?
43
+
44
+ assert app.perform :unhide
45
+ refute app.hidden?
46
+
47
+ ensure
48
+ running_app.activateWithOptions NSApplicationActivateIgnoringOtherApps
49
+ end
50
+
51
+ def test_element_at_point
52
+ button = app.main_window.close_button
53
+ assert_equal button, app.element_at_point(button)
54
+ end
55
+
56
+ end
@@ -0,0 +1,46 @@
1
+ require 'test/integration/helper'
2
+
3
+ class TestAXElement < MiniTest::Unit::TestCase
4
+
5
+ def app
6
+ @@app ||= AX::Application.new PID
7
+ end
8
+
9
+ def test_search_singular_returns_array
10
+ result = app.search(:window)
11
+ assert_kind_of AX::Window, result
12
+ end
13
+
14
+ def test_search_plural
15
+ result = app.search(:windows)
16
+ assert_kind_of NSArray, result
17
+ end
18
+
19
+ def test_set_range
20
+ box = app.window.text_area
21
+ box.set :value, 'okey dokey lemon pokey'
22
+
23
+ box.set :selected_text_range, 0..5
24
+ assert_equal 0..5, box.selected_text_range
25
+
26
+ box.set :selected_text_range, 1..5
27
+ assert_equal 1..5, box.selected_text_range
28
+
29
+ box.set :selected_text_range, 5...10
30
+ assert_equal 5..9, box.selected_text_range
31
+
32
+ box.set :selected_text_range, 1..-1
33
+ assert_equal 1..21, box.selected_text_range
34
+
35
+ box.set :selected_text_range, 4...-10
36
+ assert_equal 4..11, box.selected_text_range
37
+
38
+ ensure
39
+ box.set :value, '' if box
40
+ end
41
+
42
+ # @todo test setting selected text
43
+ # def test_setting
44
+ # end
45
+
46
+ end
@@ -0,0 +1,23 @@
1
+ require 'test/integration/helper'
2
+
3
+ class TestAXRow < MiniTest::Unit::TestCase
4
+
5
+ def app; @@app ||= AX::Application.new REF end
6
+ def table; @@table ||= app.main_window.table end
7
+ def rows; @@rows ||= table.rows end
8
+ def row; rows.first end
9
+
10
+ def test_child_in_column
11
+ assert_equal row.children.second, row.child_in_column(header: 'Two')
12
+ assert_equal row.children.first, row.child_in_column(header: 'One')
13
+
14
+ assert_raises Accessibility::SearchFailure do
15
+ rows.first.child_in_column header: 'Fifty'
16
+ end
17
+ end
18
+
19
+ def bench_row_in_column
20
+ skip 'TODO'
21
+ end
22
+
23
+ end
@@ -0,0 +1,43 @@
1
+ require 'test/integration/helper'
2
+
3
+ class TestNSArrayCompat < MiniTest::Unit::TestCase
4
+
5
+ def app
6
+ @@app ||= AX::Application.new PID
7
+ end
8
+
9
+ def test_raises_for_non_elements
10
+ assert_raises NoMethodError do
11
+ [1].rows
12
+ end
13
+
14
+ assert_raises NoMethodError do
15
+ [app,1].titles
16
+ end
17
+ end
18
+
19
+ def test_normally_pluralized_method_names_work
20
+ table = app.main_window.table
21
+ result = [table,table].rows
22
+ assert_equal result.first, result.last
23
+ end
24
+
25
+ def test_artificially_pluralized_method_names_work
26
+ assert_equal [app.title], [app].titles
27
+ end
28
+
29
+ def test_predicate_methods_work
30
+ assert_equal [true], [app].focused?
31
+ end
32
+
33
+ def test_handles_searching_an_array
34
+ assert_equal [app].windows, [app.windows]
35
+ end
36
+
37
+ def test_raises_if_element_does_not_respond
38
+ assert_raises NoMethodError do
39
+ [app.main_window.close_button].windoze
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,98 @@
1
+ require 'test/integration/helper'
2
+ require 'minitest/ax_elements'
3
+
4
+ class TestMiniTestAssertions < MiniTest::Unit::TestCase
5
+
6
+ def app
7
+ @@app ||= AX::Application.new PID
8
+ end
9
+
10
+ def test_assert_has_child
11
+ expected = app.window
12
+
13
+ assert_equal expected, assert_has_child(app, :window)
14
+ assert_equal expected, assert_has_child(app, :window, title: 'AXElementsTester')
15
+
16
+ result = assert_has_child(app, :window) { |x|
17
+ @got_called = true
18
+ x.title == 'AXElementsTester'
19
+ }
20
+ assert_equal expected, result
21
+ assert @got_called
22
+ end
23
+
24
+ def test_assert_has_child_raises_in_failure_cases
25
+ e = assert_raises MiniTest::Assertion do
26
+ assert_has_child app, :button
27
+ end
28
+ assert_match /to have Button as a child/, e.message
29
+
30
+ e = assert_raises MiniTest::Assertion do
31
+ assert_has_child app, :button, title: 'Press Me'
32
+ end
33
+ assert_match /to have Button\(title: "Press Me"\) as a child/, e.message
34
+ end
35
+
36
+ def test_assert_has_descendent
37
+ expected = app.main_window.check_box
38
+
39
+ assert_equal expected, assert_has_descendent(app,:check_box)
40
+ assert_equal expected, assert_has_descendent(app,:check_box,title: /Box/)
41
+
42
+ result = assert_has_descendent app, :check_box do |x|
43
+ @got_called = true
44
+ x.title == 'Unchecked Check Box'
45
+ end
46
+ assert_equal expected, result
47
+ assert @got_called
48
+ end
49
+
50
+ def test_assert_has_descendent_raises_in_failure_cases
51
+ ancestor = app.main_window.slider
52
+ e = assert_raises MiniTest::Assertion do
53
+ assert_has_descendent ancestor, :window, title: /Cake/
54
+ end
55
+ assert_match /to have Window\(title: \/Cake\/\) as a descendent/, e.message
56
+ end
57
+
58
+ def test_refute_has_child
59
+ assert_nil refute_has_child(app,:button)
60
+ assert_nil refute_has_child(app,:window,title: 'Herp Derp')
61
+
62
+ result = refute_has_child app, :window do |x|
63
+ @got_called = true
64
+ x.title == 'Herp Derp'
65
+ end
66
+ assert_nil result
67
+ assert @got_called
68
+ end
69
+
70
+ def test_refute_has_child_raises_in_failure_cases
71
+ e = assert_raises MiniTest::Assertion do
72
+ refute_has_child app, :window
73
+ end
74
+ assert_match /not to have #{Regexp.escape(app.window.inspect)} as a child/, e.message
75
+ end
76
+
77
+ def test_refute_has_descendent
78
+ slider = app.main_window.slider
79
+
80
+ assert_nil refute_has_descendent(slider,:window)
81
+ assert_nil refute_has_descendent(slider,:element,title: 'Rhubarb')
82
+
83
+ result = refute_has_descendent slider, :element do |x|
84
+ @got_called = true
85
+ x.attributes.include?(:title) && x.title == 'Rhubarb'
86
+ end
87
+ assert_nil result
88
+ assert @got_called
89
+ end
90
+
91
+ def test_refute_has_descendent_raises_in_failure_cases
92
+ e = assert_raises MiniTest::Assertion do
93
+ refute_has_descendent app, :window
94
+ end
95
+ assert_match /#{Regexp.escape(app.window.inspect)} as a descendent/, e.message
96
+ end
97
+
98
+ end
@@ -0,0 +1,58 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'test/integration/helper'
3
+ require 'rspec/expectations/ax_elements'
4
+
5
+ class TestRSpecMatchers < MiniTest::Unit::TestCase
6
+
7
+ def app
8
+ @@app ||= AX::Application.new PID
9
+ end
10
+
11
+ def test_have_child_should_failure_message
12
+ m = Accessibility::HasChildMatcher.new(:window, {}) {}
13
+ e = app.main_window.slider
14
+
15
+ m.matches? e
16
+ assert_equal "Expected #{e.inspect} to have child Window[✔]",
17
+ m.failure_message_for_should
18
+ end
19
+
20
+ def test_have_child_should_not_failure_message
21
+ m = Accessibility::HasChildMatcher.new(:window, {})
22
+ e = app.window
23
+
24
+ m.does_not_match? app
25
+ assert_equal "Expected #{app.inspect} to NOT have child #{e.inspect}",
26
+ m.failure_message_for_should_not
27
+ end
28
+
29
+ def test_have_child_description
30
+ m = Accessibility::HasChildMatcher.new(:window,{}) {}
31
+ assert_equal 'should have a child that matches Window[✔]', m.description
32
+ end
33
+
34
+ def test_have_descendent_should_failure_message
35
+ m = Accessibility::HasDescendentMatcher.new(:button, {}) {}
36
+ q = Accessibility::Qualifier.new(:Button,{}) {}
37
+ e = app.main_window.slider
38
+
39
+ m.matches? e
40
+ assert_equal "Expected #{e.inspect} to have descendent Button[✔]",
41
+ m.failure_message_for_should
42
+ end
43
+
44
+ def test_have_descendent_should_not_failure_message
45
+ m = Accessibility::HasDescendentMatcher.new(:window,{})
46
+ e = app.window
47
+
48
+ m.does_not_match? app
49
+ assert_equal "Expected #{app.inspect} to NOT have descendent #{e.inspect}",
50
+ m.failure_message_for_should_not
51
+ end
52
+
53
+ def test_have_descendent_description
54
+ m = Accessibility::HasDescendentMatcher.new(:button,{})
55
+ assert_equal 'should have a descendent matching Button', m.description
56
+ end
57
+
58
+ end
@@ -0,0 +1,35 @@
1
+ require 'test/integration/helper'
2
+
3
+ class TestMouse < MiniTest::Unit::TestCase
4
+
5
+ def app
6
+ @@app ||= AX::Application.new REF
7
+ end
8
+
9
+ def test_drag_to
10
+ skip
11
+ starting_point = app.main_window.position
12
+ def element
13
+ app.main_window.title_ui_element
14
+ end
15
+
16
+ point = CGPointMake(400, 400)
17
+ Mouse.move_to element.to_point
18
+ Mouse.drag_to point, 2
19
+ highlight element, timeout: 5, color: NSColor.greenColor
20
+ assert NSPointInRect(point, element.bounds),
21
+ {
22
+ point: point.inspect,
23
+ bounds: element.bounds.inspect,
24
+ mouse: Mouse.current_position.inspect
25
+ }
26
+
27
+ point = CGPointMake(800, 200)
28
+ Mouse.move_to element.to_point
29
+ Mouse.drag_to point, 2
30
+ assert NSPointInRect(point, element.bounds)
31
+ ensure
32
+ set app.main_window, position: starting_point if starting_point
33
+ end
34
+
35
+ end