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.
Files changed (54) hide show
  1. data/.yardopts +20 -0
  2. data/LICENSE.txt +25 -0
  3. data/README.markdown +150 -0
  4. data/Rakefile +109 -0
  5. data/docs/AccessibilityTips.markdown +119 -0
  6. data/docs/Acting.markdown +340 -0
  7. data/docs/Debugging.markdown +326 -0
  8. data/docs/Inspecting.markdown +255 -0
  9. data/docs/KeyboardEvents.markdown +57 -0
  10. data/docs/NewBehaviour.markdown +151 -0
  11. data/docs/Notifications.markdown +271 -0
  12. data/docs/Searching.markdown +250 -0
  13. data/docs/TestingExtensions.markdown +52 -0
  14. data/docs/images/AX.png +0 -0
  15. data/docs/images/all_the_buttons.jpg +0 -0
  16. data/docs/images/ui_hierarchy.dot +34 -0
  17. data/docs/images/ui_hierarchy.png +0 -0
  18. data/ext/key_coder/extconf.rb +6 -0
  19. data/ext/key_coder/key_coder.m +77 -0
  20. data/lib/ax_elements/accessibility/enumerators.rb +104 -0
  21. data/lib/ax_elements/accessibility/language.rb +347 -0
  22. data/lib/ax_elements/accessibility/qualifier.rb +73 -0
  23. data/lib/ax_elements/accessibility.rb +164 -0
  24. data/lib/ax_elements/core.rb +541 -0
  25. data/lib/ax_elements/element.rb +593 -0
  26. data/lib/ax_elements/elements/application.rb +88 -0
  27. data/lib/ax_elements/elements/button.rb +18 -0
  28. data/lib/ax_elements/elements/radio_button.rb +18 -0
  29. data/lib/ax_elements/elements/row.rb +30 -0
  30. data/lib/ax_elements/elements/static_text.rb +17 -0
  31. data/lib/ax_elements/elements/systemwide.rb +46 -0
  32. data/lib/ax_elements/inspector.rb +116 -0
  33. data/lib/ax_elements/macruby_extensions.rb +255 -0
  34. data/lib/ax_elements/notification.rb +37 -0
  35. data/lib/ax_elements/version.rb +9 -0
  36. data/lib/ax_elements.rb +30 -0
  37. data/lib/minitest/ax_elements.rb +19 -0
  38. data/lib/mouse.rb +185 -0
  39. data/lib/rspec/expectations/ax_elements.rb +15 -0
  40. data/test/elements/test_application.rb +72 -0
  41. data/test/elements/test_row.rb +27 -0
  42. data/test/elements/test_systemwide.rb +38 -0
  43. data/test/helper.rb +119 -0
  44. data/test/test_accessibility.rb +127 -0
  45. data/test/test_blankness.rb +26 -0
  46. data/test/test_core.rb +448 -0
  47. data/test/test_element.rb +939 -0
  48. data/test/test_enumerators.rb +81 -0
  49. data/test/test_inspector.rb +121 -0
  50. data/test/test_language.rb +157 -0
  51. data/test/test_macruby_extensions.rb +303 -0
  52. data/test/test_mouse.rb +5 -0
  53. data/test/test_search_semantics.rb +143 -0
  54. metadata +219 -0
@@ -0,0 +1,255 @@
1
+ # Inspecting The User Interface
2
+
3
+ When it comes to inspecting the user interface there are _many_ nooks
4
+ and crannies that are interesting to talk about. This document covers
5
+ the core concepts that you will need to understand before you can
6
+ begin discovering them yourself.
7
+
8
+ ## The UI Tree
9
+
10
+ The first most important thing to understand is that accessibility
11
+ exposes the user interface as a hierarchy of user interface
12
+ tokens. Each token references a GUI element on screen, either
13
+ something literal like a button, or something more structural like a
14
+ group of buttons. For simplicity, I will refer to tokens as if they were the
15
+ objects themselves.
16
+
17
+ Each object knows who its parent object is, and knows about any
18
+ children objects that it may have. thus creating a tree structure. At
19
+ least this is the theory, but there are, on occasion, some hiccups
20
+ since accessibility is a protocol to which multiple parties have to
21
+ conform.
22
+
23
+ A sample hierarchy might start with the application, which has a menu
24
+ bar as one of its children and the menu bar has menu bar items, each
25
+ menu bar item has a menu and each menu has menu items; some menu items
26
+ have another menu as its child which then leads to more menu items and
27
+ so on. This hierarchy is much easier to understand once visualized:
28
+
29
+ ![Example GUI Hierarchy](images/ui_hierarchy.png)
30
+
31
+ This example is meant to be instructive, the menu bar is one of the
32
+ more complicated hierarchies to navigate and many other nodes in the
33
+ hierarchy have been left out. The good news is that AXElements has
34
+ techniques and patterns for simplifying navigation, so don't get
35
+ scared off just yet. The point here is that each menu item knows which
36
+ menu is its parent, and each menu knows which menu item or menu bar
37
+ item is its parent, and the menu bar knows that which application is
38
+ its parent. But who is the parent of the application? It turns out
39
+ that that is a trick question, an application does not have a
40
+ parent. An application is the entry point for the UI hierarchy, it
41
+ will be the place where a script usually starts and application
42
+ objects can be created using the {Accessibility} singleton
43
+ methods. You can create the object for an application that is already
44
+ running using {Accessibility.application_with_name} like so:
45
+
46
+ app = Accessibility.application_with_name = 'Finder'
47
+
48
+ ### Accessibility Inspector
49
+
50
+ To quickly navigate through the UI tree, Apple has provided a tool,
51
+ the Accessibility Inspector, as part of the Developer Tools. The
52
+ inspector will come in handy when you are writing scripts using
53
+ AXElements, though there are some potential pitfalls that will be
54
+ discussed later.
55
+
56
+ Once you install the Developer Tools, the inspector can be found in
57
+ `/Developer/Applications/Utilities/Accessibility Tools/`. It is worth
58
+ playing around with the inspector to get a feel for what the
59
+ accessibility APIs offer; but keep in mind that the inspector is a
60
+ dumb interface to the accessibility APIs.
61
+
62
+ ## Attributes
63
+
64
+ Each item in the GUI tree has attributes; buttons have a title,
65
+ sliders have a value, etc.. Pointers to the parent and chilrden nodes
66
+ are also attributes. Programmatically, you can get a list of
67
+ attributes that an object has by asking nicely. AXElements exposes
68
+ this API via {AX::Element#attributes}. {AX::Element} actually acts as
69
+ the abstract base class for all objects, encapsulating everything that
70
+ the accessibility APIs offer.
71
+
72
+ ### Accessing Attributes
73
+
74
+ Every attribute can be accessed as a method of the UI object. The
75
+ method name will always be the
76
+ [snake_case](http://en.wikipedia.org/wiki/Letter_case) version of the
77
+ attribute name without the prefix.
78
+
79
+ Some examples:
80
+
81
+ - `AXChildren` would become `children`
82
+ - `AXMainWindow` would become `main_window`
83
+ - `AXIsApplicationRunning` would become `application_running?`
84
+
85
+ The last case is special because we consider "`Is`" to be part of the
86
+ prefix and the method name has a "`?`" at the end. This is to follow
87
+ Ruby conventions of putting "`?`" at the end of the method name if the
88
+ method is a predicate. There will be more details about these rules in
89
+ other tutorial documents, but this is really something that should be
90
+ abstracted away. This detail is not hidden right now becaues the
91
+ Accessibility Inspector does not hide the information and you still
92
+ need to understand it in order to use the inspector with AXElements.
93
+
94
+ #### Example
95
+
96
+ We can demonstrate how this all comes together with a small
97
+ example. In the terminal, you can start up a console session of
98
+ AXElements by loading `ax_elements` in `macirb` or by navigating to
99
+ the AXElements repository and running the `console` task if you have a
100
+ clone. Then you can try the following code, one line at a time:
101
+
102
+ app = Accessibility.application_with_name 'Terminal'
103
+ window = app.main_window
104
+ title = window.title
105
+ puts "The window's title is '#{title}'"
106
+
107
+ In the first line, we are creating the object for the application. As
108
+ mentioned earlier, you will usually start navigating the UI tree from
109
+ the application object. Giving the name of the application is the
110
+ easiest way to create an application object but requires the
111
+ application to already be running.
112
+
113
+ On the second line we use the application object and ask it for the
114
+ value of the `main_window` attribute. This will return to us another
115
+ UI object, this time for the window. You will also notice that the
116
+ console printed out some extra information about the window, such as
117
+ the title of the window and its position (in flipped
118
+ coordinates). Each UI element has implemented the `#inspect` method in
119
+ such a way as to provide users with a succinct but useful way to
120
+ identify the UI element on the screen, and `macirb` is designed to
121
+ happily print that information out for each statement that you enter.
122
+
123
+ On the third line, we ask the window for it's `title`, and it gives
124
+ us back a string which we then print out on the fourth line. Notice
125
+ that the title of the window was also printed by the console as part
126
+ of the `#inspect` output that `macirb` asks to print out.
127
+
128
+ ### Inspect Output
129
+
130
+ Using `#inspect` is a great way to see the important details of a UI
131
+ element, it shows the values of the most important attributes so that
132
+ you can quickly identify which element it really is on screen, but not
133
+ so many details that it becomes a pain. A typical example of
134
+ `#inspect` output looks like this:
135
+
136
+ #<AX::StandardWindow "AXElementsTester" (1584.0, 184.0) 17 children focused[✘]>
137
+
138
+ That output includes all the pieces that you will normally see from
139
+ `#inspect`, but you may see less or more depending on the UI element
140
+ that is being inspected. As is the norm in Ruby, you will always at
141
+ least get the name of the class; then AXElements will try to include a
142
+ piece of identifying information such as the `title`, then you have
143
+ numbers in parentheses which are the screen coordinates for the
144
+ object, then you have the number of children, and then check boxes for
145
+ boolean attributes. Aside from the class name, the other pieces of
146
+ information will only be included if they are relevant, and certain
147
+ objects will also include other information.
148
+
149
+ The values shown by `#inspect` are pieced together using helper
150
+ methods from the {Accessibility::PPInspector} module. {AX::Element}
151
+ implements a generic implementation with
152
+ {AX::Element#inspect}. However, the generic `#inspect` may not always
153
+ choose the best attributes to show. An example would be
154
+ {AX::Application#inspect}, which overrides the generic `inspect` so
155
+ that the process identifier for the application is also included. In
156
+ other cases, the screen co-ordinates or whether the element is enabled
157
+ may not be relevant, so you can override the method in the specific
158
+ subclass to not include those attributes and/or include other
159
+ attributes. The key idea is to make `#inspect` helpful when exploring
160
+ a UI through the console or when debugging a script.
161
+
162
+ ## Accessing Children
163
+
164
+ Following first principles shown in the example from above you might
165
+ be led to believe that in order to navigate around the UI tree you
166
+ will have to write code that looks something like this:
167
+
168
+ app.main_window.children.find do |child|
169
+ child.class == AX::Button && child.title == 'Add'
170
+ end
171
+
172
+ However, AXElements provides a way to specify what you want that is
173
+ much more convenient to use. Behold:
174
+
175
+ app.main_window.button(title: 'Add')
176
+
177
+ This is quite the simplification! If we break it down, you see that
178
+ the method name is the class of the object you want, and then if you
179
+ need to be more specific you can pass key-value pairs where the key
180
+ is an attribute and the value is the expected value. The above example
181
+ says "find a button with the title of 'Add'".
182
+
183
+ You can use as many or as few key-value pairs as you need in order to
184
+ find the element that you are looking for. If you do not specify any
185
+ key-value pairs, then the first object with the correct class will be
186
+ chosen. The {file:docs/Searching.markdown Searching Tutorial} goes
187
+ into more depth on how key-value pairs are used to specify which
188
+ object you want.
189
+
190
+ ## Parameterized Attributes
191
+
192
+ There is a special type of attribute that is called the parameterized
193
+ attribute. The difference from a regular attribute is that you need to
194
+ supply a parameter. An example of this would look like this:
195
+
196
+ static_text.string_for_range CFRange.new(0,5)
197
+
198
+ The method name suggests that you need to provide a range and in
199
+ return you will be given part of the string that corresponds to the
200
+ range. Of course, this example is quite contrived since string slicing
201
+ is so trivial in Ruby (but this parameterized attribute actually exists).
202
+
203
+ Parameterized attributes are different enough from regular attributes
204
+ that Apple does not want them mixing together and producing
205
+ offspring. AXElements is a bit progressive, but still keeps the list
206
+ of parameterized attributes separate from attributes; you can get a
207
+ list of parameterized attributes for an object with
208
+ {AX::Element#param_attributes}. Similarly, you have probably already
209
+ noticed that parameterized attributes have their own section in the
210
+ Accessibility Inspector and that many objectss do not have any
211
+ parameterized attributes.
212
+
213
+ In my experience, parameterized attributes have not been very useful,
214
+ but I haven't looked hard enough and am still looking for a good
215
+ example to put in this section of the tutorial.
216
+
217
+ ## Explicit Attribute Access
218
+
219
+ In cases where you know what you want is going to be an attribute, you
220
+ can get better performance from accessing attributes by calling
221
+ {AX::Element#attribute} and passing the attribute name.
222
+
223
+ app.attribute(:main_window)
224
+
225
+ Similarly, for parameterized attributes, you need to call
226
+ {AX::Element#param_attribute} and pass the attribute name and
227
+ parameter as parameters to that method.
228
+
229
+ app.param_attribute(:string_for_range, CFRange.new(0,5))
230
+
231
+ These methods are exposed so that other library classes can achieve
232
+ better performance; but you should avoid using them regularly. These
233
+ APIs may be hidden in the future in order to enforce the DSL usage.
234
+
235
+ ## Adding Accessibility Attributes
236
+
237
+ You can add custom attributes to objects, or even inject or hide
238
+ objects from the UI hierarchy. It is simply a matter of
239
+ overriding/implementing methods from the
240
+ [NSAccessibility](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Protocols/NSAccessibility_Protocol/Reference/Reference.html)
241
+ protocol where needed.
242
+
243
+ You should peruse the {file:docs/AccessibilityTips.markdown Accessibility Tips}
244
+ documentation before making nontrivial changes. There are a couple of
245
+ guidelines you need to be aware of in order to make sure things remain
246
+ compatible with AXElements.
247
+
248
+ ## Next Steps
249
+
250
+ You may want to play with what you have learnt so far, see if you can
251
+ find bugs and then fix them, or perhaps add missing features. ;)
252
+
253
+ From here the next logical step would be to figure out how to trigger
254
+ some sort of action and then inspect the UI for changes; for that
255
+ topic you should read the {file:docs/Acting.markdown Acting Tutorial}.
@@ -0,0 +1,57 @@
1
+ # Keyboard Events
2
+
3
+ @todo Still a little bit to do in this doc
4
+
5
+ In some cases you cannot, or do not want to, set the value of a field
6
+ directly by assigning to the accessibility object's value
7
+ attribute. In these cases you can post keyboard events to an
8
+ application object.
9
+
10
+ Other cases for using keyboard input simulation might include keyboard
11
+ navigation or hotkey activation.
12
+
13
+ The DSL exposes Keyboard events through the `type` keyword. An example
14
+ would look like this:
15
+
16
+ ```ruby
17
+ type "hello, world!", app
18
+ ```
19
+
20
+ In order make sure that certain sequences of characters are properly
21
+ transcribed to the screen you should be consistent about using double
22
+ quotes, `"`, for literal strings. An escape sequence should begin
23
+ `\\`.
24
+
25
+ First the keyword `type`, and then the string you want to type
26
+ out. Finally, there is a second argument, which is optional, that
27
+ should be the application that you wish to send the keyboard input
28
+ to; if you do not include the argument, then the input will go to the
29
+ currently activate application.
30
+
31
+ ## Behaviour
32
+
33
+ The method is asynchronous. It appears to type information in at the
34
+ keyboard repeat rate, but I have yet to check that out.
35
+
36
+ ## Escape Sequences
37
+
38
+ A number of custom escape sequences have been added in order to allow easy
39
+ encoding of all key sequences/combinations.
40
+
41
+ <table style="1px solid black">
42
+ <tr><td>Key</td><td>Escape Sequence</td></tr>
43
+ <tr><td>Delete Backwards (Backspace)</td><td>`\b`</td></tr>
44
+ <tr><td>Enter</td><td>`\n` or `\r`</td></tr>
45
+ <tr><td>Control</td><td>`\^`</td></tr>
46
+ <tr><td>Command</td><td>`\C`</td></tr>
47
+ <tr><td>Option/Alt</td><td>`\O`</td></tr>
48
+ <tr><td>Tab</td><td>`\t`</td></tr>
49
+ <tr><td>Escape</td><td>`\e`</td></tr>
50
+ <tr><td>Space</td><td>` `</td></tr>
51
+ <tr><td>Scroll To Top</td><td>`\x01`</td></tr>
52
+ <tr><td>Scroll To Bottom</td><td>`\x04`</td></tr>
53
+ <tr><td>Page Down</td><td>`\f`</td></tr>
54
+ <tr><td>Left Arrow</td><td>`\\<->`</td></tr>
55
+ <tr><td>Right Arrow</td><td>`\\->`</td></tr>
56
+ <tr><td>Down Arrow</td><td>`\DOWN`</td></tr>
57
+ </table>
@@ -0,0 +1,151 @@
1
+ # Adding Behaviour
2
+
3
+ Sometimes it is necessary to add extra methods to a UI
4
+ element. There are a few cases of this in the AXElements source code
5
+ itself, but many more opportunities exist. Unfortunately, extending UI
6
+ element classes in AXElements is not totally straightforward. Some of
7
+ the implementation details need to be understood before you can
8
+ successfully extend AXElements.
9
+
10
+ ## Laziness
11
+
12
+ In a laziness contest between AXElements and Garfield, AXElements
13
+ wins (I assume). A lot of data that AXElements needs to be processed,
14
+ such as name translation, and the work for this is delayed until it
15
+ needs to be done in order to avoid a very large amount of overhead at
16
+ boot time. Not all parts of AXElements are lazy, at least not yet.
17
+
18
+ ## Class Hierarchy
19
+
20
+ At run time you will have noticed that you were returned objects which
21
+ have a class like `AX::StandardWindow`, but you can never find the
22
+ definition of the class in the source code. This is because the class
23
+ hierarchy is lazily defined.
24
+
25
+ ### Deciding The Class Name
26
+
27
+ The first thing to understand is the way that AXElements decides what
28
+ class to instantiate for a UI element. This is actually pretty
29
+ simple. Each UI element has a `role` attribute that is supposed to be
30
+ used by assistive applications (read: AXElements) to understand which
31
+ attributes will likely be available. This is Apple's hint as to what
32
+ kind of class structure to choose.
33
+
34
+ However, some UI elements also have a `subrole` attribute. For
35
+ instance, `AXStandardWindow` is a subrole attribute that a UI element
36
+ can have if it has a `role` of `AXWindow`. Once again, this is a hint
37
+ that has been built into the system, and AXElements follows these
38
+ hints. Put into object-oriented terms, if an object has a `subrole`,
39
+ then that `subrole` becomes the class for the object and the `role`
40
+ becomes the superclass; if the object does not have a `subrole`, then
41
+ the class of the object will be decided by the `role`.
42
+
43
+ ### Abstract Base
44
+
45
+ In either case, the {AX::Element} class will be an ancestor for the
46
+ class that is chosen. A class that is its `subrole` will always have a
47
+ superclass that is its `role`, and a class that is a `role` will
48
+ always have {AX::Element} as its superclass.
49
+
50
+ The advantage to creating this hierarchy is that it becomes much
51
+ easier to implement searches that can find a "kind of" object. For
52
+ instance, you can search for `text_fields` and find `AX::TextField`
53
+ objects as well as `AX::SecureTextField` objects. This is one of the
54
+ more powerful features outlined in the
55
+ {file:docs/Searching.markdown Searching tutorial}.
56
+
57
+ ### Why Lazy?
58
+
59
+ Laziness was chosen as it makes the library more resilient to custom
60
+ roles and subroles. However, it also allows the MacRuby run time to
61
+ boot faster since it avoids having to load all the different classes
62
+ that would need to be defined.
63
+
64
+ ## Explicitly Defining Classes
65
+
66
+ Now that you understand how classes are structured it should be very
67
+ obvious how you should name your classes and choose your
68
+ superclass. However, you could also use this technique to customize
69
+ the hierarchy from what Apple has defined. For instance, you could force a
70
+ `AXPopUpButton` to be a subclass of `AXButton` even though Apple has
71
+ declared them to be separate roles. This may or may not be convenient
72
+ depending on what custom methods you wish to add.
73
+
74
+ ## Reasons To Add A Custom Method
75
+
76
+ For this topic I can only lead by example. Fortunately AXElements has
77
+ a few examples to show off. The full list is in
78
+ `lib/ax_elements/elements`, but I'll go over a few here.
79
+
80
+ ### Application Objects
81
+
82
+ {AX::Application} has been extended in a couple of ways. First off, in
83
+ order to provide an object oriented interface to sending keyboard
84
+ events I added {AX::Application#type_string}.
85
+
86
+ However, the big change with {AX::Application} is the merging of
87
+ functionality from `NSRunningApplication`. In order to provide methods
88
+ to set focus to an application I had to cache the
89
+ `NSRunningApplication` instance at initialization and forward some
90
+ method calls to that object. For instance, when you call `#set_focus`
91
+ and pass an application object, AXElements does not actually using
92
+ accessibility to set focus to the application, it uses the
93
+ `NSRunningApplication` class internally still support the
94
+ functionality in a transparent way.
95
+
96
+ ### Overriding `#==`
97
+
98
+ The most popular customization to make is to overload `#==` for an
99
+ object class that provides a more natural interface, and also one that
100
+ makes search much more flexible. There is more than one example in
101
+ this case, you could look at {AX::StaticText#==} which allows you
102
+ check equality against a string that equal to the `value` attribute
103
+ for the static text object. Similarly, {AX::Button#==} was added to
104
+ check equality with buttons.
105
+
106
+ ### Table Rows
107
+
108
+ When working with `AX::Table` objects you may have issues identifying
109
+ children that belong to a specific column. Children of table rows, in
110
+ my experience, do not have descriptions identifying which column they
111
+ belong to. In cases where you have no unique identifier for a child,
112
+ such as if you have multiple check boxes, there is no good way to find
113
+ a specific check box using the built in search mechanics.
114
+
115
+ You could hope that the column order never changes and just use the
116
+ index of the children array but that is fragile; or perhaps you actually
117
+ know what the order of the columns is to begin with and were able to
118
+ keep track of how they changed.
119
+
120
+ A much more sane way to identify the child is by identifying the
121
+ column that the child belongs to. For instance, a the column for a
122
+ table is an `AX::Column` object that usually has a `header` or `title`
123
+ attribute which will be unique for the table. For this case,
124
+ AXElements includes the {AX::Row#child_in_column} method which
125
+ provides something similar to a search but with the few extra steps
126
+ that would be necessary to correlate the child to the column and then
127
+ return the child that you wanted.
128
+
129
+ ### More
130
+
131
+ There are likely other cases that I have not come across yet which
132
+ would be significantly simplified by a helper method or the merging of
133
+ functionality from another class. Don't be afraid to share your
134
+ extensions.
135
+
136
+ ## Tests
137
+
138
+ If you are adding new features to AXElements then you should add tests
139
+ for the new features and also make sure that you don't break existing
140
+ features without realizing it.
141
+
142
+ Running the test suite is covered in the {file:README.markdown}.
143
+
144
+ Figuring out the test suite internals may not be easy, there is a bit
145
+ of duplication, and something things need better organization. The
146
+ test suite isn't well documented (on purpose) so you will have to read
147
+ some of the other code to understand how things should work before
148
+ writing your own tests. Be careful not to introduce state dependencies
149
+ between tests or else you will not have a fun time tracking down why a
150
+ certain test seems fail occassionally (which is a problem I had with
151
+ notifications tests).