lib/calabash-cucumber/keyboard_helpers.rb in calabash-cucumber-0.10.0.pre1 vs lib/calabash-cucumber/keyboard_helpers.rb in calabash-cucumber-0.10.0.pre2

- old
+ new

@@ -4,24 +4,59 @@ require 'calabash-cucumber/environment_helpers' require 'calabash-cucumber/utils/logging' module Calabash module Cucumber + # Collection of methods for interacting with the keyboard. + # + # There are two environmental variables you can use to control the speed of + # typing. We've gone to great lengths to provide the fastest keyboard + # entry possible. + # + # If you are having trouble with skipped or are receiving JSON octet + # errors when typing, you might be able to resolve the problems by slowing + # down the rate of typing. + # + # Example: Use keyboard_enter_char + :wait_after_char. + # + # ``` + # str.each_char do |char| + # # defaults to 0.05 seconds + # keyboard_enter_char(char, `{wait_after_char:0.5}`) + # end + # ``` + # + # Example: Use keyboard_enter_char + POST_ENTER_KEYBOARD + # + # ``` + # $ POST_ENTER_KEYBOARD=0.1 bundle exec cucumber + # str.each_char do |char| + # # defaults to 0.05 seconds + # keyboard_enter_char(char) + # end + # ``` + # + # @note + # We have an exhaustive set of keyboard related test.s The API is reasonably + # stable. We are fighting against known bugs in Apple's UIAutomation. You + # should only need to fall back to the examples below in unusual situations. module KeyboardHelpers include Calabash::Cucumber::TestsHelpers include Calabash::Cucumber::Logging + # @!visibility private KEYPLANE_NAMES = { :small_letters => 'small-letters', :capital_letters => 'capital-letters', :numbers_and_punctuation => 'numbers-and-punctuation', :first_alternate => 'first-alternate', :numbers_and_punctuation_alternate => 'numbers-and-punctuation-alternate' } - + # @!visibility private + # noinspection RubyStringKeysInHashInspection UIA_SUPPORTED_CHARS = { 'Delete' => '\b', 'Return' => '\n' # these are not supported yet and I am pretty sure that they # cannot be touched by passing an escaped character and instead @@ -30,21 +65,23 @@ #'Shift' => nil, #'International' => nil, #'More' => nil, } - - # returns a query string for detecting a keyboard + # @!visibility private + # Returns a query string for detecting a keyboard. def _qstr_for_keyboard "view:'UIKBKeyplaneView'" end - # returns +true+ if a +docked+ keyboard is visible. + # Returns true if a docked keyboard is visible. # - # a +docked+ keyboard is pinned to the bottom of the view. + # A docked keyboard is pinned to the bottom of the view. # - # keyboards on the iPhone and iPod are +docked+. + # Keyboards on the iPhone and iPod are docked. + # + # @return [Boolean] if a keyboard is visible and docked. def docked_keyboard_visible? res = query(_qstr_for_keyboard).first return false if res.nil? return true if device_family_iphone? @@ -65,87 +102,108 @@ false end end - # returns +true+ if an +undocked+ keyboard is visible. + # Returns true if an undocked keyboard is visible. # - # a +undocked+ keyboard is floats in the middle of the view + # A undocked keyboard is floats in the middle of the view. # - # returns +false+ if the device is not an iPad; all keyboards on the - # iPhone and iPod are +docked+ + # @return [Boolean] Returns false if the device is not an iPad; all + # keyboards on the iPhone and iPod are docked. def undocked_keyboard_visible? return false if device_family_iphone? res = query(_qstr_for_keyboard).first return false if res.nil? not docked_keyboard_visible? end - # returns +true+ if a +split+ keyboard is visible. + # Returns true if a split keyboard is visible. # - # a +split+ keyboard is floats in the middle of the view and is split to + # A split keyboard is floats in the middle of the view and is split to # allow faster thumb typing # - # returns +false+ if the device is not an iPad; all keyboards on the - # iPhone and iPod are +docked+ + # @return [Boolean] Returns false if the device is not an iPad; all + # keyboards on the Phone and iPod are docked and not split. def split_keyboard_visible? return false if device_family_iphone? query("view:'UIKBKeyView'").count > 0 and element_does_not_exist(_qstr_for_keyboard) end - # returns true if there is a visible keyboard + # Returns true if there is a visible keyboard. + # + # @return [Boolean] Returns true if there is a visible keyboard. def keyboard_visible? docked_keyboard_visible? or undocked_keyboard_visible? or split_keyboard_visible? end - # waits for a keyboard to appear and once it does appear waits for 0.3 - # seconds + # Waits for a keyboard to appear and once it does appear waits for + # `:post_timeout` seconds. # - # raises an error if no keyboard appears + # @see Calabash::Cucumber::WaitHelpers#wait_for for other options this + # method can handle. + # + # @param [Hash] opts controls the `wait_for` behavior + # @option opts [String] :timeout_message ('keyboard did not appear') + # Controls the message that appears in the exception. + # @option opts [Number] :post_timeout (0.3) Controls how long to wait + # _after_ the keyboard has appeared. + # + # @raise [Calabash::Cucumber::WaitHelpers::WaitError] if no keyboard appears def wait_for_keyboard(opts={}) default_opts = {:timeout_message => 'keyboard did not appear', :post_timeout => 0.3} opts = default_opts.merge(opts) wait_for(opts) do keyboard_visible? end end - # <b>DEPRECATED:</b> Use <tt>wait_for_keyboard</tt> instead. + # @deprecated 0.9.163 replaced with `wait_for_keyboard` + # @see #wait_for_keyboard def await_keyboard _deprecated('0.9.163', "use 'wait_for_keyboard' instead", :warn) wait_for_keyboard end + # @!visibility private # returns an array of possible ipad keyboard modes def _ipad_keyboard_modes [:docked, :undocked, :split] end - # returns the keyboard +mode+ + # Returns the keyboard mode. # + # @example How to use in a wait_* function. + # wait_for do + # ipad_keyboard_mode({:raise_on_no_visible_keyboard => false}) == :split + # end + # + # ``` # keyboard is pinned to bottom of the view #=> :docked # keyboard is floating in the middle of the view #=> :undocked # keyboard is floating and split #=> :split - # no keyboard and :raise_on_no_visible_keyboard == +false+ #=> :unknown + # no keyboard and :raise_on_no_visible_keyboard == false #=> :unknown + # ``` # - # raises an error if the device is not an iPad + # @raise [RuntimeError] if the device under test is not an iPad. # - # raises an error if the <tt>:raise_on_no_visible_keyboard</tt> is +true+ - # (default) and no keyboard is visible - # - # set <tt>:raise_on_no_visible_keyboard</tt> to +false+ to use in +wait+ - # functions + # @raise [RuntimeError] if `:raise_on_no_visible_keyboard` is truthy and + # no keyboard is visible. + # @param [Hash] opts controls the runtime behavior. + # @option opts [Boolean] :raise_on_no_visible_keyboard (true) set to false + # if you don't want to raise an error. + # @return [Symbol] Returns one of `{:docked | :undocked | :split | :unknown}` def ipad_keyboard_mode(opts = {}) raise 'the keyboard mode does not exist on the iphone or ipod' if device_family_iphone? default_opts = {:raise_on_no_visible_keyboard => true} - opts = default_opts.merge(opts) - if opts[:raise_on_no_visible_keyboard] + merged_opts = default_opts.merge(opts) + if merged_opts[:raise_on_no_visible_keyboard] screenshot_and_raise 'there is no visible keyboard' unless keyboard_visible? return :docked if docked_keyboard_visible? return :undocked if undocked_keyboard_visible? :split else @@ -154,19 +212,22 @@ return :split if split_keyboard_visible? :unknown end end - # ensures that there is a keyboard to enter text + # @!visibility private + # Ensures that there is a keyboard to enter text. # - # IMPORTANT will always raise an error when the keyboard is split and - # there is no <tt>run_loop</tt> i.e. +UIAutomation+ is not available + # @note + # *IMPORTANT* will always raise an error when the keyboard is split and + # there is no `run_loop`; i.e. UIAutomation is not available. # - # the default options are - # :screenshot +true+ raise with a screenshot - # :skip +false+ skip any checking (a nop) - used when iterating over - # keyplanes for keys + # @param [Hash] opts controls screenshot-ing and error raising conditions + # @option opts [Boolean] :screenshot (true) raise with a screenshot if + # a keyboard cannot be ensured + # @option opts [Boolean] :skip (false) skip any checking (a nop) - used + # when iterating over keyplanes for keys def _ensure_can_enter_text(opts={}) default_opts = {:screenshot => true, :skip => false} opts = default_opts.merge(opts) return if opts[:skip] @@ -189,27 +250,41 @@ raise msg end end end - # use keyboard to enter +chr+ + # Use keyboard to enter a character. # - # IMPORTANT: use the <tt>POST_ENTER_KEYBOARD</tt> environmental variable - # to slow down the typing; adds a wait after each character is touched. - # this can fix problems where the typing is too fast and characters are - # skipped. + # @note + # IMPORTANT: Use the `POST_ENTER_KEYBOARD` environmental variable + # to slow down the typing; adds a wait after each character is touched. + # this can fix problems where the typing is too fast and characters are + # skipped. # - # there are several special 'characters', some of which do not appear on all - # keyboards: - # * 'Delete' - # * 'Return' + # @note + # There are several special 'characters', some of which do not appear on + # all keyboards; e.g. `Delete`, `Return`. # - # raises error if there is no visible keyboard or the keyboard is not - # supported + # @note + # Since 0.9.163, this method accepts a Hash as the second parameter. The + # previous second parameter was a Boolean that controlled whether or not + # to screenshot on errors. # - # use the +should_screenshot+ to control whether or not to raise an error - # if +chr+ is not found + # @see #keyboard_enter_text + # + # @note + # You should prefer to call `keyboard_enter_text`. + # + # @raise [RuntimeError] if there is no visible keyboard + # @raise [RuntimeError] if the keyboard (layout) is not supported + # + # @param [String] chr the character to type + # @param [Hash] opts options to control the behavior of the method + # @option opts [Boolean] :should_screenshot (true) whether or not to + # screenshot on errors + # @option opts [Float] :wait_after_char ('POST_ENTER_KEYBOARD' or 0.05) + # how long to wait after a character is typed. def keyboard_enter_char(chr, opts={}) unless opts.is_a?(Hash) msg = "you should no longer pass a boolean as the second arg; pass {:should_screenshot => '#{opts}'} hash instead" _deprecated('0.9.163', msg, :warn) opts = {:should_screenshot => opts} @@ -246,10 +321,11 @@ tap_keyboard_action_key else uia_type_string(code, '') end end + # noinspection RubyStringKeysInHashInspection res = {'results' => []} else res = http({:method => :post, :path => 'keyboard'}, {:key => chr, :events => load_playback_data('touch_done')}) res = JSON.parse(res) @@ -272,13 +348,14 @@ pause = opts[:wait_after_char] sleep(pause) if pause > 0 res['results'] end - # uses the keyboard to enter +text+ + # Uses the keyboard to enter text. # - # raises an error if the text cannot be entered + # @param [String] text the text to type. + # @raise [RuntimeError] if the text cannot be typed. def keyboard_enter_text(text) _ensure_can_enter_text if uia_available? text_before = _text_from_first_responder() text_before = text_before.gsub("\n","\\n") if text_before @@ -292,63 +369,78 @@ end end end end - # touches the keyboard +action+ key + # Touches the keyboard action key. # - # the +action+ key depends on the keyboard. some examples include: + # The action key depends on the keyboard. Some examples include: + # # * Return # * Next # * Go # * Join # * Search # - # not all keyboards have an +action+ key - # raises an error if the key cannot be entered + # @note + # Not all keyboards have an action key. For example, numeric keyboards + # do not have an action key. + # + # @raise [RuntimeError] if the text cannot be typed. def tap_keyboard_action_key if uia_available? uia_type_string '\n', '', false else keyboard_enter_char 'Return' end end - # touches the keyboard +action+ key + # @deprecated 0.10.0 replaced with `tap_keyboard_action_key` + # @see #tap_keyboard_action_key # - # the +action+ key depends on the keyboard. + # Touches the keyboard action key. # - # some examples include: + # The action key depends on the keyboard. Some examples include: + # # * Return # * Next # * Go # * Join # * Search # - # not all keyboards have an +action+ key - # raises an error if the key cannot be entered + # @note + # Not all keyboards have an action key. For example, numeric keyboards + # do not have an action key. + # + # @raise [RuntimeError] if the text cannot be typed. def done tap_keyboard_action_key end - # returns the current keyplane + # @!visibility private + # Returns the current keyplane. def _current_keyplane kp_arr = _do_keyplane( lambda { query("view:'UIKBKeyplaneView'", 'keyplane', 'componentName') }, lambda { query("view:'UIKBKeyplaneView'", 'keyplane', 'name') }) kp_arr.first.downcase end - # searches the available keyplanes for +chr+ and if it is found, types it + # @!visibility private + # Searches the available keyplanes for chr and if it is found, types it. # - # this is a recursive function + # This is a recursive function. # - # IMPORTANT: use the <tt>KEYPLANE_SEARCH_STEP_PAUSE</tt> variable to - # control how quickly the next keyplane is searched. increase this value - # if you encounter problems with missed keystrokes. + # @note + # Use the `KEYPLANE_SEARCH_STEP_PAUSE` variable to control how quickly + # the next keyplane is searched. Increase this value if you encounter + # problems with missed keystrokes. # - # raises an error if the +chr+ cannot be found + # @note + # When running under instruments, this method is not called. + # + # @raise [RuntimeError] if the char cannot be found def _search_keyplanes_and_enter_char(chr, visited=Set.new) cur_kp = _current_keyplane begin keyboard_enter_char(chr, {:should_screenshot => false}) return true #found @@ -385,13 +477,14 @@ end return false end end - # process a keyplane + # @!visibility private + # Process a keyplane. # - # raises an error if there is not visible keyplane + # @raise [RuntimeError] if there is no visible keyplane def _do_keyplane(kbtree_proc, keyplane_proc) desc = query("view:'UIKBKeyplaneView'", 'keyplane') fail('No keyplane (UIKBKeyplaneView keyplane)') if desc.empty? fail('Several keyplanes (UIKBKeyplaneView keyplane)') if desc.count > 1 kp_desc = desc.first @@ -402,20 +495,23 @@ #ios4 keyplane_proc.call end end - # returns a query string for finding the iPad 'Hide keyboard' button + # @!visibility private + # Returns a query string for finding the iPad 'Hide keyboard' button. def _query_uia_hide_keyboard_button "uia.keyboard().buttons()['Hide keyboard']" end - # dismisses a iPad keyboard by touching the 'Hide keyboard' button and waits - # for the keyboard to disappear + # Dismisses a iPad keyboard by touching the 'Hide keyboard' button and waits + # for the keyboard to disappear. # - # raises an error if the device is not an iPad. the dismiss keyboard - # key does not exist on the iPhone or iPod + # @note + # the dismiss keyboard key does not exist on the iPhone or iPod + # + # @raise [RuntimeError] if the device is not an iPad def dismiss_ipad_keyboard screenshot_and_raise 'cannot dismiss keyboard on iphone' if device_family_iphone? if uia_available? send_uia_command({:command => "#{_query_uia_hide_keyboard_button}.tap()"}) @@ -427,46 +523,47 @@ wait_for(opts) do not keyboard_visible? end end - # returns the activation point of the iPad keyboard +mode+ key. + # @!visibility private + # Returns the activation point of the iPad keyboard mode key. # - # the +mode+ key is also known as the <tt>Hide keyboard</tt> key. + # The mode key is also known as the 'Hide keyboard' key. # - # raises an error when - # * the device is not an iPad - # * the app was not launched with instruments i.e. there is no <tt>run_loop</tt> + # @note + # This is only available when running under instruments. + # + # @raise [RuntimeError] when the device is not an iPad + # @raise [RuntimeError] the app was not launched with instruments def _point_for_ipad_keyboard_mode_key raise 'the keyboard mode does not exist on the on the iphone' if device_family_iphone? raise 'cannot detect keyboard mode key without launching with instruments' unless uia_available? res = send_uia_command({:command => "#{_query_uia_hide_keyboard_button}.rect()"}) origin = res['value']['origin'] {:x => origin['x'], :y => origin['y']} # this did not work. #size = res['value']['size'] - #{:x => (origin['x'] + (size['width']/2)), :y => (origin['y'] + (size['height']/2))} + #{:x => (origin['x'] (size['width']/2)), :y => (origin['y'] (size['height']/2))} end - - # returns a query string for touching one of the options that appears when - # the iPad +mode+ key is touched and held. + # @!visibility private + # Returns a query string for touching one of the options that appears when + # the iPad mode key is touched and held. # - # the +mode+ key is also know as the <tt>Hide keyboard</tt> key. + # The mode key is also know as the 'Hide keyboard' key. # - # valid arguments are: - # top_or_bottom :top | :bottom - # mode :docked | :undocked | :skipped + # @note + # This is only available when running outside of instruments. # - # use <tt>_point_for_keyboard_mode_key</tt> if there is a <tt>run_loop</tt> - # available + # @param [Symbol] top_or_bottom can be one of `{:top | :bottom}` + # @param [Symbol] mode `{:docked | :undocked | :skipped}` # - # raises an error when - # * the device is not an iPad - # * the app was launched with Instruments i.e. there is a <tt>run_loop</tt> - # * it is passed invalid arguments + # @raise [RuntimeError] the device is not an iPad + # @raise [RuntimeError] the app was not launched with instruments + # @raise [RuntimeError] the method is passed invalid arguments def _query_for_touch_for_keyboard_mode_option(top_or_bottom, mode) raise 'the keyboard mode does not exist on the iphone' if device_family_iphone? if uia_available? raise "UIA is available, use '_point_for_keyboard_mode_key' instead" @@ -490,16 +587,19 @@ :bottom => 'Split'}} mark = hash[mode][top_or_bottom] "label marked:'#{mark}'" end - # returns a query for touching the iPad keyboard +mode+ key. + # @!visibility private + # Returns a query for touching the iPad keyboard mode key. # - # the +mode+ key is also know as the <tt>Hide keyboard</tt> key. + # The mode key is also know as the 'Hide keyboard' key. # - # use <tt>_point_for_keyboard_mode_key</tt> if there is a <tt>run_loop</tt> - # available + # @note + # This is only available when running outside of instruments. Use + # ` _point_for_ipad_keyboard_mode_key` when the app is _not_ launched + # with instruments. # # raises an error when # * the device is not an iPad # * the app was launched with Instruments i.e. there is a <tt>run_loop</tt> def _query_for_keyboard_mode_key @@ -510,16 +610,17 @@ qstr = "view:'UIKBKeyView'" idx = query(qstr).count - 1 "#{qstr} index:#{idx}" end - # touches the bottom option on the popup dialog that is presented when the - # the iPad keyboard +mode+ key is touched and held. + # @!visibility private + # Touches the bottom option on the popup dialog that is presented when the + # the iPad keyboard `mode` key is touched and held. # - # the +mode+ key is also know as the <tt>Hide keyboard</tt> key. + # The `mode` key is also know as the 'Hide keyboard' key. # - # the +mode+ key allows the user to undock, dock, or split the keyboard. + # The `mode` key allows the user to undock, dock, or split the keyboard. def _touch_bottom_keyboard_mode_row mode = ipad_keyboard_mode if uia_available? start_pt = _point_for_ipad_keyboard_mode_key # there are 10 pt btw the key and the popup and the row is 50 pt @@ -532,16 +633,16 @@ sleep(0.5) end 2.times { sleep(0.5) } end - # touches the top option on the popup dialog that is presented when the - # the iPad keyboard +mode+ key is touched and held. + # Touches the top option on the popup dialog that is presented when the + # the iPad keyboard mode key is touched and held. # - # the +mode+ key is also know as the <tt>Hide keyboard</tt> key. + # The `mode` key is also know as the 'Hide keyboard' key. # - # the +mode+ key allows the user to undock, dock, or split the keyboard. + # The `mode` key allows the user to undock, dock, or split the keyboard. def _touch_top_keyboard_mode_row mode = ipad_keyboard_mode if uia_available? start_pt = _point_for_ipad_keyboard_mode_key # there are 10 pt btw the key and the popup and each row is 50 pt @@ -556,20 +657,19 @@ sleep(0.5) end 2.times { sleep(0.5) } end - # ensures that the iPad keyboard is +docked+ + # Ensures that the iPad keyboard is docked. # - # +docked+ means the keyboard is pinned to bottom of the view + # Docked means the keyboard is pinned to bottom of the view. # - # if the device is not an iPad, this is behaves like a call to - # <tt>wait_for_keyboard</tt> + # If the device is not an iPad, this is behaves like a call to + # `wait_for_keyboard`. # - # raises an error when - # * there is no visible keyboard or - # * the +docked+ keyboard cannot be achieved + # @raise [RuntimeError] if there is no visible keyboard + # @raise [RuntimeError] a docked keyboard was not achieved def ensure_docked_keyboard wait_for_keyboard return if device_family_iphone? @@ -595,20 +695,22 @@ screenshot_and_raise "expected keyboard to be ':docked' but found '#{mode}' in orientation '#{o}'" end end - # ensures that the iPad keyboard is +undocked+ + # Ensures that the iPad keyboard is undocked. # - # +undocked+ means the keyboard is floating in the middle of the view + # Undocked means the keyboard is floating in the middle of the view. # - # if the device is not an iPad, this is behaves like a call to - # <tt>wait_for_keyboard</tt> + # If the device is not an iPad, this is behaves like a call to + # `wait_for_keyboard`. # - # raises an error when - # * there is no visible keyboard or - # * the an +undocked+ keyboard cannot be achieved + # If the device is not an iPad, this is behaves like a call to + # `wait_for_keyboard`. + # + # @raise [RuntimeError] if there is no visible keyboard + # @raise [RuntimeError] an undocked keyboard was not achieved def ensure_undocked_keyboard wait_for_keyboard() return if device_family_iphone? @@ -639,21 +741,23 @@ _wait_for_keyboard_in_mode(:undocked) end - # ensures that the iPad keyboard is +split+ + # Ensures that the iPad keyboard is split. # - # +split+ means the keyboard is floating in the middle of the view and is + # Split means the keyboard is floating in the middle of the view and is # split into two sections to enable faster thumb typing. # - # if the device is not an iPad, this is behaves like a call to - # <tt>wait_for_keyboard</tt> + # If the device is not an iPad, this is behaves like a call to + # `wait_for_keyboard`. # - # raises an error when - # * there is no visible keyboard or - # * the an +undocked+ keyboard cannot be achieved + # If the device is not an iPad, this is behaves like a call to + # `wait_for_keyboard`. + # + # @raise [RuntimeError] if there is no visible keyboard + # @raise [RuntimeError] a split keyboard was not achieved def ensure_split_keyboard wait_for_keyboard return if device_family_iphone? @@ -670,10 +774,11 @@ end _wait_for_keyboard_in_mode(:split) end + # @!visibility private def _wait_for_keyboard_in_mode(mode, opts={}) default_opts = {:post_timeout => 1.0} opts = default_opts.merge(opts) begin wait_for(opts) do @@ -693,32 +798,38 @@ o = status_bar_orientation screenshot_and_raise "expected keyboard to be '#{mode}' but found '#{actual}' in orientation '#{o}'" end end - # used for detecting keyboards that are not normally visible to calabash - # e.g. the keyboard on +'z'+ + # Used for detecting keyboards that are not normally visible to calabash; + # e.g. the keyboard on the `MFMailComposeViewController` # - # IMPORTANT this should only be used when the app does not respond to - # <tt>keyboard_visible?</tt> + # @note + # IMPORTANT this should only be used when the app does not respond to + # `keyboard_visible?`. # - # raises an error if the there is no <tt>run_loop</tt> + # @see #keyboard_visible? + # + # @raise [RuntimeError] if the app was not launched with instruments def uia_keyboard_visible? unless uia_available? screenshot_and_raise 'only available if there is a run_loop i.e. the app was launched with Instruments' end res = uia_query_windows(:keyboard) not res.eql?(':nil') end - # waits for a keyboard that is not normally visible to calabash - # e.g. the keyboard on +MFMailComposeViewController+ + # Waits for a keyboard that is not normally visible to calabash; + # e.g. the keyboard on `MFMailComposeViewController`. # - # IMPORTANT this should only be used when the app does not respond to - # <tt>keyboard_visible?</tt> + # @note + # IMPORTANT this should only be used when the app does not respond to + # `keyboard_visible?`. # - # raises an error if the there is no <tt>run_loop</tt> + # @see #keyboard_visible? + # + # @raise [RuntimeError] if the app was not launched with instruments def uia_wait_for_keyboard(opts={}) unless uia_available? screenshot_and_raise 'only available if there is a run_loop i.e. the app was launched with Instruments' end default_opts = {:timeout => 10, @@ -733,30 +844,28 @@ wait_for(opts) do uia_keyboard_visible? end end - - private - - # returns the the text in the first responder + # @!visibility private + # Returns the the text in the first responder. # - # the first responder will be the +UITextField+ or +UITextView+ instance + # The first responder will be the UITextField or UITextView instance # that is associated with the visible keyboard. # - # returns +empty string+ if no +textField+ or +textView+ elements are found to be + # Teturns empty string if no textField or textView elements are found to be # the first responder. # - # raises an exception if there is no visible keyboard + # @raise [RuntimeError] if there is no visible keyboard def _text_from_first_responder raise 'there must be a visible keyboard' unless keyboard_visible? ['textField', 'textView'].each do |ui_class| res = query("#{ui_class} isFirstResponder:1", :text) return res.first unless res.empty? end #noinspection RubyUnnecessaryReturnStatement - return "" + return '' end end end end