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,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