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
@@ -1,104 +0,0 @@
1
- ##
2
- # @abstract
3
- #
4
- # Common code for all enumerators.
5
- class Accessibility::AbstractEnumerator
6
- include Enumerable
7
-
8
- ##
9
- # Caches the root.
10
- #
11
- # @param [AX::Element] root
12
- def initialize root
13
- @root = root
14
- end
15
-
16
- end
17
-
18
- ##
19
- # Enumerator for visiting each element in a UI hierarchy in breadth
20
- # first order.
21
- class Accessibility::BFEnumerator < Accessibility::AbstractEnumerator
22
-
23
- ##
24
- # @todo Lazy-wrap element refs, might make things a bit faster
25
- # for fat trees; what is impact on thin trees?
26
- # @todo See if we can implement method in a single loop
27
- #
28
- # Semi-lazily iterate through the tree.
29
- #
30
- # @yieldparam [AX::Element] element a descendant of the root element
31
- def each
32
- queue = [@root]
33
- until queue.empty?
34
- queue.shift.attribute(:children).each do |x|
35
- queue << x if x.attributes.include? KAXChildrenAttribute
36
- yield x
37
- end
38
- end
39
- end
40
-
41
- ##
42
- # Explicitly defined so that escaping at the first found element
43
- # actually works. Since only a single `break` is called when an item
44
- # is found it does not fully escape the method.
45
- #
46
- # Technically, we need to do this with other 'escape-early' iteraters,
47
- # but they aren't being used...yet.
48
- def find
49
- each { |x| return x if yield x }
50
- end
51
-
52
- end
53
-
54
- ##
55
- # Enumerator for visitng each element in a UI hierarchy in
56
- # depth first order.
57
- class Accessibility::DFEnumerator < Accessibility::AbstractEnumerator
58
-
59
- # @yieldparam [AX::Element] element a descendant of the root
60
- def each
61
- stack = @root.attribute(:children)
62
- until stack.empty?
63
- current = stack.shift
64
- yield current
65
- if current.attributes.include? KAXChildrenAttribute
66
- # need to reverse it since child ordering seems to matter in practice
67
- stack.unshift *current.attribute(:children)
68
- end
69
- end
70
- end
71
-
72
- ##
73
- # @todo A bit of a hack that I would like to fix one day...
74
- #
75
- # Walk the UI element tree and yield both the element and the depth
76
- # in three relative to the root.
77
- #
78
- # @yieldparam [AX::Element]
79
- # @yieldparam [Number]
80
- def each_with_height &block
81
- @root.attribute(:children).each do |element|
82
- recursive_each_with_height element, 1, block
83
- end
84
- end
85
-
86
-
87
- private
88
-
89
- ##
90
- # Recursive implementation of a depth first iterator.
91
- #
92
- # @param [AX::Element]
93
- # @param [Number]
94
- # @param [#call]
95
- def recursive_each_with_height element, depth, block
96
- block.call element, depth
97
- if element.attributes.include? KAXChildrenAttribute
98
- element.attribute(:children).each do |x|
99
- recursive_each_with_height x, depth + 1, block
100
- end
101
- end
102
- end
103
-
104
- end
@@ -1,118 +0,0 @@
1
- ##
2
- # They see me graphing, they hatin, patrollin they can't catch me graphin' dirty.
3
- class Accessibility::Graph
4
-
5
- ##
6
- # Exploit the ordering of a breadth-first enumeration to simplify the
7
- # creation of edges for the graph. This only works because the UI
8
- # hiearchy is a simple tree.
9
- #
10
- # @return [Array<Accessibility::Graph::Node>]
11
- attr_reader :edge_queue
12
-
13
- ##
14
- # List of nodes in the UI hierarchy.
15
- #
16
- # @return [Array<Accessibility::Graph::Node>]
17
- attr_reader :nodes
18
-
19
- ##
20
- # A node in the UI hierarchy. Used by {Accessibility::Graph} in order
21
- # to build Graphviz dot graphs.
22
- class Node
23
-
24
- # @return [AX::Element]
25
- attr_reader :ref
26
-
27
- ##
28
- # Unique identifier for the node.
29
- #
30
- # @return [String]
31
- attr_reader :id
32
-
33
- ##
34
- # Label to use for displaying the node.
35
- #
36
- # @return [String]
37
- attr_reader :label
38
-
39
- ##
40
- # Shape to draw the node as.
41
- #
42
- # @return [String]
43
- attr_reader :shape
44
-
45
- ##
46
- # Colour to fill the node with.
47
- #
48
- # @return [String]
49
- attr_reader :colour
50
-
51
- # @param [AX::Element]
52
- def initialize element
53
- @ref = element
54
- @id = "element_#{element.object_id}"
55
- @label = element.class.to_s
56
- @shape = 3 # based on size?
57
- @colour = 3 # rotate a la minitest?
58
- end
59
- end
60
-
61
- ##
62
- # List of edges in the graph.
63
- #
64
- # @return [Hash{Accessibility::Graph::Node=>Accessibility::Graph::Node}]
65
- attr_reader :edges
66
-
67
- # @param [AX::Element]
68
- def initialize root
69
- @nodes = []
70
- @edges = {}
71
- @edge_queue = [:root] # hack
72
- add_node root
73
- end
74
-
75
- ##
76
- # Construct the list of nodes and edges for the graph...
77
- def build!
78
- Accessibility::BFEnumerator.new(nodes.first.ref).each do |element|
79
- add_node element
80
- end
81
- end
82
-
83
- ##
84
- #
85
- #
86
- # @return [String]
87
- def to_s
88
- graph = "digraph {\n"
89
- graph << nodes_list
90
- graph << edges_list
91
- graph << "}\n"
92
- end
93
-
94
- def nodes_list
95
- nodes.reduce('') do |string, node|
96
- string << "#{node.name} [label=\"#{node.label}\"]\n"
97
- end
98
- end
99
-
100
- def edges_list
101
- edges.delete_if { |_,v| v == :root } # remove hack
102
- edges.reduce('') do |string, pair|
103
- string << "#{pair.second.name} -> #{pair.first.name}\n"
104
- end
105
- end
106
-
107
- def add_node element
108
- node = Node.new(element)
109
- nodes << node
110
- edges[node] = edge_queue.shift
111
- if element.respond_to? :children
112
- element.size_of(:children).times do
113
- edge_queue << node
114
- end
115
- end
116
- end
117
-
118
- end
@@ -1,347 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'mouse'
4
-
5
- ##
6
- # @todo Allow the animation duration to be overridden for Mouse stuff?
7
- #
8
- # The idea here is to pull actions out from an object and put them
9
- # in front of object to give AXElements more of a DSL feel to make
10
- # communicating test steps more clear. See the
11
- # {file:docs/Acting.markdown Acting tutorial} for examples on how to use
12
- # methods from this module.
13
- module Accessibility::Language
14
-
15
- # @group Actions
16
-
17
- ##
18
- # We assume that any method that has the first argument with a type
19
- # of {AX::Element} is intended to be an action and so `#method_missing`
20
- # will forward the message to the element.
21
- #
22
- # @param [String] method an action constant
23
- def method_missing method, *args
24
- arg = args.first
25
- unless arg.kind_of? AX::Element
26
- # should be able to just call super, but there is a bug in MacRuby (#1320)
27
- # so we just recreate what should be happening
28
- message = "undefined method `#{method}' for #{self}:#{self.class}"
29
- raise NoMethodError, message
30
- end
31
- arg.perform_action method
32
- end
33
-
34
- ##
35
- # Try to perform the `press` action on the given element.
36
- #
37
- # @param [AX::Element]
38
- # @return [Boolean]
39
- def press element
40
- element.perform_action :press
41
- end
42
-
43
- ##
44
- # Try to perform the `show_menu` action on the given element.
45
- #
46
- # @param [AX::Element]
47
- # @return [Boolean]
48
- def show_menu element
49
- element.perform_action :show_menu
50
- end
51
-
52
- ##
53
- # Try to perform the `pick` action on the given element.
54
- #
55
- # @param [AX::Element]
56
- # @return [Boolean]
57
- def pick element
58
- element.perform_action :pick
59
- end
60
-
61
- ##
62
- # Try to perform the `decrement` action on the given element.
63
- #
64
- # @param [AX::Element]
65
- # @return [Boolean]
66
- def decrement element
67
- element.perform_action :decrement
68
- end
69
-
70
- ##
71
- # Try to perform the `confirm` action on the given element.
72
- #
73
- # @param [AX::Element]
74
- # @return [Boolean]
75
- def confirm element
76
- element.perform_action :confirm
77
- end
78
-
79
- ##
80
- # Try to perform the `increment` action on the given element.
81
- #
82
- # @param [AX::Element]
83
- # @return [Boolean]
84
- def increment element
85
- element.perform_action :increment
86
- end
87
-
88
- ##
89
- # Try to perform the `delete` action on the given element.
90
- #
91
- # @param [AX::Element]
92
- # @return [Boolean]
93
- def delete element
94
- element.perform_action :delete
95
- end
96
-
97
- ##
98
- # Try to perform the `cancel` action on the given element.
99
- #
100
- # @param [AX::Element]
101
- # @return [Boolean]
102
- def cancel element
103
- element.perform_action :cancel
104
- end
105
-
106
- ##
107
- # Tell an app to hide itself.
108
- #
109
- # @param [AX::Application]
110
- # @return [Boolean]
111
- def hide app
112
- app.perform_action :hide
113
- end
114
-
115
- ##
116
- # Tell an app to unhide itself, which does not guarantee it will be
117
- # focused.
118
- #
119
- # @param [AX::Application]
120
- # @return [Boolean]
121
- def unhide app
122
- app.perform_action :unhide
123
- end
124
-
125
- ##
126
- # Tell an app to quit.
127
- #
128
- # @param [AX::Application]
129
- # @return [Boolean]
130
- def terminate app
131
- app.perform_action :terminate
132
- end
133
-
134
- ##
135
- # @note This method overrides `Kernel#raise` so we have to check the
136
- # class of the first argument to decide which code path to take.
137
- #
138
- # Try to perform the `press` action on the given element.
139
- #
140
- # @overload raise element
141
- # @param [AX::Element] element
142
- # @return [Boolean]
143
- #
144
- # @overload raise exception[, message[, backtrace]]
145
- # The normal way to raise an exception.
146
- def raise *args
147
- arg = args.first
148
- arg.kind_of?(AX::Element) ? arg.perform_action(:raise) : super
149
- end
150
-
151
- ##
152
- # Focus an element on the screen, but do not set focus again if
153
- # already focused.
154
- #
155
- # @param [AX::Element]
156
- def set_focus element
157
- element.set_attribute(:focused, true) unless element.attribute(:focused?)
158
- end
159
-
160
- ##
161
- # @note We try to set focus to the element first; this is to avoid false
162
- # positives where developers assumed an element would have to have
163
- # focus before a user could change the value.
164
- #
165
- # You would think that the `#set` method should belong to {AX::Element},
166
- # but I think taking it out of the class and putting it in front helps
167
- # make the difference between performing actions and inspecting UI more
168
- # concrete.
169
- #
170
- # @overload set element, attribute_name: new_value
171
- # Set a specified attribute to a new value
172
- # @param [AX::Element] element
173
- # @param [Hash{attribute_name=>new_value}] change
174
- #
175
- # @overload set element, new_value
176
- # Set the `value` attribute to a new value
177
- # @param [AX::Element] element
178
- # @param [Object] change
179
- #
180
- # @return [nil] do not rely on a return value
181
- def set element, change
182
- set_focus element if element.attribute_writable? :focused
183
- key, value = change.is_a?(Hash) ? change.first : [:value, change]
184
- element.set_attribute key, value
185
- end
186
-
187
- ##
188
- # Simulate keyboard input by typing out the given string. To learn
189
- # more about how to encode modifier keys (e.g. Command), see the
190
- # dedicated documentation page on
191
- # {file:docs/KeyboardEvents.markdown Keyboard Events}.
192
- #
193
- # @overload type string
194
- # Send input to the currently focused application
195
- # @param [#to_s]
196
- #
197
- # @overload type string, app
198
- # Send input to a specific application
199
- # @param [#to_s]
200
- # @param [AX::Application]
201
- def type string, app = AX::SYSTEM
202
- app.type_string string.to_s
203
- end
204
-
205
- # @group Notifications
206
-
207
- ##
208
- # @todo Change this to `register_for_notification:from:` when the
209
- # syntax is supported by YARD (v0.8) or someone complains,
210
- # which ever comes first.
211
- #
212
- # @param [AX::Element]
213
- # @param [String]
214
- def register_for_notification element, notif, &block
215
- element.on_notification notif, &block
216
- end
217
-
218
- ##
219
- # Pause script execution until notification that has been registered
220
- # for is received or the full timeout period has passed.
221
- #
222
- # If the script is unpaused because of a timeout, then it is assumed
223
- # that the notification was never received and all notification
224
- # registrations will be unregistered to avoid future complications.
225
- #
226
- # @param [Float] timeout number of seconds to wait for a notification
227
- def wait_for_notification timeout = 10.0
228
- AX.wait_for_notif(timeout).tap { |_| unregister_notifications }
229
- end
230
-
231
- ##
232
- # Undo _all_ notification registries.
233
- def unregister_notifications
234
- AX.unregister_notifs
235
- end
236
-
237
- # @group Mouse Input
238
-
239
- ##
240
- # @overload move_mouse_to(element)
241
- # Move the mouse to a UI element
242
- # @param [AX::Element]
243
- #
244
- # @overload move_mouse_to(point)
245
- # Move the mouse to an arbitrary point
246
- # @param [CGPoint]
247
- #
248
- # @overload move_mouse_to([x,y])
249
- # Move the mouse to an arbitrary point given as an two element array
250
- # @param [Array(Float,Float)]
251
- def move_mouse_to arg
252
- Mouse.move_to arg.to_point
253
- end
254
-
255
- ##
256
- # There are many reasons why you would want to cause a drag event
257
- # with the mouse. Perhaps you want to drag an object to another
258
- # place, or maybe you want to hightlight an area of the screen.
259
- #
260
- # This method will drag the mouse from its current point on the screen
261
- # to the point given by calling `#to_point` on the argument.
262
- #
263
- # Generally, you will pass a {CGPoint} or some kind of {AX::Element},
264
- # but you could pass anything that responds to #to_point.
265
- #
266
- # @param [#to_point]
267
- def drag_mouse_to arg
268
- Mouse.drag_to point.to_point
269
- end
270
-
271
- ##
272
- # @todo Need to expose the units option? Would allow scrolling by pixel.
273
- #
274
- # Scrolls an arbitrary number of lines at the mouses current point on
275
- # the screen. Use a positive number to scroll down, and a negative number
276
- # to scroll up.
277
- #
278
- # If the second argument is provided then the mouse will move to that
279
- # point first; the argument must respond to `#to_point`.
280
- #
281
- # @param [Number]
282
- # @param [#to_point]
283
- def scroll lines, obj = nil
284
- move_mouse_to obj if obj
285
- Mouse.scroll lines
286
- end
287
-
288
- ##
289
- # Perform a regular click.
290
- #
291
- # If an argument is provided then the mouse will move to that point
292
- # first; the argument must respond to `#to_point`.
293
- #
294
- # @param [#to_point]
295
- def click obj = nil
296
- move_mouse_to obj if obj
297
- Mouse.click
298
- end
299
-
300
- ##
301
- # Perform a right (aka secondary) click action.
302
- #
303
- # If an argument is provided then the mouse will move to that point
304
- # first; the argument must respond to `#to_point`.
305
- #
306
- # @param [#to_point]
307
- def right_click obj = nil
308
- move_mouse_to obj if obj
309
- Mouse.right_click
310
- end
311
- alias_method :secondary_click, :right_click
312
-
313
- ##
314
- # Perform a double click action.
315
- #
316
- # If an argument is provided then the mouse will move to that point
317
- # first; the argument must respond to `#to_point`.
318
- #
319
- # @param [#to_point]
320
- def double_click obj = nil
321
- move_mouse_to obj if obj
322
- Mouse.double_click
323
- end
324
-
325
- # @group Macros
326
-
327
- ##
328
- # Show the "About" window for an app.
329
- #
330
- # @param [AX::Application]
331
- def show_about_window_for app
332
- set_focus app
333
- press app.menu_bar_item(title:(app.title))
334
- press app.menu_bar.menu_item(title: "About #{app.title}")
335
- end
336
-
337
- ##
338
- # Try to open the preferences for an app using the menu bar.
339
- #
340
- # @param [AX::Application]
341
- def show_preferences_window_for app
342
- set_focus app
343
- press app.menu_bar_item(title:(app.title))
344
- press app.menu_bar.menu_item(title:'Preferences…')
345
- end
346
-
347
- end
@@ -1,73 +0,0 @@
1
- ##
2
- # Used in searches to answer whether or not a given element meets the
3
- # expected criteria.
4
- class Accessibility::Qualifier
5
-
6
- ##
7
- # Initialize a qualifier with the kind of object that you want to
8
- # qualify and a dictionary of filter criteria.
9
- #
10
- # @param [String,Symbol]
11
- # @param [Hash]
12
- def initialize klass, criteria
13
- @sym = klass
14
- @criteria = criteria
15
- end
16
-
17
- ##
18
- # Whether or not a candidate object matches the criteria given
19
- # at initialization.
20
- #
21
- # @param [AX::Element] element
22
- def qualifies? element
23
- return false unless the_right_type? element
24
- return false unless meets_criteria? element
25
- return true
26
- end
27
-
28
-
29
- private
30
-
31
- ##
32
- # Checks if a candidate object is of the correct class, respecting
33
- # that that the class being searched for may not be defined yet.
34
- #
35
- # @param [AX::Element]
36
- def the_right_type? element
37
- unless @klass
38
- if AX.const_defined? @sym
39
- @klass = AX.const_get @sym
40
- else
41
- return false
42
- end
43
- end
44
- return element.kind_of? @klass
45
- end
46
-
47
- ##
48
- # @todo How could we handle filters that use parameterized
49
- # attributes?
50
- # @todo Optimize searching by compiling filters into an
51
- # optimized filter qualifier. `eval` is not an option.
52
- #
53
- # Determines if the element meets all the criteria of the filters,
54
- # spawning sub-searches if necessary.
55
- #
56
- # @param [AX::Element] element
57
- def meets_criteria? element
58
- @criteria.all? do |filter, value|
59
- if value.kind_of? Hash
60
- if element.attributes.include? KAXChildrenAttribute
61
- !element.search(filter, value).blank?
62
- else
63
- false
64
- end
65
- elsif element.respond_to? filter
66
- element.send(filter) == value
67
- else # this legitimately occurs
68
- false
69
- end
70
- end
71
- end
72
-
73
- end