module Appium::Ios

def alert_accept

Returns:
  • (void) -
def alert_accept
  # @driver.switch_to.alert.accept
  # ".switch_to.alert" calls getAlertText so use bridge directly
  driver.send(:bridge).acceptAlert
end

def alert_accept_text

Returns:
  • (String) -
def alert_accept_text
  a = @driver.find_element(:tag_name, :alert)
  return if a.nil?
  b = a.find_elements(:tag_name, :button)
  b.last.text if b && b.size >= 1
end

def alert_click value

Returns:
  • (void) -

Parameters:
  • value (Integer, String) -- either an integer index of the button or the button's name
def alert_click value
  value = "'#{value}'" if value.is_a?(String)
  @driver.execute_script "UIATarget.localTarget().frontMostApp().alert().buttons()[#{value}].tap();"
end

def alert_dismiss

Returns:
  • (void) -
def alert_dismiss
  # @driver.switch_to.alert.dismiss
  # ".switch_to.alert" calls getAlertText so use bridge directly
  driver.send(:bridge).dismissAlert
end

def alert_dismiss_text

Returns:
  • (String) -
def alert_dismiss_text
  a = @driver.find_element(:tag_name, :alert)
  return if a.nil?
  b = a.find_elements(:tag_name, :button)
  b.first.text if b && b.size >= 1
end

def alert_text

Returns:
  • (String) -
def alert_text
  # this will call get text twice so call bridge directly
  # ".switch_to.alert" calls it once, then ".text" another time.
  # @driver.switch_to.alert.text
  driver.send(:bridge).getAlertText
end

def all_ele_js predicate

Returns:
  • (String) - the completed JavaScript program

Parameters:
  • predicate (String) -- the predicate

Other tags:
    Private: -
def all_ele_js predicate
  (<<-JS).strip # remove trailing newline
    au.mainApp.getAllWithPredicate("#{predicate}");
  JS
end

def e_textfields

Returns:
  • (Array) -
def e_textfields
  execute_script textfield_js
end

def empty ele

Other tags:
    Private: -
def empty ele
  (ele['name'] || ele['label'] || ele['value']) == nil
end

def fast_duration

Returns:
  • (Float) -
def fast_duration
  0.5
end

def find text

Returns:
  • (Element) - the first matching element

Parameters:
  • text (String) -- the text to search for
def find text
  ele = nil
  # prefer value search. this may error with:
  # Can't use in/contains operator with collection 1
  js = first_ele_js "value contains[c] '#{text}'"
  ele = ignore { execute_script js }
  # now search name and label if the value search didn't match.
  unless ele
    js = first_ele_js "name contains[c] '#{text}' || label contains[c] '#{text}'"
    ele = ignore { execute_script js }
  end
  # manually raise error if no element was found
  raise Selenium::WebDriver::Error::NoSuchElementError, 'An element could not be located on the page using the given search parameters.' unless ele
  ele
end

def find_2_eles_attr tag_name_1, tag_name_2, attribute

Returns:
  • (Array) - an array of strings containing the attribute from found elements of type tag_name.

Parameters:
  • attribute (String) -- the attribute to collect
  • tag_name_2 (String) -- the 2nd tag name to find
  • tag_name_1 (String) -- the 1st tag name to find
def find_2_eles_attr tag_name_1, tag_name_2, attribute
  # Use au.lookup(tag_name) instead of $(tag_name)
  # See https://github.com/appium/appium/issues/214
  js = %Q(
      var eles = au.lookup('#{tag_name_1}');
      eles = $(eles.concat(au.lookup('#{tag_name_2}')));
      var result = [];
      for (var a = 0, length = eles.length; a < length; a++) {
        result.push(eles[a].#{attribute}());
      }
      result
    )
  @driver.execute_script js
end

def find_eles_attr tag_name, attribute

Returns:
  • (Array) - an array of strings containing the attribute from found elements of type tag_name.

Parameters:
  • attribute (String) -- the attribute to collect
  • tag_name (String) -- the tag name to find
def find_eles_attr tag_name, attribute
  # Use au.lookup(tag_name) instead of $(tag_name)
  # See https://github.com/appium/appium/issues/214
  js = %Q(
      var eles = au.lookup('#{tag_name}');
      var result = [];
      for (var a = 0, length = eles.length; a < length; a++) {
        result.push(eles[a].#{attribute}());
      }
      result
    )
  @driver.execute_script js
end

def finds text

Returns:
  • (Array) - all matching elements

Parameters:
  • text (String) -- the text to search for
def finds text
  eles = []
  # value contains may error
  js = all_ele_js "value contains[c] '#{text}'"
  eles = ignore { execute_script js }
  js = all_ele_js "name contains[c] '#{text}' || label contains[c] '#{text}'"
  eles += ignore { execute_script js }
  eles
end

def first_ele_js predicate

Returns:
  • (String) - the completed JavaScript program

Parameters:
  • predicate (String) -- the predicate

Other tags:
    Private: -
def first_ele_js predicate
  (<<-JS).strip # remove trailing newline
     au.mainApp.getFirstWithPredicateWeighted("#{predicate}");
  JS
end

def first_textfield

Returns:
  • (Textfield) -
def first_textfield
  js = textfield_js 'r = r.length > 0 ? $(r[0]) : r;'
  execute_script(js).first
end

def fix_space s

Other tags:
    Private: -
def fix_space s
  # ints don't respond to force encoding
  return s unless s.respond_to? :force_encoding
  # char code 160 (name, label) vs 32 (value) will break comparison.
  # convert string to binary and remove 160.
  # \xC2\xA0
  s.force_encoding('binary').gsub("\xC2\xA0".force_encoding('binary'), ' ') if s
end

def get_page element=source_window(0)

Returns:
  • (String) -

Parameters:
  • element (Object) -- the element to search. omit to search everything
def get_page element=source_window(0)
  lazy_load_strings
  # @private
  def empty ele
    (ele['name'] || ele['label'] || ele['value']) == nil
  end
  # @private
  def fix_space s
    # ints don't respond to force encoding
    return s unless s.respond_to? :force_encoding
    # char code 160 (name, label) vs 32 (value) will break comparison.
    # convert string to binary and remove 160.
    # \xC2\xA0
    s.force_encoding('binary').gsub("\xC2\xA0".force_encoding('binary'), ' ') if s
  end
  unless empty(element)
    puts "#{element['type']}"
    name = fix_space element['name']
    label = fix_space element['label']
    value = fix_space element['value']
    if name == label && name == value
      puts "   name, label, value: #{name}" if name
    elsif name == label
      puts "   name, label: #{name}" if name
      puts "   value: #{value}" if value
    elsif name == value
      puts "   name, value: #{name}" if name
      puts "  label: #{label}" if label
    else
      puts "   name: #{name}" if name
      puts "  label: #{label}" if label
      puts "  value: #{value}" if value
    end
    # there may be many ids with the same value.
    # output all exact matches.
    id_matches = @strings_xml.select do |key, val|
      val == name || val == label || val == value
    end
    if id_matches && id_matches.length > 0
      match_str = ''
      # [0] = key, [1] = value
      id_matches.each do |match|
        match_str += ' ' * 7 + "#{match[0]}\n"
      end
      puts "   id: #{match_str.strip}\n"
    end
  end
  children = element['children']
  children.each { |c| get_page c } if children
  nil
end

def get_page_class

Returns a string of class counts.
def get_page_class
  r = []
  run_internal = lambda do |node|
    if node.kind_of? Array
      node.each { |node| run_internal.call node }
      return
    end
    keys = node.keys
    return if keys.empty?
    r.push node['type'] if keys.include?('type')
    run_internal.call node['children'] if keys.include?('children')
  end
  json = get_source
  run_internal.call json['children']
  res = []
  r = r.sort
  r.uniq.each do |ele|
    res.push "#{r.count(ele)}x #{ele}\n"
  end
  count_sort  = ->(one,two) { two.match(/(\d+)x/)[1].to_i <=> one.match(/(\d+)x/)[1].to_i }
  res.sort(&count_sort).join ''
end

def last_textfield

Returns:
  • (Textfield) -
def last_textfield
  js = textfield_js 'r = r.length > 0 ? $(r[r.length - 1]) : r;'
  execute_script(js).first
end

def name name

Returns:
  • (Element) - the first matching element

Parameters:
  • name (String) -- the name to search for
def name name
  mobile :findElementNameContains, name: name
end

def names name

Returns:
  • (Array) - all matching elements

Parameters:
  • name (String) -- the name to search for
def names name
  # :name is not consistent across iOS and Android so use custom JavaScript
  # https://github.com/appium/appium/issues/379
  js = all_ele_js "name contains[c] '#{name}' || label contains[c] '#{name}'"
  execute_script js
end

def page

Returns:
  • (void) -
def page
  get_page
  nil
end

def page_class

def page_class
  puts get_page_class
  nil
end

def page_window window_number=0

Parameters:
  • window_number (Integer) -- the int index of the target window
def page_window window_number=0
  get_page source_window window_number
  nil
end

def password length=1

Returns:
  • (String) - the returned string is of size length

Parameters:
  • length (Integer) -- the length of the password to generate
def password length=1
  '•' * length
end

def patch_webdriver_element

Other tags:
    Private: -
def patch_webdriver_element
  Selenium::WebDriver::Element.class_eval do
    # Cross platform way of entering text into a textfield
    def type text
      # enter text then tap window to hide the keyboard.
egin
nd the top left corner of the keyboard and move up 10 pixels (origin.y - 10)
w swipe down until the end of the window - 10 pixels.
0 to ensure we're not going outside the window bounds.
iping inside the keyboard will not dismiss it.
nd
      # type
      $driver.execute_script %(au.getElement('#{self.ref}').setValue('#{text}');)
      $driver.ignore {
        # wait 5 seconds for keyboard. if the textfield is disabled then
        # setValue will work, however the keyboard will never display
        # because users are normally not allowed to type into it.
        $driver.wait_true(5) do
          $driver.execute_script %(au.mainApp.keyboard().type() !== 'UIAElementNil')
        end
        # dismiss keyboard
        js = <<-JS
          if (au.mainApp.keyboard().type() !== "UIAElementNil") {
            var startY = au.mainApp.keyboard().rect().origin.y - 10;
            var endY = au.mainWindow.rect().size.height - 10;
            au.flickApp(0, startY, 0, endY);
          }
        JS
        $driver.execute_script js
      }
    end
  end
end

def source_window window_number=0

Returns:
  • (JSON) -

Parameters:
  • window_number (Integer) -- the int index of the target window
def source_window window_number=0
  execute_script "UIATarget.localTarget().frontMostApp().windows()[#{window_number}].getTree()"
end

def text text

Returns:
  • (Element) - the first matching element

Parameters:
  • text (String) -- the text to search for
def text text
  js = first_ele_js "value contains[c] '#{text}'"
  execute_script js
end

def textfield text

Returns:
  • (Textfield) -

Parameters:
  • text (String, Integer) -- the text to match exactly. If int then the textfield at that index is returned.
def textfield text
  # Don't use ele_index because that only works on one element type.
  # iOS needs to combine textfield and secure to match Android.
  if text.is_a? Numeric
    js = textfield_js "r = r.length > 0 ? $(r[#{text}]) : r;"
    return execute_script(js).first
  end
  textfield_include text
end

def textfield_exact text

Returns:
  • (Textfield) -

Parameters:
  • text (String) -- the text the textfield must exactly match
def textfield_exact text
  # find_ele_by_text :textfield, text
  js = %Q(
    var t = au.getElementsByXpath('textfield[@text="#{text}"]').value;
    var s = au.getElementsByXpath('secure[@text="#{text}"]').value;
    t.concat(s)[0];
  )
  execute_script js
end

def textfield_include text

Returns:
  • (Textfield) -

Parameters:
  • text (String) -- the text the textfield must include
def textfield_include text
  js = %Q(
    var t = au.getElementsByXpath('textfield[contains(@text, "#{text}")]').value;
    var s = au.getElementsByXpath('secure[contains(@text, "#{text}")]').value;
    t.concat(s)[0];
  )
  execute_script js
end

def textfield_js filter=''

Other tags:
    Private: -
def textfield_js filter=''
%Q(
  var t = au.lookup('textfield');
  var s = au.lookup('secure');
  var r = $(t.concat(s));
  #{filter}
  au._returnElems(r);
)
end

def textfields

Returns:
  • (Array) -
def textfields
  find_2_eles_attr :textfield, :secure, :text
end

def texts text

Returns:
  • (Array) - all matching elements

Parameters:
  • text (String) -- the text to search for
def texts text
  # XPath //* is not implemented on iOS
  # https://github.com/appium/appium/issues/430
  js = all_ele_js "value contains[c] '#{text}'"
  execute_script js
end

def type text

Cross platform way of entering text into a textfield
def type text
  # enter text then tap window to hide the keyboard.

he top left corner of the keyboard and move up 10 pixels (origin.y - 10)
ipe down until the end of the window - 10 pixels.
 ensure we're not going outside the window bounds.
g inside the keyboard will not dismiss it.
  # type
  $driver.execute_script %(au.getElement('#{self.ref}').setValue('#{text}');)
  $driver.ignore {
    # wait 5 seconds for keyboard. if the textfield is disabled then
    # setValue will work, however the keyboard will never display
    # because users are normally not allowed to type into it.
    $driver.wait_true(5) do
      $driver.execute_script %(au.mainApp.keyboard().type() !== 'UIAElementNil')
    end
    # dismiss keyboard
    js = <<-JS
      if (au.mainApp.keyboard().type() !== "UIAElementNil") {
        var startY = au.mainApp.keyboard().rect().origin.y - 10;
        var endY = au.mainWindow.rect().size.height - 10;
        au.flickApp(0, startY, 0, endY);
      }
    JS
    $driver.execute_script js
  }
end