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.
- data/.yardopts +1 -2
- data/README.markdown +152 -88
- data/Rakefile +8 -103
- data/docs/Debugging.markdown +9 -2
- data/docs/KeyboardEvents.markdown +114 -49
- data/docs/Setting.markdown +1 -0
- data/docs/images/next_version.png +0 -0
- data/ext/accessibility/key_coder/extconf.rb +22 -0
- data/ext/accessibility/key_coder/key_coder.c +113 -0
- data/lib/AXElements.rb +2 -0
- data/lib/accessibility/core.rb +897 -0
- data/lib/accessibility/debug.rb +168 -0
- data/lib/accessibility/dsl.rb +697 -0
- data/lib/accessibility/enumerators.rb +104 -0
- data/lib/accessibility/errors.rb +32 -0
- data/lib/accessibility/factory.rb +153 -0
- data/lib/accessibility/graph.rb +150 -0
- data/lib/{ax_elements/inspector.rb → accessibility/pp_inspector.rb} +39 -28
- data/lib/accessibility/qualifier.rb +158 -0
- data/lib/accessibility/string.rb +494 -0
- data/lib/accessibility/translator.rb +178 -0
- data/lib/accessibility/version.rb +7 -0
- data/lib/accessibility.rb +79 -0
- data/lib/ax/application.rb +234 -0
- data/lib/{ax_elements/elements → ax}/button.rb +2 -0
- data/lib/ax/element.rb +518 -0
- data/lib/{ax_elements/elements → ax}/radio_button.rb +2 -0
- data/lib/ax/row.rb +37 -0
- data/lib/{ax_elements/elements → ax}/static_text.rb +2 -0
- data/lib/ax/systemwide.rb +86 -0
- data/lib/ax_elements/awesome_print.rb +25 -0
- data/lib/ax_elements/exception_workaround.rb +8 -0
- data/lib/ax_elements/nsarray_compat.rb +64 -0
- data/lib/ax_elements/vendor/inflection_data.rb +65 -0
- data/lib/ax_elements/vendor/inflections.rb +172 -0
- data/lib/ax_elements/vendor/inflector.rb +306 -0
- data/lib/ax_elements.rb +14 -25
- data/lib/minitest/ax_elements.rb +112 -12
- data/lib/mouse.rb +72 -46
- data/lib/rspec/expectations/ax_elements.rb +133 -6
- data/rakelib/doc.rake +13 -0
- data/rakelib/ext.rake +61 -0
- data/rakelib/gem.rake +28 -0
- data/rakelib/test.rake +53 -0
- data/test/helper.rb +11 -97
- data/test/integration/accessibility/test_core.rb +18 -0
- data/test/integration/accessibility/test_debug.rb +44 -0
- data/test/integration/accessibility/test_dsl.rb +225 -0
- data/test/integration/accessibility/test_enumerators.rb +122 -0
- data/test/integration/accessibility/test_errors.rb +38 -0
- data/test/integration/accessibility/test_notifications.rb +22 -0
- data/test/integration/accessibility/test_qualifier.rb +148 -0
- data/test/integration/ax/test_application.rb +56 -0
- data/test/integration/ax/test_element.rb +46 -0
- data/test/integration/ax/test_row.rb +23 -0
- data/test/integration/ax_elements/test_nsarray_compat.rb +43 -0
- data/test/integration/minitest/test_ax_elements.rb +98 -0
- data/test/integration/rspec/expectations/test_ax_elements.rb +58 -0
- data/test/integration/test_mouse.rb +35 -0
- data/test/sanity/accessibility/test_core.rb +553 -0
- data/test/sanity/accessibility/test_debug.rb +63 -0
- data/test/sanity/accessibility/test_dsl.rb +75 -0
- data/test/sanity/accessibility/test_errors.rb +10 -0
- data/test/sanity/accessibility/test_factory.rb +88 -0
- data/test/sanity/accessibility/test_pp_inspector.rb +110 -0
- data/test/sanity/accessibility/test_qualifier.rb +13 -0
- data/test/sanity/accessibility/test_string.rb +238 -0
- data/test/sanity/accessibility/test_translator.rb +145 -0
- data/test/sanity/ax/test_application.rb +90 -0
- data/test/sanity/ax/test_element.rb +80 -0
- data/test/sanity/ax/test_systemwide.rb +66 -0
- data/test/sanity/ax_elements/test_nsarray_compat.rb +16 -0
- data/test/sanity/ax_elements/test_nsobject_inspect.rb +11 -0
- data/test/sanity/minitest/test_ax_elements.rb +15 -0
- data/test/sanity/rspec/expectations/test_ax_elements.rb +12 -0
- data/test/sanity/test_ax_elements.rb +10 -0
- data/test/sanity/test_mouse.rb +19 -0
- metadata +111 -93
- data/LICENSE.txt +0 -25
- data/ext/key_coder/extconf.rb +0 -6
- data/ext/key_coder/key_coder.m +0 -77
- data/lib/ax_elements/accessibility/enumerators.rb +0 -104
- data/lib/ax_elements/accessibility/graph.rb +0 -118
- data/lib/ax_elements/accessibility/language.rb +0 -347
- data/lib/ax_elements/accessibility/qualifier.rb +0 -73
- data/lib/ax_elements/accessibility.rb +0 -166
- data/lib/ax_elements/core.rb +0 -541
- data/lib/ax_elements/element.rb +0 -593
- data/lib/ax_elements/elements/application.rb +0 -88
- data/lib/ax_elements/elements/row.rb +0 -30
- data/lib/ax_elements/elements/systemwide.rb +0 -46
- data/lib/ax_elements/macruby_extensions.rb +0 -255
- data/lib/ax_elements/notification.rb +0 -37
- data/lib/ax_elements/version.rb +0 -9
- data/test/elements/test_application.rb +0 -72
- data/test/elements/test_row.rb +0 -27
- data/test/elements/test_systemwide.rb +0 -38
- data/test/test_accessibility.rb +0 -127
- data/test/test_blankness.rb +0 -26
- data/test/test_core.rb +0 -448
- data/test/test_element.rb +0 -939
- data/test/test_enumerators.rb +0 -81
- data/test/test_inspector.rb +0 -130
- data/test/test_language.rb +0 -157
- data/test/test_macruby_extensions.rb +0 -303
- data/test/test_mouse.rb +0 -5
- data/test/test_search_semantics.rb +0 -143
data/lib/minitest/ax_elements.rb
CHANGED
|
@@ -1,19 +1,119 @@
|
|
|
1
|
+
require 'ax/element'
|
|
2
|
+
require 'accessibility/qualifier'
|
|
3
|
+
|
|
1
4
|
##
|
|
2
|
-
#
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
# AXElements assertions for MiniTest.
|
|
6
|
+
# [Learn more about minitest.](https://github.com/seattlerb/minitest)
|
|
7
|
+
class MiniTest::Assertions
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
# Test that an element has a specific child. For example, test
|
|
11
|
+
# that a table has a row with certain contents. You can pass any
|
|
12
|
+
# filters that you normally would during a search, including a block.
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
#
|
|
16
|
+
# assert_has_child table, :row, static_text: { value: 'Mark' }
|
|
17
|
+
#
|
|
18
|
+
# @param [AX::Element]
|
|
19
|
+
# @param [#to_s]
|
|
20
|
+
# @param [Hash]
|
|
21
|
+
# @return [AX::Element]
|
|
22
|
+
def assert_has_child parent, kind, filters = {}, &block
|
|
23
|
+
msg = message {
|
|
24
|
+
child = ax_search_id kind, filters, block
|
|
25
|
+
"Expected #{parent.inspect} to have #{child} as a child"
|
|
26
|
+
}
|
|
27
|
+
result = ax_check_children parent, kind, filters, block
|
|
28
|
+
refute result.blank?, msg
|
|
29
|
+
result
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Test that an element has a specifc descendent. For example, test
|
|
34
|
+
# that a window contains a specific label. You can pass any filters
|
|
35
|
+
# that you normally would during a search, including a block.
|
|
36
|
+
#
|
|
37
|
+
# @example
|
|
38
|
+
#
|
|
39
|
+
# assert_has_descendent window, :static_text, value: /Cake/
|
|
40
|
+
#
|
|
41
|
+
# @param [AX::Element]
|
|
42
|
+
# @param [#to_s]
|
|
43
|
+
# @param [Hash]
|
|
44
|
+
# @return [AX::Element]
|
|
45
|
+
def assert_has_descendent ancestor, kind, filters = {}, &block
|
|
46
|
+
msg = message {
|
|
47
|
+
descendent = ax_search_id kind, filters, block
|
|
48
|
+
"Expected #{ancestor.inspect} to have #{descendent} as a descendent"
|
|
49
|
+
}
|
|
50
|
+
result = ax_check_descendent ancestor, kind, filters, block
|
|
51
|
+
refute result.blank?, msg
|
|
52
|
+
result
|
|
13
53
|
end
|
|
54
|
+
alias_method :assert_has_descendant, :assert_has_descendent
|
|
14
55
|
|
|
15
|
-
|
|
56
|
+
##
|
|
57
|
+
# Test that an element _does not_ have a specific child. For example,
|
|
58
|
+
# test that a row is no longer in a table. You can pass any filters
|
|
59
|
+
# that you normally would during a search, including a block.
|
|
60
|
+
#
|
|
61
|
+
# @example
|
|
62
|
+
#
|
|
63
|
+
# refute_has_child table, :row, id: 'MyRow'
|
|
64
|
+
#
|
|
65
|
+
# @param [AX::Element]
|
|
66
|
+
# @param [#to_s]
|
|
67
|
+
# @param [Hash]
|
|
68
|
+
# @return [nil]
|
|
69
|
+
def refute_has_child parent, kind, filters = {}, &block
|
|
70
|
+
result = ax_check_children parent, kind, filters, block
|
|
71
|
+
msg = message {
|
|
72
|
+
"Expected #{parent.inspect} not to have #{result} as a child"
|
|
73
|
+
}
|
|
74
|
+
assert result.blank?, msg
|
|
75
|
+
result
|
|
16
76
|
end
|
|
77
|
+
|
|
78
|
+
##
|
|
79
|
+
# Test that an element _does not_ have a specific descendent. For
|
|
80
|
+
# example, test that a window does not contain a spinning progress
|
|
81
|
+
# indicator anymore.
|
|
82
|
+
#
|
|
83
|
+
# @example
|
|
84
|
+
#
|
|
85
|
+
# refute_has_descendent window, :busy_indicator
|
|
86
|
+
#
|
|
87
|
+
# @param [AX::Element]
|
|
88
|
+
# @param [#to_s]
|
|
89
|
+
# @param [Hash]
|
|
90
|
+
# @return [nil,Array()]
|
|
91
|
+
def refute_has_descendent ancestor, kind, filters = {}, &block
|
|
92
|
+
result = ax_check_descendent ancestor, kind, filters, block
|
|
93
|
+
msg = message {
|
|
94
|
+
"Expected #{ancestor.inspect} not to have #{result} as a descendent"
|
|
95
|
+
}
|
|
96
|
+
assert result.blank?, msg
|
|
97
|
+
result
|
|
98
|
+
end
|
|
99
|
+
alias_method :refute_has_descendant, :refute_has_descendent
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def ax_search_id kind, filters, block
|
|
105
|
+
Accessibility::Qualifier.new(kind, filters, &block).describe
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def ax_check_children parent, kind, filters, block
|
|
109
|
+
q = Accessibility::Qualifier.new(kind, filters, &block)
|
|
110
|
+
parent.attribute(:children).find { |x| q.qualifies? x }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def ax_check_descendent ancestor, kind, filters, block
|
|
114
|
+
ancestor.search(kind, filters, &block)
|
|
115
|
+
end
|
|
116
|
+
|
|
17
117
|
end
|
|
18
118
|
|
|
19
119
|
# @todo assertions for minitest/spec
|
data/lib/mouse.rb
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
framework 'ApplicationServices'
|
|
2
|
+
|
|
1
3
|
##
|
|
2
4
|
# [Reference](http://developer.apple.com/library/mac/#documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html).
|
|
3
5
|
#
|
|
4
6
|
# @todo Inertial scrolling
|
|
5
7
|
# @todo Bezier paths
|
|
6
8
|
# @todo More intelligent default duration
|
|
7
|
-
# @todo Point arguments should accept a pair tuple...or should they?
|
|
8
9
|
# @todo Refactor to try and reuse the same event for a single action
|
|
9
10
|
# instead of creating new events.
|
|
10
11
|
# @todo Pause between down/up clicks
|
|
11
|
-
module Mouse
|
|
12
|
-
|
|
13
|
-
class << Mouse
|
|
12
|
+
module Mouse
|
|
13
|
+
extend self
|
|
14
14
|
|
|
15
15
|
##
|
|
16
16
|
# Number of animation steps per second.
|
|
@@ -60,11 +60,11 @@ class << Mouse
|
|
|
60
60
|
# @param [CGPoint]
|
|
61
61
|
# @param [Float] duration animation duration, in seconds
|
|
62
62
|
def drag_to point, duration = 0.2
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
post new_event(KCGEventLeftMouseDown, current_position, KCGMouseButtonLeft)
|
|
64
|
+
|
|
65
65
|
animate KCGEventLeftMouseDragged, KCGMouseButtonLeft, current_position, point, duration
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
|
|
67
|
+
post new_event(KCGEventLeftMouseUp, current_position, KCGMouseButtonLeft)
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
##
|
|
@@ -78,18 +78,15 @@ class << Mouse
|
|
|
78
78
|
# @param [Fixnum] amount number of units to scroll; positive to scroll
|
|
79
79
|
# up or negative to scroll down
|
|
80
80
|
# @param [Float] duration animation duration, in seconds
|
|
81
|
-
# @param [
|
|
81
|
+
# @param [Symbol] units `:line` scrolls by line, `:pixel` scrolls by pixel
|
|
82
82
|
def scroll amount, duration = 0.2, units = :line
|
|
83
83
|
units = UNIT[units] || raise(ArgumentError, "#{units} is not a valid unit")
|
|
84
|
-
steps = (FPS * duration).
|
|
84
|
+
steps = (FPS * duration).round
|
|
85
85
|
current = 0.0
|
|
86
86
|
steps.times do |step|
|
|
87
87
|
done = (step+1).to_f / steps
|
|
88
88
|
scroll = ((done - current)*amount).floor
|
|
89
|
-
|
|
90
|
-
# on the mouse we are simulating (up to 3)
|
|
91
|
-
event = CGEventCreateScrollWheelEvent(nil, units, 1, scroll)
|
|
92
|
-
CGEventPost(KCGHIDEventTap, event)
|
|
89
|
+
post new_scroll_event(units, 1, scroll)
|
|
93
90
|
sleep QUANTUM
|
|
94
91
|
current += scroll.to_f / amount
|
|
95
92
|
end
|
|
@@ -99,24 +96,24 @@ class << Mouse
|
|
|
99
96
|
# A standard click. Default position is the current position.
|
|
100
97
|
#
|
|
101
98
|
# @param [CGPoint]
|
|
102
|
-
def click point = current_position
|
|
103
|
-
event =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
CGEventPost(KCGHIDEventTap, event)
|
|
99
|
+
def click point = current_position, duration = 12
|
|
100
|
+
event = new_event KCGEventLeftMouseDown, point, KCGMouseButtonLeft
|
|
101
|
+
post event
|
|
102
|
+
duration.times { sleep QUANTUM }
|
|
103
|
+
set event, to: KCGEventLeftMouseUp
|
|
104
|
+
post event
|
|
109
105
|
end
|
|
110
106
|
|
|
111
107
|
##
|
|
112
108
|
# Standard secondary click. Default position is the current position.
|
|
113
109
|
#
|
|
114
110
|
# @param [CGPoint]
|
|
115
|
-
def secondary_click point = current_position
|
|
116
|
-
event =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
111
|
+
def secondary_click point = current_position, duration = 12
|
|
112
|
+
event = new_event KCGEventRightMouseDown, point, KCGMouseButtonRight
|
|
113
|
+
post event
|
|
114
|
+
duration.times { sleep QUANTUM }
|
|
115
|
+
set event, to: KCGEventRightMouseUp
|
|
116
|
+
post event
|
|
120
117
|
end
|
|
121
118
|
alias_method :right_click, :secondary_click
|
|
122
119
|
|
|
@@ -125,16 +122,16 @@ class << Mouse
|
|
|
125
122
|
#
|
|
126
123
|
# @param [CGPoint]
|
|
127
124
|
def double_click point = current_position
|
|
128
|
-
event =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
event = new_event KCGEventLeftMouseDown, point, KCGMouseButtonLeft
|
|
126
|
+
post event
|
|
127
|
+
set event, to: KCGEventLeftMouseUp
|
|
128
|
+
post event
|
|
132
129
|
|
|
133
130
|
CGEventSetIntegerValueField(event, KCGMouseEventClickState, 2)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
set event, to: KCGEventLeftMouseDown
|
|
132
|
+
post event
|
|
133
|
+
set event, to: KCGEventLeftMouseUp
|
|
134
|
+
post event
|
|
138
135
|
end
|
|
139
136
|
|
|
140
137
|
##
|
|
@@ -151,9 +148,10 @@ class << Mouse
|
|
|
151
148
|
#
|
|
152
149
|
# @param [CGPoint]
|
|
153
150
|
# @param [Number]
|
|
154
|
-
def arbitrary_click point = current_position, button = KCGMouseButtonCenter
|
|
151
|
+
def arbitrary_click point = current_position, button = KCGMouseButtonCenter, duration = 12
|
|
155
152
|
event = CGEventCreateMouseEvent(nil, KCGEventOtherMouseDown, point, button)
|
|
156
153
|
CGEventPost(KCGHIDEventTap, event)
|
|
154
|
+
duration.times do sleep QUANTUM end
|
|
157
155
|
CGEventSetType(event, KCGEventOtherMouseUp)
|
|
158
156
|
CGEventPost(KCGHIDEventTap, event)
|
|
159
157
|
end
|
|
@@ -166,20 +164,48 @@ class << Mouse
|
|
|
166
164
|
# Executes a mouse movement animation. It can be a simple cursor
|
|
167
165
|
# move or a drag depending on what is passed to `type`.
|
|
168
166
|
def animate type, button, from, to, duration
|
|
169
|
-
|
|
170
|
-
xstep
|
|
171
|
-
ystep
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
167
|
+
current = current_position
|
|
168
|
+
xstep = (to.x - current.x) / (FPS * duration)
|
|
169
|
+
ystep = (to.y - current.y) / (FPS * duration)
|
|
170
|
+
start = NSDate.date
|
|
171
|
+
|
|
172
|
+
until close_enough?(current, to)
|
|
173
|
+
remaining = to.x - current.x
|
|
174
|
+
current.x += xstep.abs > remaining.abs ? remaining : xstep
|
|
175
|
+
|
|
176
|
+
remaining = to.y - current.y
|
|
177
|
+
current.y += ystep.abs > remaining.abs ? remaining : ystep
|
|
178
|
+
|
|
179
|
+
post new_event(type, current, button)
|
|
180
|
+
|
|
177
181
|
sleep QUANTUM
|
|
182
|
+
break if NSDate.date.timeIntervalSinceDate(start) > 5.0
|
|
183
|
+
current = current_position
|
|
178
184
|
end
|
|
179
|
-
|
|
180
|
-
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def close_enough? current, target
|
|
188
|
+
x = current.x - target.x
|
|
189
|
+
y = current.y - target.y
|
|
190
|
+
delta = Math.sqrt((x**2) + (y**2))
|
|
191
|
+
delta <= 1.0
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def new_event event, position, button
|
|
195
|
+
CGEventCreateMouseEvent(nil, event, position, button)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# @param [Fixnum] wheel which scroll wheel to use (value between 1-3)
|
|
199
|
+
def new_scroll_event units, wheel, amount
|
|
200
|
+
CGEventCreateScrollWheelEvent(nil, units, wheel, amount)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def post event
|
|
181
204
|
CGEventPost(KCGHIDEventTap, event)
|
|
182
|
-
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def set event, to: state
|
|
208
|
+
CGEventSetType(event, state)
|
|
183
209
|
end
|
|
184
210
|
|
|
185
211
|
end
|
|
@@ -1,15 +1,142 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'accessibility/qualifier'
|
|
2
|
+
require 'ax/element'
|
|
2
3
|
|
|
3
4
|
##
|
|
4
|
-
# Custom RSpec
|
|
5
|
-
|
|
5
|
+
# Custom matcher for RSpec to check if an element has the specified
|
|
6
|
+
# child element.
|
|
7
|
+
class Accessibility::HasChildMatcher
|
|
8
|
+
|
|
9
|
+
# @param [#to_s]
|
|
10
|
+
# @param [Hash]
|
|
11
|
+
# @yield
|
|
12
|
+
def initialize kind, filters, &block
|
|
13
|
+
@qualifier = Accessibility::Qualifier.new(kind, filters, &block)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param [AX::Element]
|
|
17
|
+
def matches? parent
|
|
18
|
+
!search(parent).blank?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [String]
|
|
22
|
+
def failure_message_for_should
|
|
23
|
+
"Expected #@parent to have child #{@qualifier.describe}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @param [AX::Element]
|
|
27
|
+
def does_not_match? parent
|
|
28
|
+
search(parent).blank?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [String]
|
|
32
|
+
def failure_message_for_should_not
|
|
33
|
+
"Expected #@parent to NOT have child #@result"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# Implemented to override `NSObject#description`.
|
|
38
|
+
#
|
|
39
|
+
# @return [String]
|
|
40
|
+
def description
|
|
41
|
+
"should have a child that matches #{@qualifier.describe}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def search parent
|
|
48
|
+
@parent = parent
|
|
49
|
+
@result = parent.attribute(:children).find { |x| @qualifier.qualifies? x }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Assert that the receiving element has the specified child element. You
|
|
56
|
+
# can use any filters you would normally use in a search, including
|
|
57
|
+
# a block.
|
|
58
|
+
#
|
|
59
|
+
# @example
|
|
60
|
+
#
|
|
61
|
+
# window.toolbar.should have_child(:search_field)
|
|
62
|
+
# table.should have_child(:row, static_text: { value: /42/ })
|
|
63
|
+
#
|
|
64
|
+
# search_field.should_not have_child(:busy_indicator)
|
|
65
|
+
#
|
|
66
|
+
# @param [#to_s]
|
|
67
|
+
# @param [Hash]
|
|
68
|
+
# @yield An optional block to be used as part of the search qualifier
|
|
69
|
+
def have_child kind, filters = {}, &block
|
|
70
|
+
Accessibility::HasChildMatcher.new kind, filters, &block
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
# Custom matcher for RSpec to check if an element has the specified
|
|
76
|
+
# descendent element.
|
|
77
|
+
class Accessibility::HasDescendentMatcher
|
|
78
|
+
|
|
79
|
+
# @param [#to_s]
|
|
80
|
+
# @param [Hash]
|
|
81
|
+
# @yield
|
|
82
|
+
def initialize kind, filters, &block
|
|
83
|
+
@kind, @filters, @block = kind, filters, block
|
|
84
|
+
@qualifier = Accessibility::Qualifier.new(@kind, @filters, &@block)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @param [AX::Element]
|
|
88
|
+
def matches? ancestor
|
|
89
|
+
!search(ancestor).blank?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @return [String]
|
|
93
|
+
def failure_message_for_should
|
|
94
|
+
"Expected #@ancestor to have descendent #{@qualifier.describe}"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @param [AX::Element]
|
|
98
|
+
def does_not_match? ancestor
|
|
99
|
+
search(ancestor).blank?
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @return [String]
|
|
103
|
+
def failure_message_for_should_not
|
|
104
|
+
"Expected #@ancestor to NOT have descendent #@result"
|
|
105
|
+
end
|
|
6
106
|
|
|
7
107
|
##
|
|
8
|
-
#
|
|
9
|
-
|
|
108
|
+
# Implemented to override `NSObject#description`.
|
|
109
|
+
#
|
|
110
|
+
# @return [String]
|
|
111
|
+
def description
|
|
112
|
+
"should have a descendent matching #{@qualifier.describe}"
|
|
10
113
|
end
|
|
11
114
|
|
|
12
|
-
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def search ancestor
|
|
119
|
+
@ancestor = ancestor
|
|
120
|
+
@result = ancestor.search(@kind, @filters, &@block)
|
|
13
121
|
end
|
|
14
122
|
|
|
15
123
|
end
|
|
124
|
+
|
|
125
|
+
##
|
|
126
|
+
# Assert that the given element has the specified descendent. You can
|
|
127
|
+
# pass any parameters you normally would use during a search,
|
|
128
|
+
# including a block.
|
|
129
|
+
#
|
|
130
|
+
# @example
|
|
131
|
+
#
|
|
132
|
+
# app.main_window.should have_descendent(:button, title: 'Press Me')
|
|
133
|
+
#
|
|
134
|
+
# row.should_not have_descendent(:check_box)
|
|
135
|
+
#
|
|
136
|
+
# @param [#to_s]
|
|
137
|
+
# @param [Hash]
|
|
138
|
+
# @yield An optional block to be used as part of the search qualifier
|
|
139
|
+
def have_descendent kind, filters = {}, &block
|
|
140
|
+
Accessibility::HasDescendentMatcher.new kind, filters, &block
|
|
141
|
+
end
|
|
142
|
+
alias :have_descendant :have_descendent
|
data/rakelib/doc.rake
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# begin
|
|
2
|
+
# require 'yard'
|
|
3
|
+
# YARD::Rake::YardocTask.new
|
|
4
|
+
# rescue LoadError => e
|
|
5
|
+
# $stderr.puts 'It seems as though you do not have yard installed.'
|
|
6
|
+
# command = ENV['RUBY_VERSION'] ? 'rake' : 'sudo macrake'
|
|
7
|
+
# $stderr.puts "You can install it by running `#{command} setup_dev`"
|
|
8
|
+
# end
|
|
9
|
+
|
|
10
|
+
# desc 'Generate Graphviz object graph'
|
|
11
|
+
# task :garden => :yard do
|
|
12
|
+
# sh 'yard graph --full --dependencies --dot="-Tpng:quartz" -f docs/images/AX.dot'
|
|
13
|
+
# end
|
data/rakelib/ext.rake
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module ExtHelpers
|
|
2
|
+
def macruby?
|
|
3
|
+
defined? MACRUBY_REVISION
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def cruby?
|
|
7
|
+
RUBY_ENGINE == 'ruby'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def needs_regeneration? source, bundle
|
|
11
|
+
return true unless File.exists? bundle
|
|
12
|
+
return true unless File.mtime(bundle) > File.mtime(source)
|
|
13
|
+
return true if macruby? && ext_platform(bundle) == :cruby
|
|
14
|
+
return true if cruby? && ext_platform(bundle) == :macruby
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def ext_platform bundle
|
|
18
|
+
return :macruby if `otool -L #{bundle}`.match /MacRuby/
|
|
19
|
+
return :cruby
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
namespace :ext do
|
|
25
|
+
extend ExtHelpers
|
|
26
|
+
|
|
27
|
+
desc 'Compile C extensions'
|
|
28
|
+
task :key_coder do
|
|
29
|
+
dir = 'ext/accessibility/key_coder'
|
|
30
|
+
ext = "#{dir}/key_coder"
|
|
31
|
+
if needs_regeneration? "#{ext}.c", "#{ext}.bundle"
|
|
32
|
+
Rake::Task['clobber_key_coder'].execute
|
|
33
|
+
Dir.chdir(dir) do
|
|
34
|
+
ruby 'extconf.rb'
|
|
35
|
+
sh 'make'
|
|
36
|
+
end
|
|
37
|
+
cp "#{ext}.bundle", 'lib/accessibility'
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
desc 'Remove files generated by compiling key_coder'
|
|
44
|
+
task :clobber_key_coder do
|
|
45
|
+
Dir.glob('{lib,ext}/**/*{.bundle,.o}').each do |file|
|
|
46
|
+
$stdout.puts "rm #{file}"
|
|
47
|
+
rm_f file
|
|
48
|
+
end
|
|
49
|
+
file = 'rm ext/accessibility/key_coder/Makefile'
|
|
50
|
+
$stdout.puts file
|
|
51
|
+
rm_f file
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
desc 'Remove files generated by compiling extensions'
|
|
55
|
+
task :clobber_ext => [:clobber_key_coder]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if RUBY_ENGINE == 'macruby'
|
|
59
|
+
require 'rake/compiletask'
|
|
60
|
+
Rake::CompileTask.new
|
|
61
|
+
end
|
data/rakelib/gem.rake
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'rubygems/package_task'
|
|
2
|
+
|
|
3
|
+
ax_elements = Gem::Specification.load('AXElements.gemspec')
|
|
4
|
+
Gem::PackageTask.new(ax_elements) { }
|
|
5
|
+
|
|
6
|
+
desc 'Build and install gem (not including deps)'
|
|
7
|
+
task :install => :gem do
|
|
8
|
+
require 'rubygems/installer'
|
|
9
|
+
Gem::Installer.new("pkg/#{ax_elements.file_name}").install
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
desc 'Install dependencies for development'
|
|
13
|
+
task :setup_dev do
|
|
14
|
+
require 'rubygems/dependency_installer'
|
|
15
|
+
spec.development_dependencies.each do |dep|
|
|
16
|
+
puts "Installing #{dep.name} (#{dep.requirement})"
|
|
17
|
+
Gem::DependencyInstaller.new.install(dep.name, dep.requirement)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
ax_typer = Gem::Specification.load('AXTyper.gemspec')
|
|
22
|
+
Gem::PackageTask.new(ax_typer) { }
|
|
23
|
+
|
|
24
|
+
desc 'Build and install AXTyper'
|
|
25
|
+
task :install_typer => :gem do
|
|
26
|
+
require 'rubygems/installer'
|
|
27
|
+
Gem::Installer.new("pkg/#{ax_typer.file_name}").install
|
|
28
|
+
end
|
data/rakelib/test.rake
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
desc 'Start up irb with AXElements loaded'
|
|
2
|
+
task :console => :ext do
|
|
3
|
+
irb = ENV['RUBY_VERSION'] ? 'irb' : 'macirb'
|
|
4
|
+
sh "#{irb} -Ilib -rubygems -rax_elements"
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
desc 'Open the fixture app'
|
|
8
|
+
task :run_fixture => :fixture do
|
|
9
|
+
sh 'open test/fixture/Release/AXElementsTester.app'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
desc 'Build the test fixture'
|
|
13
|
+
task :fixture do
|
|
14
|
+
sh 'cd test/AXElementsTester && xcodebuild'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc 'Remove the built fixture app'
|
|
18
|
+
task :clobber_fixture do
|
|
19
|
+
$stdout.puts 'rm -rf test/fixture'
|
|
20
|
+
rm_rf 'test/fixture'
|
|
21
|
+
end
|
|
22
|
+
task :clobber => :clobber_fixture
|
|
23
|
+
|
|
24
|
+
require 'rake/testtask'
|
|
25
|
+
namespace :test do
|
|
26
|
+
Rake::TestTask.new(:sanity) do |t|
|
|
27
|
+
t.libs << '.'
|
|
28
|
+
t.pattern = "test/sanity/**/test_*.rb"
|
|
29
|
+
end
|
|
30
|
+
task :sanity => [:ext, :fixture]
|
|
31
|
+
|
|
32
|
+
Rake::TestTask.new(:integration) do |t|
|
|
33
|
+
t.libs << '.'
|
|
34
|
+
t.pattern = "test/integration/**/test_*.rb"
|
|
35
|
+
end
|
|
36
|
+
task :integration => [:ext, :fixture]
|
|
37
|
+
|
|
38
|
+
desc 'Run tests for the string parser'
|
|
39
|
+
Rake::TestTask.new(:string) do |t|
|
|
40
|
+
t.libs << '.'
|
|
41
|
+
t.pattern = "test/sanity/accessibility/test_string.rb"
|
|
42
|
+
end
|
|
43
|
+
task :string => :ext
|
|
44
|
+
|
|
45
|
+
desc 'Run tests under CRuby (where applicable)'
|
|
46
|
+
task :cruby do
|
|
47
|
+
if ENV['RUBY_VERSION'] # using rvm
|
|
48
|
+
puts sh 'rvm 1.9.3 do rake test:string'
|
|
49
|
+
else
|
|
50
|
+
sh 'rake test:string'
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|