AXElements 0.6.0beta1

Sign up to get free protection for your applications and to get access to all the features.
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).