AXElements 0.6.0beta1
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 +20 -0
- data/LICENSE.txt +25 -0
- data/README.markdown +150 -0
- data/Rakefile +109 -0
- data/docs/AccessibilityTips.markdown +119 -0
- data/docs/Acting.markdown +340 -0
- data/docs/Debugging.markdown +326 -0
- data/docs/Inspecting.markdown +255 -0
- data/docs/KeyboardEvents.markdown +57 -0
- data/docs/NewBehaviour.markdown +151 -0
- data/docs/Notifications.markdown +271 -0
- data/docs/Searching.markdown +250 -0
- data/docs/TestingExtensions.markdown +52 -0
- data/docs/images/AX.png +0 -0
- data/docs/images/all_the_buttons.jpg +0 -0
- data/docs/images/ui_hierarchy.dot +34 -0
- data/docs/images/ui_hierarchy.png +0 -0
- data/ext/key_coder/extconf.rb +6 -0
- data/ext/key_coder/key_coder.m +77 -0
- data/lib/ax_elements/accessibility/enumerators.rb +104 -0
- data/lib/ax_elements/accessibility/language.rb +347 -0
- data/lib/ax_elements/accessibility/qualifier.rb +73 -0
- data/lib/ax_elements/accessibility.rb +164 -0
- data/lib/ax_elements/core.rb +541 -0
- data/lib/ax_elements/element.rb +593 -0
- data/lib/ax_elements/elements/application.rb +88 -0
- data/lib/ax_elements/elements/button.rb +18 -0
- data/lib/ax_elements/elements/radio_button.rb +18 -0
- data/lib/ax_elements/elements/row.rb +30 -0
- data/lib/ax_elements/elements/static_text.rb +17 -0
- data/lib/ax_elements/elements/systemwide.rb +46 -0
- data/lib/ax_elements/inspector.rb +116 -0
- data/lib/ax_elements/macruby_extensions.rb +255 -0
- data/lib/ax_elements/notification.rb +37 -0
- data/lib/ax_elements/version.rb +9 -0
- data/lib/ax_elements.rb +30 -0
- data/lib/minitest/ax_elements.rb +19 -0
- data/lib/mouse.rb +185 -0
- data/lib/rspec/expectations/ax_elements.rb +15 -0
- data/test/elements/test_application.rb +72 -0
- data/test/elements/test_row.rb +27 -0
- data/test/elements/test_systemwide.rb +38 -0
- data/test/helper.rb +119 -0
- data/test/test_accessibility.rb +127 -0
- data/test/test_blankness.rb +26 -0
- data/test/test_core.rb +448 -0
- data/test/test_element.rb +939 -0
- data/test/test_enumerators.rb +81 -0
- data/test/test_inspector.rb +121 -0
- data/test/test_language.rb +157 -0
- data/test/test_macruby_extensions.rb +303 -0
- data/test/test_mouse.rb +5 -0
- data/test/test_search_semantics.rb +143 -0
- metadata +219 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
##
|
2
|
+
# UI Element for the row in a table, outline, etc.
|
3
|
+
class AX::Row < AX::Element
|
4
|
+
|
5
|
+
##
|
6
|
+
# @todo Can this be more efficient? It wraps the columns twice,
|
7
|
+
# which is expensive if there are many columns. Needs to be
|
8
|
+
# done in such that we preserve search semantics for the
|
9
|
+
# filters.
|
10
|
+
#
|
11
|
+
# Retrieve the child in a row that corresponds to a specific column.
|
12
|
+
# You must pass filters here in the same way that you would for a
|
13
|
+
# search.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
#
|
17
|
+
# table.row[5].child_in_column(header: 'Price')
|
18
|
+
#
|
19
|
+
# @param [Hash]
|
20
|
+
# @return [AX::Element]
|
21
|
+
def child_in_column filters
|
22
|
+
table = self.parent
|
23
|
+
column = table.column(filters)
|
24
|
+
columns = table.columns
|
25
|
+
index = columns.index { |x| x == column }
|
26
|
+
return self.children.at(index) if index
|
27
|
+
raise AX::Element::SearchFailure.new(self.parent, 'column', filters)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
##
|
2
|
+
# Represents text on the screen that cannot directly be changed by
|
3
|
+
# a user, usually a label or an instructional text block.
|
4
|
+
class AX::StaticText < AX::Element
|
5
|
+
|
6
|
+
##
|
7
|
+
# Test equality with another object. Equality can be with another
|
8
|
+
# {AX::Element} or it can be with a string that matches the value
|
9
|
+
# of the static text.
|
10
|
+
#
|
11
|
+
# @return [Boolean]
|
12
|
+
def == other
|
13
|
+
return super unless other.kind_of? NSString
|
14
|
+
return attribute(:value) == other
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Represents the special `SystemWide` accessibility object.
|
5
|
+
class AX::SystemWide < AX::Element
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
##
|
9
|
+
# Overridden since there is only one way to get the element ref.
|
10
|
+
def initialize
|
11
|
+
ref = AXUIElementCreateSystemWide()
|
12
|
+
super ref, AX.attrs_of_element(ref)
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# @note With the `SystemWide` class, using {#type_string} will send the
|
17
|
+
# events to which ever app has focus.
|
18
|
+
#
|
19
|
+
# Generate keyboard events by simulating keyboard input.
|
20
|
+
def type_string string
|
21
|
+
AX.keyboard_action @ref, string
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Overridden to avoid a difficult to understand error message.
|
26
|
+
def search *args
|
27
|
+
raise NoMethodError, 'AX::SystemWide cannot search'
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Raises an `NoMethodError` instead of (possibly) silently failing to
|
32
|
+
# register for a notification.
|
33
|
+
#
|
34
|
+
# @raise [NoMethodError]
|
35
|
+
def on_notification *args
|
36
|
+
raise NoMethodError, 'AX::SystemWide cannot register for notifications'
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
##
|
43
|
+
# Singleton instance of the SystemWide element
|
44
|
+
#
|
45
|
+
# @return [AX::SystemWide]
|
46
|
+
AX::SYSTEM = AX::SystemWide.instance
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
##
|
4
|
+
# Convenience methods to use when building an `#inspect` method for
|
5
|
+
# {AX::Element} and its descendants.
|
6
|
+
#
|
7
|
+
# The module only expects three methods in order to operate:
|
8
|
+
#
|
9
|
+
# - `#attributes` returns a list of available attributes
|
10
|
+
# - `#attribute` returns the value of a given attribute
|
11
|
+
# - `#size_of` returns the size for an attribute
|
12
|
+
#
|
13
|
+
module Accessibility::PPInspector
|
14
|
+
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
##
|
19
|
+
# Added for backwards compatability with Snow Leopard.
|
20
|
+
#
|
21
|
+
# @return [String]
|
22
|
+
KAXIdentifierAttribute = 'AXIdentifier'.freeze
|
23
|
+
|
24
|
+
##
|
25
|
+
# @todo I feel a bit bad about having such a large method that has
|
26
|
+
# some inefficiencies.
|
27
|
+
#
|
28
|
+
# Create an identifier for `self` using various attributes that should
|
29
|
+
# make it very easy to identify the element.
|
30
|
+
#
|
31
|
+
# @return [String]
|
32
|
+
def pp_identifier
|
33
|
+
# use or lack of use of #inspect is intentional for visual effect
|
34
|
+
|
35
|
+
if attributes.include? KAXValueAttribute
|
36
|
+
val = attribute :value
|
37
|
+
if val.kind_of? NSString
|
38
|
+
return " #{val.inspect}" unless val.empty?
|
39
|
+
else
|
40
|
+
return " value=#{val.inspect}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if attributes.include? KAXTitleAttribute
|
45
|
+
val = attribute(:title)
|
46
|
+
return " #{val.inspect}" if val && !val.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
if attributes.include? KAXTitleUIElementAttribute
|
50
|
+
val = attribute :title_ui_element
|
51
|
+
return BUFFER + val.inspect if val
|
52
|
+
end
|
53
|
+
|
54
|
+
if attributes.include? KAXDescriptionAttribute
|
55
|
+
val = attribute(:description).to_s
|
56
|
+
return BUFFER + val unless val.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
if attributes.include? KAXIdentifierAttribute
|
60
|
+
return " id=#{attribute(:identifier)}"
|
61
|
+
end
|
62
|
+
|
63
|
+
# @todo should we have other fallbacks?
|
64
|
+
return ::EMPTY_STRING
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Create a string that succinctly encodes the screen coordinates
|
69
|
+
# of `self`.
|
70
|
+
#
|
71
|
+
# @return [String]
|
72
|
+
def pp_position
|
73
|
+
position = attribute :position
|
74
|
+
" (#{position.x}, #{position.y})"
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Create a string that nicely presents the number of children
|
79
|
+
# that `self` has.
|
80
|
+
#
|
81
|
+
# @return [String]
|
82
|
+
def pp_children
|
83
|
+
child_count = size_of :children
|
84
|
+
if child_count > 1
|
85
|
+
" #{child_count} children"
|
86
|
+
elsif child_count == 1
|
87
|
+
' 1 child'
|
88
|
+
else # there are some odd edge cases
|
89
|
+
::EMPTY_STRING
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Create a string that looks like a labeled check box. The label
|
95
|
+
# is the given attribute, and the check box value will be
|
96
|
+
# determined by the value of the attribute.
|
97
|
+
#
|
98
|
+
# @param [Symbol]
|
99
|
+
# @return [String]
|
100
|
+
def pp_checkbox attr
|
101
|
+
" #{attr}[#{attribute(attr) ? '✔' : '✘'}]"
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
##
|
108
|
+
# @private
|
109
|
+
#
|
110
|
+
# A string with a single space, used as a buffer. This is a
|
111
|
+
# performance hack.
|
112
|
+
#
|
113
|
+
# @return [String]
|
114
|
+
BUFFER = ' '.freeze
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Extensions to `NSArray`.
|
5
|
+
class NSArray
|
6
|
+
##
|
7
|
+
# Equivalent to `#[1]`
|
8
|
+
def second
|
9
|
+
at(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Equivalent to `#[2]`
|
14
|
+
def third
|
15
|
+
at(2)
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Create a `CGPoint` from the first two elements in the array.
|
20
|
+
#
|
21
|
+
# @return [CGPoint]
|
22
|
+
def to_point
|
23
|
+
CGPoint.new(first, second)
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Create a `CGSize` from the first two elements in the array.
|
28
|
+
#
|
29
|
+
# @return [CGSize]
|
30
|
+
def to_size
|
31
|
+
CGSize.new(first, second)
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Create a `CGRect` from the first four elements in the array.
|
36
|
+
#
|
37
|
+
# @return [CGRect]
|
38
|
+
def to_rect
|
39
|
+
CGRectMake(*self[0,4])
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# @method blank?
|
44
|
+
#
|
45
|
+
# Borrowed from ActiveSupport. Too bad this docstring isn't being
|
46
|
+
# picked up by YARD.
|
47
|
+
alias_method :blank?, :empty?
|
48
|
+
|
49
|
+
alias_method :ax_array_method_missing, :method_missing
|
50
|
+
##
|
51
|
+
# @todo We should really rethink what this does in terms of the
|
52
|
+
# semantics of the language. For instance, when we say something
|
53
|
+
# like "outline.rows.text_field" did we want the text field for
|
54
|
+
# each row or did we want a row that has a text field or did we
|
55
|
+
# want the first text field that is a child of one of the rows?
|
56
|
+
#
|
57
|
+
# If the array contains {AX::Element} objects and the method name
|
58
|
+
# belongs to an attribute then the method will be mapped
|
59
|
+
# across the array. In this case, you can artificially pluralize
|
60
|
+
# the attribute name and the lookup will singularize the method name
|
61
|
+
# for you.
|
62
|
+
#
|
63
|
+
# You also have to be careful in cases where the array contains
|
64
|
+
# various types of {AX::Element} objects that may not all respond to
|
65
|
+
# the same attribute.
|
66
|
+
def method_missing method, *args
|
67
|
+
if first.kind_of? AX::Element
|
68
|
+
return map(&method) if first.respond_to?(method)
|
69
|
+
return map(&singularized_method_name(method))
|
70
|
+
end
|
71
|
+
ax_array_method_missing method, *args
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
##
|
78
|
+
# Takes a method name and singularizes it, including the case where
|
79
|
+
# the method name is a predicate.
|
80
|
+
#
|
81
|
+
# @param [Symbol] method
|
82
|
+
# @return [Symbol]
|
83
|
+
def singularized_method_name method
|
84
|
+
(method.predicate? ? method[0..-2] : method).singularize.to_sym
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
##
|
90
|
+
# @private
|
91
|
+
#
|
92
|
+
# An empty string, performance hack.
|
93
|
+
#
|
94
|
+
# @return [String]
|
95
|
+
EMPTY_STRING = ''.freeze
|
96
|
+
|
97
|
+
##
|
98
|
+
# Extensions to `NSString`.
|
99
|
+
class NSString
|
100
|
+
##
|
101
|
+
# Used to test a symbol/string representing a method name.
|
102
|
+
# Returns `true` if the string ends with a '?".
|
103
|
+
def predicate?
|
104
|
+
self[-1] == '?'
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# Force the #singularize method to be defined on NSString objects,
|
109
|
+
# and therefore on Symbol objects...at least until that bug gets
|
110
|
+
# fixed.
|
111
|
+
#
|
112
|
+
# @return [String]
|
113
|
+
def singularize
|
114
|
+
ActiveSupport::Inflector.singularize(self)
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Returns the upper camel case version of the string. The string
|
119
|
+
# is assumed to be in `snake_case`, but still works on a string that
|
120
|
+
# is already in camel case.
|
121
|
+
#
|
122
|
+
# I have this method update the string in-place as it is a fairly hot
|
123
|
+
# method and should perform well; by running in-place we save an
|
124
|
+
# allocation (which is slow on MacRuby right now).
|
125
|
+
#
|
126
|
+
# Returns `nil` the string was empty.
|
127
|
+
#
|
128
|
+
# @return [String,nil] returns `self`
|
129
|
+
def camelize
|
130
|
+
gsub /(?:^|_)(.)/ do $1.upcase! end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
##
|
136
|
+
# Extensions to `CGPoint`.
|
137
|
+
class CGPoint
|
138
|
+
##
|
139
|
+
# Get the center point in a rectangle.
|
140
|
+
#
|
141
|
+
# @param [CGRect] rect
|
142
|
+
# @return [CGPoint]
|
143
|
+
def self.center_of_rect rect
|
144
|
+
rect.origin.center rect.size
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Find the center of a rectangle, treating `self` as the origin and
|
149
|
+
# the given `size` as the size of the rectangle.
|
150
|
+
#
|
151
|
+
# @param [CGSize] size
|
152
|
+
# @return [CGPoint]
|
153
|
+
def center size
|
154
|
+
x = self.x + (size.width / 2.0)
|
155
|
+
y = self.y + (size.height / 2.0)
|
156
|
+
CGPoint.new(x, y)
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# @note This method does not show up in the documentation with
|
161
|
+
# YARD 0.7.x
|
162
|
+
#
|
163
|
+
# @return [CGPoint]
|
164
|
+
alias_method :to_point, :self
|
165
|
+
|
166
|
+
@ax_value = KAXValueCGPointType
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
##
|
171
|
+
# Extensions to `Boxed` objects.
|
172
|
+
class Boxed
|
173
|
+
class << self
|
174
|
+
##
|
175
|
+
# The `AXValue` constant for the struct type. Not all structs
|
176
|
+
# have a value.
|
177
|
+
#
|
178
|
+
# @return [AXValueType]
|
179
|
+
attr_reader :ax_value
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# Create an `AXValue` from the `Boxed` instance. This will only
|
184
|
+
# work if for a few boxed types, check the AXAPI documentation.
|
185
|
+
#
|
186
|
+
# @return [AXValueRef]
|
187
|
+
def to_axvalue
|
188
|
+
klass = self.class
|
189
|
+
ptr = Pointer.new klass.type
|
190
|
+
ptr.assign self
|
191
|
+
AXValueCreate(klass.ax_value, ptr)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
##
|
197
|
+
# Extensions to `CGSize`.
|
198
|
+
class CGSize
|
199
|
+
@ax_value = KAXValueCGSizeType
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
##
|
204
|
+
# Extensions to `CGRect`.
|
205
|
+
class CGRect
|
206
|
+
@ax_value = KAXValueCGRectType
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
##
|
211
|
+
# Extensions to `CFRange`.
|
212
|
+
class CFRange
|
213
|
+
@ax_value = KAXValueCFRangeType
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
##
|
218
|
+
# Extensions to `NilClass`.
|
219
|
+
class NilClass
|
220
|
+
##
|
221
|
+
# Borrowed from Active Support.
|
222
|
+
def blank?
|
223
|
+
true
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
# ##
|
229
|
+
# # Correct a problem with ArgumentError not providing a proper backtrace.
|
230
|
+
# class ArgumentError
|
231
|
+
# alias_method :original_message, :message
|
232
|
+
# def message
|
233
|
+
# "#{original_message}\n\t#{backtrace.join("\n\t")}"
|
234
|
+
# end
|
235
|
+
# end
|
236
|
+
|
237
|
+
|
238
|
+
# ##
|
239
|
+
# # Correct a problem with NameError not providing a proper backtrace.
|
240
|
+
# class NameError
|
241
|
+
# alias_method :original_message, :message
|
242
|
+
# def message
|
243
|
+
# "#{original_message}\n\t#{backtrace.join("\n\t")}"
|
244
|
+
# end
|
245
|
+
# end
|
246
|
+
|
247
|
+
|
248
|
+
# ##
|
249
|
+
# # Correct a problem with NoMethodError not providing a proper backtrace.
|
250
|
+
# class NoMethodError
|
251
|
+
# alias_method :original_message, :message
|
252
|
+
# def message
|
253
|
+
# "#{original_message}\n\t#{backtrace.join("\n\t")}"
|
254
|
+
# end
|
255
|
+
# end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Accessibility::Notification
|
2
|
+
|
3
|
+
attr_reader :observer
|
4
|
+
attr_reader :element
|
5
|
+
attr_reader :string
|
6
|
+
|
7
|
+
def callback observer, element, notif, refcon
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize element, string
|
11
|
+
@element = element
|
12
|
+
@string = string
|
13
|
+
end
|
14
|
+
|
15
|
+
def register
|
16
|
+
code = AXObserverAddNotification(observer, element, string, nil)
|
17
|
+
log_error element, code unless code.zero?
|
18
|
+
end
|
19
|
+
|
20
|
+
def unregister
|
21
|
+
case code = AXObserverRemoveNotification(observer, element, string)
|
22
|
+
when KAXErrorNotificationNotRegistered
|
23
|
+
Accessibility.log.warn "Notif no longer registered: (#{ref}:#{notif})"
|
24
|
+
when KAXErrorIllegalArgument
|
25
|
+
raise ArgumentError, "Notif not unregistered (#{ref}:#{notif})"
|
26
|
+
else
|
27
|
+
log_error element, code unless code.zero?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def == other
|
32
|
+
observer == other.observer &&
|
33
|
+
element == other.element &&
|
34
|
+
string == other.string
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/ax_elements.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
framework 'Cocoa'
|
2
|
+
|
3
|
+
# check that the Accessibility APIs are enabled and are available to MacRuby
|
4
|
+
begin
|
5
|
+
unless AXAPIEnabled()
|
6
|
+
raise RuntimeError, <<-EOS
|
7
|
+
Universal Access is disabled on this machine. Please enable it in the System Preferences.
|
8
|
+
EOS
|
9
|
+
end
|
10
|
+
rescue NoMethodError
|
11
|
+
raise NotImplementedError, <<-EOS
|
12
|
+
You need to install the latest BridgeSupport preview for AXElements to work.
|
13
|
+
EOS
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'ax_elements/macruby_extensions'
|
17
|
+
require 'ax_elements/version'
|
18
|
+
require 'ax_elements/core'
|
19
|
+
require 'ax_elements/element'
|
20
|
+
|
21
|
+
require 'ax_elements/accessibility/language'
|
22
|
+
|
23
|
+
# Mix the language methods in to the TopLevel
|
24
|
+
include Accessibility::Language
|
25
|
+
|
26
|
+
##
|
27
|
+
# The Mac OS X dock application.
|
28
|
+
#
|
29
|
+
# @return [AX::Application]
|
30
|
+
AX::DOCK = Accessibility.application_with_bundle_identifier 'com.apple.dock'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
class MiniTest::Unit::TestCase
|
4
|
+
# @yield
|
5
|
+
def assert_exists
|
6
|
+
begin
|
7
|
+
yield.blank?
|
8
|
+
# blank? if nothing found, kind_of Element otherwise
|
9
|
+
rescue AX::Element::SearchFailure => e
|
10
|
+
# problem somewhere in the path
|
11
|
+
end
|
12
|
+
assert_respond_to
|
13
|
+
end
|
14
|
+
|
15
|
+
def refute_exists
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @todo assertions for minitest/spec
|