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
    
        data/lib/accessibility.rb
    CHANGED
    
    | @@ -1,29 +1,41 @@ | |
| 1 | 
            +
            require 'accessibility/version'
         | 
| 1 2 | 
             
            require 'ax/application'
         | 
| 2 3 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 4 | 
            +
            class << Accessibility
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              # Initialize the DEBUG value
         | 
| 7 | 
            +
              @debug = ENV.fetch 'AXDEBUG', $DEBUG
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              ##
         | 
| 10 | 
            +
              # Whether or not to turn on DEBUG features in AXElements. The
         | 
| 11 | 
            +
              # value is initially inherited from `$DEBUG` but can be overridden
         | 
| 12 | 
            +
              # by an environment variable named `AXDEBUG` or changed dynamically
         | 
| 13 | 
            +
              # at runtime.
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              # @return [Boolean]
         | 
| 16 | 
            +
              attr_accessor :debug
         | 
| 17 | 
            +
              alias_method :debug?, :debug
         | 
| 18 | 
            +
             | 
| 7 19 |  | 
| 8 20 | 
             
              # @group Finding an application object
         | 
| 9 21 |  | 
| 10 22 | 
             
              ##
         | 
| 11 23 | 
             
              # @todo Move to {AX::Aplication#initialize} eventually.
         | 
| 12 | 
            -
              # @todo Find a way for this method to work without sleeping;
         | 
| 13 | 
            -
              #       consider looping begin/rescue/end until AX starts up
         | 
| 14 | 
            -
              # @todo This needs to handle bad bundle identifier's gracefully
         | 
| 15 24 | 
             
              #
         | 
| 16 25 | 
             
              # This is the standard way of creating an application object. It will
         | 
| 17 26 | 
             
              # launch the app if it is not already running and then create the
         | 
| 18 27 | 
             
              # accessibility object.
         | 
| 19 28 | 
             
              #
         | 
| 20 | 
            -
              # However, this method is a  | 
| 29 | 
            +
              # However, this method is a bit of a hack in cases where the app is not
         | 
| 21 30 | 
             
              # already running; I've tried to register for notifications, launch
         | 
| 22 31 | 
             
              # synchronously, etc., but there is always a problem with accessibility
         | 
| 23 | 
            -
              # not being ready.
         | 
| 32 | 
            +
              # not being ready right away.
         | 
| 24 33 | 
             
              #
         | 
| 25 34 | 
             
              # If this method fails to find an app with the appropriate bundle
         | 
| 26 | 
            -
              # identifier then it will  | 
| 35 | 
            +
              # identifier then it will raise an exception. If the problem was not a
         | 
| 36 | 
            +
              # typo, then it might mean that the bundle identifier has not been
         | 
| 37 | 
            +
              # registered with the system yet and you should launch the app once
         | 
| 38 | 
            +
              # manually.
         | 
| 27 39 | 
             
              #
         | 
| 28 40 | 
             
              # @example
         | 
| 29 41 | 
             
              #
         | 
| @@ -33,17 +45,20 @@ class << self | |
| 33 45 | 
             
              # @param [String] bundle a bundle identifier
         | 
| 34 46 | 
             
              # @return [AX::Application,nil]
         | 
| 35 47 | 
             
              def application_with_bundle_identifier bundle
         | 
| 36 | 
            -
                 | 
| 37 | 
            -
                   | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
                   | 
| 48 | 
            +
                if app_running?(bundle) || launch_application(bundle)
         | 
| 49 | 
            +
                  10.times do
         | 
| 50 | 
            +
                    return AX::Application.new(bundle) if app_running? bundle
         | 
| 51 | 
            +
                    sleep 1
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                else
         | 
| 54 | 
            +
                  raise ArgumentError "Could not launch app matching bundle id `#{bundle}'"
         | 
| 41 55 | 
             
                end
         | 
| 42 56 | 
             
                nil
         | 
| 43 57 | 
             
              end
         | 
| 44 58 |  | 
| 45 59 | 
             
              ##
         | 
| 46 | 
            -
              # @deprecated  | 
| 60 | 
            +
              # @deprecated Directly initialize an {AX::Application} instance instead
         | 
| 61 | 
            +
              #             (e.g. `AX::Application.new('Terminal')`).
         | 
| 47 62 | 
             
              #
         | 
| 48 63 | 
             
              # Get the accessibility object for an application given its localized
         | 
| 49 64 | 
             
              # name. This will only work if the application is already running.
         | 
| @@ -55,6 +70,7 @@ class << self | |
| 55 70 | 
             
              # @param [String] name name of the application to launch
         | 
| 56 71 | 
             
              # @return [AX::Application,nil]
         | 
| 57 72 | 
             
              def application_with_name name
         | 
| 73 | 
            +
                $stderr.puts 'DEPRECATED: Use AX::Application.new instead'
         | 
| 58 74 | 
             
                AX::Application.new name
         | 
| 59 75 | 
             
              end
         | 
| 60 76 |  | 
| @@ -63,6 +79,16 @@ class << self | |
| 63 79 |  | 
| 64 80 | 
             
              private
         | 
| 65 81 |  | 
| 82 | 
            +
              ##
         | 
| 83 | 
            +
              # Find out if the app is running and if so, return the running application
         | 
| 84 | 
            +
              # for that bundle.
         | 
| 85 | 
            +
              #
         | 
| 86 | 
            +
              # @param [String]
         | 
| 87 | 
            +
              # @return [NSRunningApplication,nil]
         | 
| 88 | 
            +
              def app_running? bundle
         | 
| 89 | 
            +
                NSRunningApplication.runningApplicationsWithBundleIdentifier(bundle).first
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
             | 
| 66 92 | 
             
              ##
         | 
| 67 93 | 
             
              # Asynchronously launch an application given the bundle identifier.
         | 
| 68 94 | 
             
              #
         | 
| @@ -76,4 +102,3 @@ class << self | |
| 76 102 | 
             
              end
         | 
| 77 103 |  | 
| 78 104 | 
             
            end
         | 
| 79 | 
            -
            end
         | 
    
        data/lib/ax/application.rb
    CHANGED
    
    | @@ -2,28 +2,24 @@ require 'ax/element' | |
| 2 2 | 
             
            require 'accessibility/string'
         | 
| 3 3 |  | 
| 4 4 | 
             
            ##
         | 
| 5 | 
            -
            #  | 
| 5 | 
            +
            # The accessibility object representing the running application. This
         | 
| 6 | 
            +
            # class contains some additional constructors and conveniences for
         | 
| 7 | 
            +
            # Application objects.
         | 
| 6 8 | 
             
            #
         | 
| 7 9 | 
             
            # As this class has evolved, it has gathered some functionality from
         | 
| 8 | 
            -
            # the `NSRunningApplication`  | 
| 10 | 
            +
            # the `NSRunningApplication` and `NSBundle` classes.
         | 
| 9 11 | 
             
            class AX::Application < AX::Element
         | 
| 10 12 | 
             
              include Accessibility::String
         | 
| 11 13 |  | 
| 12 14 | 
             
              ##
         | 
| 13 | 
            -
              #  | 
| 14 | 
            -
              # Cached reference to the system wide object.
         | 
| 15 | 
            -
              #
         | 
| 16 | 
            -
              # @return [AXUIElementRef]
         | 
| 17 | 
            -
              SYSTEMWIDE = AXUIElementCreateSystemWide()
         | 
| 18 | 
            -
             | 
| 19 | 
            -
              ##
         | 
| 20 | 
            -
              # Overridden so that we can also cache the `NSRunningApplication`
         | 
| 21 | 
            -
              # instance for this object.
         | 
| 15 | 
            +
              # Overridden so that we can more flexibly manipulate input.
         | 
| 22 16 | 
             
              #
         | 
| 23 17 | 
             
              # You can initialize an application object with either the process
         | 
| 24 18 | 
             
              # identifier (pid) of the application, the name of the application,
         | 
| 25 19 | 
             
              # an `NSRunningApplication` instance for the application, or an
         | 
| 26 20 | 
             
              # accessibility (`AXUIElementRef`) token.
         | 
| 21 | 
            +
              #
         | 
| 22 | 
            +
              # @param [Number,String,NSRunningApplication]
         | 
| 27 23 | 
             
              def initialize arg
         | 
| 28 24 | 
             
                case arg
         | 
| 29 25 | 
             
                when Fixnum
         | 
| @@ -52,7 +48,7 @@ class AX::Application < AX::Element | |
| 52 48 | 
             
              # @group Attributes
         | 
| 53 49 |  | 
| 54 50 | 
             
              ##
         | 
| 55 | 
            -
              # Overridden to handle the {Accessibility::DSL# | 
| 51 | 
            +
              # Overridden to handle the {Accessibility::DSL#set_focus_to} case.
         | 
| 56 52 | 
             
              #
         | 
| 57 53 | 
             
              # (see AX::Element#attribute)
         | 
| 58 54 | 
             
              def attribute attr
         | 
| @@ -74,7 +70,7 @@ class AX::Application < AX::Element | |
| 74 70 |  | 
| 75 71 | 
             
              ##
         | 
| 76 72 | 
             
              # Ask the app whether or not it is the active app. This is equivalent
         | 
| 77 | 
            -
              # to the dynamic  | 
| 73 | 
            +
              # to the dynamic `#focused?` method, but might make more sense to use
         | 
| 78 74 | 
             
              # in some cases.
         | 
| 79 75 | 
             
              def active?
         | 
| 80 76 | 
             
                @ref.spin_run_loop
         | 
| @@ -100,7 +96,7 @@ class AX::Application < AX::Element | |
| 100 96 | 
             
              ##
         | 
| 101 97 | 
             
              # Overridden to handle the {Accessibility::Language#set_focus} case.
         | 
| 102 98 | 
             
              #
         | 
| 103 | 
            -
              # (see AX::Element#set | 
| 99 | 
            +
              # (see AX::Element#set)
         | 
| 104 100 | 
             
              def set attr, value
         | 
| 105 101 | 
             
                case attr
         | 
| 106 102 | 
             
                when :focused
         | 
| @@ -142,30 +138,50 @@ class AX::Application < AX::Element | |
| 142 138 | 
             
              end
         | 
| 143 139 |  | 
| 144 140 | 
             
              ##
         | 
| 145 | 
            -
              # Send keyboard input to  | 
| 146 | 
            -
              # has focus will receive the key presses.
         | 
| 141 | 
            +
              # Send keyboard input to the receiver, the control in the app that
         | 
| 142 | 
            +
              # currently has focus will receive the key presses.
         | 
| 147 143 | 
             
              #
         | 
| 148 144 | 
             
              # For details on how to format the string, check out the
         | 
| 149 | 
            -
              #  | 
| 145 | 
            +
              # [Keyboarding documentation](http://github.com/Marketcircle/AXElements/wiki/Keyboarding).
         | 
| 150 146 | 
             
              #
         | 
| 151 147 | 
             
              # @return [Boolean]
         | 
| 152 148 | 
             
              def type string
         | 
| 153 149 | 
             
                @ref.post keyboard_events_for string
         | 
| 154 150 | 
             
                true
         | 
| 155 151 | 
             
              end
         | 
| 152 | 
            +
              alias_method :type_string, :type
         | 
| 156 153 |  | 
| 157 | 
            -
               | 
| 158 | 
            -
               | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 161 | 
            -
               | 
| 162 | 
            -
             | 
| 163 | 
            -
              #  | 
| 164 | 
            -
               | 
| 165 | 
            -
             | 
| 166 | 
            -
             | 
| 154 | 
            +
              ##
         | 
| 155 | 
            +
              # Press the given modifier key and hold it down while yielding to
         | 
| 156 | 
            +
              # the given block.
         | 
| 157 | 
            +
              #
         | 
| 158 | 
            +
              # @example
         | 
| 159 | 
            +
              #
         | 
| 160 | 
            +
              #   hold_key "\\CONTROL" do
         | 
| 161 | 
            +
              #     drag_mouse_to point
         | 
| 162 | 
            +
              #   end
         | 
| 163 | 
            +
              #
         | 
| 164 | 
            +
              # @param [String]
         | 
| 165 | 
            +
              # @return [Number,nil]
         | 
| 166 | 
            +
              def hold_modifier key
         | 
| 167 | 
            +
                code = EventGenerator::CUSTOM[key]
         | 
| 168 | 
            +
                raise ArgumentError, "Invalid modifier `#{key}' given" unless code
         | 
| 169 | 
            +
                @ref.post [[code, true]]
         | 
| 170 | 
            +
                yield
         | 
| 171 | 
            +
              ensure
         | 
| 172 | 
            +
                @ref.post [[code,false]] if code
         | 
| 173 | 
            +
                code
         | 
| 167 174 | 
             
              end
         | 
| 168 175 |  | 
| 176 | 
            +
              ##
         | 
| 177 | 
            +
              # Navigate the menu bar menus for the receiver and select the menu
         | 
| 178 | 
            +
              # item at the end of the given path. This method will open each menu
         | 
| 179 | 
            +
              # in the path.
         | 
| 180 | 
            +
              #
         | 
| 181 | 
            +
              # @example
         | 
| 182 | 
            +
              #
         | 
| 183 | 
            +
              #   safari.select_menu_item 'Edit', 'Find', /Google/
         | 
| 184 | 
            +
              #
         | 
| 169 185 | 
             
              # @return [AX::MenuItem]
         | 
| 170 186 | 
             
              def select_menu_item *path
         | 
| 171 187 | 
             
                target = navigate_menu *path
         | 
| @@ -173,8 +189,15 @@ class AX::Application < AX::Element | |
| 173 189 | 
             
                target
         | 
| 174 190 | 
             
              end
         | 
| 175 191 |  | 
| 192 | 
            +
              ##
         | 
| 193 | 
            +
              # Navigate the menu bar menus for the receiver. This method will not
         | 
| 194 | 
            +
              # select the last item, but it will open each menu along the path.
         | 
| 195 | 
            +
              #
         | 
| 196 | 
            +
              # You may also be interested in {#select_menu_item}.
         | 
| 197 | 
            +
              #
         | 
| 176 198 | 
             
              # @return [AX::MenuItem]
         | 
| 177 199 | 
             
              def navigate_menu *path
         | 
| 200 | 
            +
                # @todo CLEAN UP
         | 
| 178 201 | 
             
                perform :unhide # can't navigate menus unless the app is up front
         | 
| 179 202 | 
             
                current = attribute(:menu_bar).search(:menu_bar_item, title: path.shift)
         | 
| 180 203 | 
             
                path.each do |part|
         | 
| @@ -220,15 +243,13 @@ class AX::Application < AX::Element | |
| 220 243 |  | 
| 221 244 |  | 
| 222 245 | 
             
              ##
         | 
| 223 | 
            -
              # @todo Include bundle identifier?
         | 
| 224 | 
            -
              #
         | 
| 225 246 | 
             
              # Override the base class to make sure the pid is included.
         | 
| 226 247 | 
             
              def inspect
         | 
| 227 248 | 
             
                super.sub! />$/, "#{pp_checkbox(:focused)} pid=#{pid}>"
         | 
| 228 249 | 
             
              end
         | 
| 229 250 |  | 
| 230 251 | 
             
              ##
         | 
| 231 | 
            -
              # Find the element in  | 
| 252 | 
            +
              # Find the element in the receiver that is at point given.
         | 
| 232 253 | 
             
              #
         | 
| 233 254 | 
             
              # `nil` will be returned if there was nothing at that point.
         | 
| 234 255 | 
             
              #
         | 
| @@ -251,4 +272,43 @@ class AX::Application < AX::Element | |
| 251 272 | 
             
                @app.bundleIdentifier
         | 
| 252 273 | 
             
              end
         | 
| 253 274 |  | 
| 275 | 
            +
              ##
         | 
| 276 | 
            +
              # Return the `Info.plist` data for the application. This is a plist
         | 
| 277 | 
            +
              # file that all bundles in OS X must contain.
         | 
| 278 | 
            +
              #
         | 
| 279 | 
            +
              # Many bits of metadata are stored in the plist, check the
         | 
| 280 | 
            +
              # [reference](https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigFiles.html)
         | 
| 281 | 
            +
              # for more details.
         | 
| 282 | 
            +
              #
         | 
| 283 | 
            +
              # @return [Hash]
         | 
| 284 | 
            +
              def info_plist
         | 
| 285 | 
            +
                bundle.infoDictionary
         | 
| 286 | 
            +
              end
         | 
| 287 | 
            +
             | 
| 288 | 
            +
              ##
         | 
| 289 | 
            +
              # Get the version string for the application.
         | 
| 290 | 
            +
              #
         | 
| 291 | 
            +
              # @example
         | 
| 292 | 
            +
              #
         | 
| 293 | 
            +
              #   AX::Application.new("Safari").version    # => "5.2"
         | 
| 294 | 
            +
              #   AX::Application.new("Terminal").version  # => "2.2.2"
         | 
| 295 | 
            +
              #   AX::Application.new("Daylite").version   # => "3.15 (build 3664)"
         | 
| 296 | 
            +
              #
         | 
| 297 | 
            +
              # @return [String]
         | 
| 298 | 
            +
              def version
         | 
| 299 | 
            +
                bundle.objectForInfoDictionaryKey 'CFBundleShortVersionString'
         | 
| 300 | 
            +
              end
         | 
| 301 | 
            +
             | 
| 302 | 
            +
             | 
| 303 | 
            +
              private
         | 
| 304 | 
            +
             | 
| 305 | 
            +
              # @return [NSBundle]
         | 
| 306 | 
            +
              def bundle
         | 
| 307 | 
            +
                @bundle ||= NSBundle.bundleWithURL @app.bundleURL
         | 
| 308 | 
            +
              end
         | 
| 309 | 
            +
             | 
| 310 | 
            +
              # @private
         | 
| 311 | 
            +
              # @return [AXUIElementRef]
         | 
| 312 | 
            +
              SYSTEMWIDE = AXUIElementCreateSystemWide()
         | 
| 313 | 
            +
             | 
| 254 314 | 
             
            end
         | 
    
        data/lib/ax/button.rb
    CHANGED
    
    | @@ -13,8 +13,11 @@ class AX::Button < AX::Element | |
| 13 13 | 
             
              #
         | 
| 14 14 | 
             
              # @return [Boolean]
         | 
| 15 15 | 
             
              def == other
         | 
| 16 | 
            -
                 | 
| 17 | 
            -
             | 
| 16 | 
            +
                if other.kind_of? NSString
         | 
| 17 | 
            +
                  attribute(:title) == other
         | 
| 18 | 
            +
                else
         | 
| 19 | 
            +
                  super
         | 
| 20 | 
            +
                end
         | 
| 18 21 | 
             
              end
         | 
| 19 22 |  | 
| 20 23 | 
             
            end
         |