AXElements 0.7.8 → 0.8.0
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 -10
- data/README.markdown +7 -14
- data/ext/accessibility/key_coder/key_coder.c +7 -0
- data/lib/AXElements.rb +0 -2
- data/lib/accessibility/core.rb +180 -123
- data/lib/accessibility/dsl.rb +310 -191
- data/lib/accessibility/enumerators.rb +9 -8
- data/lib/accessibility/errors.rb +7 -8
- data/lib/accessibility/factory.rb +16 -9
- data/lib/accessibility/graph.rb +68 -22
- data/lib/accessibility/highlighter.rb +86 -0
- data/lib/accessibility/pp_inspector.rb +4 -4
- data/lib/accessibility/qualifier.rb +11 -9
- data/lib/accessibility/string.rb +12 -4
- data/lib/accessibility/translator.rb +19 -10
- data/lib/accessibility/version.rb +3 -1
- data/lib/accessibility.rb +42 -17
- data/lib/ax/application.rb +90 -30
- data/lib/ax/button.rb +5 -2
- data/lib/ax/element.rb +133 -149
- data/lib/ax/pop_up_button.rb +12 -0
- data/lib/ax/radio_button.rb +5 -2
- data/lib/ax/row.rb +2 -2
- data/lib/ax/static_text.rb +5 -2
- data/lib/ax/systemwide.rb +24 -12
- data/lib/ax_elements/awesome_print.rb +13 -0
- data/lib/ax_elements/exception_workaround.rb +5 -0
- data/lib/ax_elements/nsarray_compat.rb +1 -0
- data/lib/ax_elements.rb +2 -1
- data/lib/minitest/ax_elements.rb +60 -4
- data/lib/mouse.rb +47 -20
- data/lib/rspec/expectations/ax_elements.rb +180 -88
- data/rakelib/doc.rake +7 -0
- data/test/helper.rb +2 -1
- data/test/integration/accessibility/test_dsl.rb +126 -18
- data/test/integration/accessibility/test_errors.rb +1 -1
- data/test/integration/ax/test_element.rb +17 -0
- data/test/integration/minitest/test_ax_elements.rb +33 -38
- data/test/integration/rspec/expectations/test_ax_elements.rb +68 -19
- data/test/sanity/accessibility/test_core.rb +45 -37
- data/test/sanity/accessibility/test_highlighter.rb +56 -0
- data/test/sanity/ax/test_application.rb +8 -0
- data/test/sanity/ax/test_element.rb +7 -3
- data/test/sanity/minitest/test_ax_elements.rb +2 -0
- data/test/sanity/rspec/expectations/test_ax_elements.rb +3 -0
- data/test/sanity/test_accessibility.rb +9 -0
- data/test/sanity/test_mouse.rb +2 -2
- metadata +11 -38
- data/docs/AccessibilityTips.markdown +0 -119
- data/docs/Acting.markdown +0 -340
- data/docs/Debugging.markdown +0 -165
- data/docs/Inspecting.markdown +0 -261
- data/docs/KeyboardEvents.markdown +0 -122
- data/docs/NewBehaviour.markdown +0 -151
- data/docs/Notifications.markdown +0 -271
- data/docs/Searching.markdown +0 -250
- data/docs/TestingExtensions.markdown +0 -52
- data/docs/images/all_the_buttons.jpg +0 -0
- data/docs/images/next_version.png +0 -0
- data/docs/images/ui_hierarchy.dot +0 -34
- data/docs/images/ui_hierarchy.png +0 -0
- data/lib/accessibility/debug.rb +0 -164
- data/test/integration/accessibility/test_debug.rb +0 -44
- data/test/sanity/accessibility/test_debug.rb +0 -63
@@ -1,52 +0,0 @@
|
|
1
|
-
# Test Helpers
|
2
|
-
|
3
|
-
@todo Pretend that this does not exist yet
|
4
|
-
|
5
|
-
There are some types of assertions that you would like to make during
|
6
|
-
testing that simply does not make sense using the build in
|
7
|
-
assertions. These can include things like existence checks which you
|
8
|
-
would have to implement by searching, or ... that is really the only
|
9
|
-
one I have so far :)
|
10
|
-
|
11
|
-
## RSpec
|
12
|
-
|
13
|
-
RSpec 2 is the Marketcircle choice for implementing functional and
|
14
|
-
behavioural tests for apps using AXElements. It is great and offers
|
15
|
-
the flexibility and features that make using it very good for large
|
16
|
-
test suites.
|
17
|
-
|
18
|
-
You can load the RSpec matchers that AXElements adds by requiring
|
19
|
-
|
20
|
-
require 'rspec/ax_elements'
|
21
|
-
|
22
|
-
### Existence
|
23
|
-
|
24
|
-
Checking for the existence of an object in RSpec with AXElements looks
|
25
|
-
a bit awkward:
|
26
|
-
|
27
|
-
window.search(:button).should be_empty
|
28
|
-
# or
|
29
|
-
window.search(:button).should_not be_empty
|
30
|
-
|
31
|
-
That does not communicate intent as clearly as it could. What if you
|
32
|
-
could say something like:
|
33
|
-
|
34
|
-
window.should have_a :button
|
35
|
-
# or
|
36
|
-
window.should_not have_a :button
|
37
|
-
|
38
|
-
What if you have filters? Well, that is handled as well, though maybe
|
39
|
-
not as nicely:
|
40
|
-
|
41
|
-
window.should have_a(:button).with(title: 'Hello')
|
42
|
-
|
43
|
-
## Minitest
|
44
|
-
|
45
|
-
AXElements uses minitest for its own regression test suite. Minitest
|
46
|
-
is pretty cool, and there is good cause to support it as well. To that
|
47
|
-
end, we have provided the equivalent assertions.
|
48
|
-
|
49
|
-
You can load it like so:
|
50
|
-
|
51
|
-
require 'minitest/ax_elements'
|
52
|
-
|
Binary file
|
Binary file
|
@@ -1,34 +0,0 @@
|
|
1
|
-
digraph {
|
2
|
-
Application [label = "Application: Mail", style=filled]
|
3
|
-
MainWindow [label = "MainWindow: Inbox"]
|
4
|
-
Outline [label = "Outline"]
|
5
|
-
Table [label = "Table: Emails"]
|
6
|
-
TableRow1 [label = "TableRow: Spam"]
|
7
|
-
TableRow2 [label = "TableRow: Daily LOLcat"]
|
8
|
-
MenuBar [label = "MenuBar", style=filled]
|
9
|
-
MenuBarItem1 [label = "MenuBarItem: File", style=filled]
|
10
|
-
MenuBarItem2 [label = "MenuBarItem: Edit"]
|
11
|
-
MenuBarItem3 [label = "MenuBarItem: Help"]
|
12
|
-
Menu1 [label = "Menu", style=filled]
|
13
|
-
MenuItem1 [label = "MenuItem: Preferences"]
|
14
|
-
MenuItem2 [label = "MenuItem: Services", style=filled]
|
15
|
-
MenuItem3 [label = "MenuItem: Quit"]
|
16
|
-
Menu2 [label = "Menu", style=filled]
|
17
|
-
MenuItem4 [label = "MenuItem: Services Preferences", style=filled]
|
18
|
-
|
19
|
-
Application -> MainWindow
|
20
|
-
Application -> MenuBar
|
21
|
-
MainWindow -> Outline
|
22
|
-
MainWindow -> Table
|
23
|
-
Table -> TableRow1
|
24
|
-
Table -> TableRow2
|
25
|
-
MenuBar -> MenuBarItem1
|
26
|
-
MenuBar -> MenuBarItem2
|
27
|
-
MenuBar -> MenuBarItem3
|
28
|
-
MenuBarItem1 -> Menu1
|
29
|
-
Menu1 -> MenuItem1
|
30
|
-
Menu1 -> MenuItem2
|
31
|
-
Menu1 -> MenuItem3
|
32
|
-
MenuItem2 -> Menu2
|
33
|
-
Menu2 -> MenuItem4
|
34
|
-
}
|
Binary file
|
data/lib/accessibility/debug.rb
DELETED
@@ -1,164 +0,0 @@
|
|
1
|
-
require 'accessibility/version'
|
2
|
-
framework 'Cocoa'
|
3
|
-
|
4
|
-
##
|
5
|
-
# Collection of utility methods helpful when trying to debug issues.
|
6
|
-
module Accessibility::Debug
|
7
|
-
|
8
|
-
# Initialize the DEBUG value
|
9
|
-
@on = ENV.fetch 'AXDEBUG', $DEBUG
|
10
|
-
|
11
|
-
|
12
|
-
class << self
|
13
|
-
|
14
|
-
##
|
15
|
-
# Whether or not to turn on DEBUG features in AXElements. The
|
16
|
-
# value is initially inherited from `$DEBUG` but can be overridden
|
17
|
-
# by an environment variable named `AXDEBUG` or changed dynamically
|
18
|
-
# at runtime.
|
19
|
-
#
|
20
|
-
# @return [Boolean]
|
21
|
-
attr_accessor :on
|
22
|
-
alias_method :on?, :on
|
23
|
-
|
24
|
-
##
|
25
|
-
# Get a list of elements, starting with an element you give, and riding
|
26
|
-
# the hierarchy up to the top level object (i.e. the {AX::Application}).
|
27
|
-
#
|
28
|
-
# @example
|
29
|
-
#
|
30
|
-
# element = AX::DOCK.list.application_dock_item
|
31
|
-
# path_for element
|
32
|
-
# # => [AX::ApplicationDockItem, AX::List, AX::Application]
|
33
|
-
#
|
34
|
-
# @param [AX::Element]
|
35
|
-
# @return [Array<AX::Element>] the path in ascending order
|
36
|
-
def path *elements
|
37
|
-
element = elements.last
|
38
|
-
return path(elements << element.parent) if element.respond_to? :parent
|
39
|
-
return elements
|
40
|
-
end
|
41
|
-
|
42
|
-
##
|
43
|
-
# @note This is an unfinished feature
|
44
|
-
#
|
45
|
-
# Make a `dot` format graph of the tree, meant for graphing with
|
46
|
-
# GraphViz.
|
47
|
-
#
|
48
|
-
# @return [String]
|
49
|
-
def graph_subtree root
|
50
|
-
require 'accessibility/graph'
|
51
|
-
dot = Accessibility::Graph.new(root)
|
52
|
-
dot.build!
|
53
|
-
dot.to_s
|
54
|
-
end
|
55
|
-
|
56
|
-
##
|
57
|
-
# Dump a tree to the console, indenting for each level down the
|
58
|
-
# tree that we go, and inspecting each element.
|
59
|
-
#
|
60
|
-
# @example
|
61
|
-
#
|
62
|
-
# puts subtree_for app
|
63
|
-
#
|
64
|
-
# @return [String]
|
65
|
-
def text_subtree element
|
66
|
-
output = element.inspect + "\n"
|
67
|
-
# @todo should use each_child_with_level instead
|
68
|
-
enum = Accessibility::Enumerators::DepthFirst.new element
|
69
|
-
enum.each_with_level do |element, depth|
|
70
|
-
output << "\t"*depth + element.inspect + "\n"
|
71
|
-
end
|
72
|
-
output
|
73
|
-
end
|
74
|
-
|
75
|
-
##
|
76
|
-
# Highlight an element on screen. You can optionally specify the
|
77
|
-
# highlight colour or pass a timeout to automatically have the
|
78
|
-
# highlighter disappear.
|
79
|
-
#
|
80
|
-
# The highlighter is actually a window, so if you do not set a
|
81
|
-
# timeout, you will need to call `#stop` or `#close` on the `NSWindow`
|
82
|
-
# object that this method returns in order to get rid of the
|
83
|
-
# highlighter.
|
84
|
-
#
|
85
|
-
# You could use this method to highlight an arbitrary number of
|
86
|
-
# elements on screen, with a rainbow of colours for debugging.
|
87
|
-
#
|
88
|
-
# @example
|
89
|
-
#
|
90
|
-
# highlighter = highlight window.outline
|
91
|
-
# highlight window.outline.row, colour: NSColor.greenColor, timeout: 5
|
92
|
-
# highlighter.stop
|
93
|
-
#
|
94
|
-
# @param [AX::Element]
|
95
|
-
# @param [Hash] opts
|
96
|
-
# @option opts [Number] :timeout
|
97
|
-
# @option opts [NSColor] :colour
|
98
|
-
# @return [NSWindow]
|
99
|
-
def highlight element, opts = {}
|
100
|
-
app = NSApplication.sharedApplication
|
101
|
-
colour = opts[:colour] || opts[:color] || NSColor.magentaColor
|
102
|
-
window = highlight_window_for element.bounds, colour
|
103
|
-
|
104
|
-
if opts.has_key? :timeout
|
105
|
-
Dispatch::Queue.new('window_killer').after opts[:timeout] do
|
106
|
-
window.close
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
window
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
private
|
115
|
-
|
116
|
-
##
|
117
|
-
# Create the window that acts as the highlighted portion of the screen.
|
118
|
-
#
|
119
|
-
# @param [NSRect]
|
120
|
-
# @param [NSColor]
|
121
|
-
# @return [NSWindow]
|
122
|
-
def highlight_window_for bounds, colour
|
123
|
-
bounds.flip!
|
124
|
-
window = NSWindow.alloc.initWithContentRect bounds,
|
125
|
-
styleMask: NSBorderlessWindowMask,
|
126
|
-
backing: NSBackingStoreBuffered,
|
127
|
-
defer: true
|
128
|
-
|
129
|
-
window.setOpaque false
|
130
|
-
window.setAlphaValue 0.20
|
131
|
-
window.setLevel NSStatusWindowLevel
|
132
|
-
window.setBackgroundColor colour
|
133
|
-
window.setIgnoresMouseEvents true
|
134
|
-
window.setFrame bounds, display: false
|
135
|
-
window.makeKeyAndOrderFront NSApp
|
136
|
-
def window.stop
|
137
|
-
close
|
138
|
-
end
|
139
|
-
window
|
140
|
-
end
|
141
|
-
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
|
146
|
-
##
|
147
|
-
# AXElements extensions to `CGRect`.
|
148
|
-
class CGRect
|
149
|
-
##
|
150
|
-
# Treats the rect as belonging to one co-ordinate system and then
|
151
|
-
# converts it to the other system.
|
152
|
-
#
|
153
|
-
# This is useful because accessibility API's expect to work with
|
154
|
-
# the flipped co-ordinate system (origin in top left), but AppKit
|
155
|
-
# prefers to use the cartesian co-ordinate system (origin in bottom
|
156
|
-
# left).
|
157
|
-
#
|
158
|
-
# @return [CGRect]
|
159
|
-
def flip!
|
160
|
-
screen_height = NSMaxY(NSScreen.mainScreen.frame)
|
161
|
-
origin.y = screen_height - NSMaxY(self)
|
162
|
-
self
|
163
|
-
end
|
164
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
require 'test/integration/helper'
|
2
|
-
|
3
|
-
class TestAccessibilityDebug < MiniTest::Unit::TestCase
|
4
|
-
|
5
|
-
def app
|
6
|
-
@app ||= AX::Application.new PID
|
7
|
-
end
|
8
|
-
|
9
|
-
def test_path_returns_correct_elements_in_correct_order
|
10
|
-
list = Accessibility::Debug.path(app.window.close_button)
|
11
|
-
assert_equal 3, list.size
|
12
|
-
assert_instance_of AX::CloseButton, list.first
|
13
|
-
assert_instance_of AX::StandardWindow, list.second
|
14
|
-
assert_instance_of AX::Application, list.third
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_dump_works_for_nested_tab_groups
|
18
|
-
element = app.window.tab_group
|
19
|
-
output = Accessibility::Debug.subtree_for element
|
20
|
-
|
21
|
-
expected = [
|
22
|
-
['AX::TabGroup', 0],
|
23
|
-
['AX::RadioButton', 1], ['AX::RadioButton', 1], ['AX::TabGroup', 1],
|
24
|
-
['AX::RadioButton', 2], ['AX::RadioButton', 2], ['AX::TabGroup', 2],
|
25
|
-
['AX::RadioButton', 3], ['AX::RadioButton', 3], ['AX::TabGroup', 3],
|
26
|
-
['AX::RadioButton', 4], ['AX::RadioButton', 4],
|
27
|
-
['AX::Group', 4],
|
28
|
-
['AX::TextField', 5], ['AX::StaticText', 6],
|
29
|
-
['AX::TextField' , 5], ['AX::StaticText', 6]
|
30
|
-
]
|
31
|
-
|
32
|
-
refute_empty output
|
33
|
-
output = output.split("\n")
|
34
|
-
|
35
|
-
until output.empty?
|
36
|
-
line = output.shift
|
37
|
-
klass, indents = expected.shift
|
38
|
-
assert_equal indents, line.match(/^\t*/).to_a.first.length, line
|
39
|
-
line.strip!
|
40
|
-
assert_match /^\#<#{klass}/, line
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
require 'test/runner'
|
2
|
-
require 'accessibility/debug'
|
3
|
-
|
4
|
-
class TestAccessibilityDebug < MiniTest::Unit::TestCase
|
5
|
-
|
6
|
-
def mock_element
|
7
|
-
@mock = Object.new
|
8
|
-
def @mock.bounds; CGRectMake(100,100,100,100); end
|
9
|
-
@mock
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_debug_setting
|
13
|
-
assert_respond_to Accessibility::Debug, :on?
|
14
|
-
assert_respond_to Accessibility::Debug, :on=
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_highlight_returns_created_window
|
18
|
-
w = Accessibility::Debug.highlight mock_element
|
19
|
-
assert_kind_of NSWindow, w
|
20
|
-
assert_respond_to w, :stop
|
21
|
-
ensure
|
22
|
-
w.close if w
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_highlight_can_take_a_timeout
|
26
|
-
w = Accessibility::Debug.highlight mock_element, timeout: 0.1
|
27
|
-
assert w.visible?
|
28
|
-
sleep 0.15
|
29
|
-
refute w.visible? # Not exactly the assertion I want, but close enough
|
30
|
-
ensure
|
31
|
-
w.close if w
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_highlight_can_have_custom_colour
|
35
|
-
w = Accessibility::Debug.highlight mock_element, color: NSColor.cyanColor
|
36
|
-
assert w.backgroundColor == NSColor.cyanColor
|
37
|
-
w.close
|
38
|
-
|
39
|
-
# test both spellings of colour
|
40
|
-
w = Accessibility::Debug.highlight mock_element, colour: NSColor.purpleColor
|
41
|
-
assert w.backgroundColor == NSColor.purpleColor
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_highlight_highlights_correct_rect
|
45
|
-
w = Accessibility::Debug.highlight mock_element
|
46
|
-
assert_equal w.frame, mock_element.bounds.flip!
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
class TestCGRectExtensions < MiniTest::Unit::TestCase
|
52
|
-
|
53
|
-
def test_flipping
|
54
|
-
size = NSScreen.mainScreen.frame.size
|
55
|
-
assert_equal CGRectMake( 0, size.height, 0, 0), CGRectZero.dup.flip!
|
56
|
-
assert_equal CGRectMake(100, size.height-200,100,100), CGRectMake(100,100,100,100).flip!
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_flipping_twice_returns_to_original
|
60
|
-
assert_equal CGRectZero.dup, CGRectZero.dup.flip!.flip!
|
61
|
-
end
|
62
|
-
|
63
|
-
end
|