Module: Appium::Ios
- Defined in:
- lib/appium_lib/ios/patch.rb,
lib/appium_lib/ios/helper.rb,
lib/appium_lib/ios/element/alert.rb,
lib/appium_lib/ios/element/generic.rb,
lib/appium_lib/ios/element/textfield.rb
Instance Method Summary collapse
-
#alert_accept ⇒ void
Accept the alert.
-
#alert_accept_text ⇒ String
Get the text of the alert’s accept button.
-
#alert_click(value) ⇒ void
iOS only Tap the alert button identified by value.
-
#alert_dismiss ⇒ void
Dismiss the alert.
-
#alert_dismiss_text ⇒ String
Get the text of the alert’s dismiss button.
-
#alert_text ⇒ String
Get the alert message text.
-
#all_ele_js(predicate) ⇒ String
The completed JavaScript program.
-
#e_textfields ⇒ Array<Textfield>
Get an array of textfield elements.
- #empty(ele) ⇒ Object
-
#fast_duration ⇒ Float
The fastest duration that can be used on iOS.
-
#find(text) ⇒ Element
Return the first element matching text.
-
#find_2_eles_attr(tag_name_1, tag_name_2, attribute) ⇒ Array<String>
iOS only.
-
#find_eles_attr(tag_name, attribute) ⇒ Array<String>
iOS only.
-
#finds(text) ⇒ Array<Element>
Return all elements matching text.
-
#first_ele_js(predicate) ⇒ String
returnElems requires a wrapped $(element).
-
#first_textfield ⇒ Textfield
Get the first textfield element.
- #fix_space(s) ⇒ Object
-
#get_page(element = get_source) ⇒ String
Returns a string of interesting elements.
-
#get_page_class ⇒ Object
Returns a string of class counts.
-
#last_textfield ⇒ Textfield
Get the last textfield element.
-
#name(name) ⇒ Element
Return the first element matching name.
-
#names(name) ⇒ Array<Element>
Return all elements matching name.
-
#page ⇒ void
Prints a string of interesting elements to the console.
- #page_class ⇒ Object
-
#page_window(window_number = 0) ⇒ Object
Prints parsed page source to console.
-
#password(length = 1) ⇒ String
iOS only.
-
#patch_webdriver_element ⇒ Object
class_eval inside a method because class Selenium::WebDriver::Element will trigger as soon as the file is required.
-
#source_window(window_number = 0) ⇒ JSON
Gets the JSON source of window number.
-
#text(text) ⇒ Element
Return the first element matching text.
-
#textfield(text) ⇒ Textfield
Get the first textfield that matches text.
-
#textfield_exact(text) ⇒ Textfield
Get the first textfield that exactly matches text.
-
#textfield_include(text) ⇒ Textfield
Get the first textfield that includes text.
-
#textfield_js(filter = '') ⇒ Object
Return combined lookup of textfield and secure with an optional filter.
-
#textfields ⇒ Array<String>
Get an array of textfield texts.
-
#texts(text) ⇒ Array<Element>
Return all elements matching text.
Instance Method Details
#alert_accept ⇒ void
This method returns an undefined value.
Accept the alert.
30 31 32 33 34 |
# File 'lib/appium_lib/ios/element/alert.rb', line 30 def alert_accept # @driver.switch_to.alert.accept # ".switch_to.alert" calls getAlertText so use bridge directly driver.send(:bridge).acceptAlert end |
#alert_accept_text ⇒ String
Get the text of the alert’s accept button. The last button is considered “accept.”
39 40 41 42 43 44 |
# File 'lib/appium_lib/ios/element/alert.rb', line 39 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 |
#alert_click(value) ⇒ void
This method returns an undefined value.
iOS only Tap the alert button identified by value.
Click the ok button:
alert_click 'OK'
Click the first button:
alert_click 0
14 15 16 17 |
# File 'lib/appium_lib/ios/element/alert.rb', line 14 def alert_click value value = "'#{value}'" if value.is_a?(String) @driver.execute_script "UIATarget.localTarget().frontMostApp().alert().buttons()[#{value}].tap();" end |
#alert_dismiss ⇒ void
This method returns an undefined value.
Dismiss the alert.
48 49 50 51 52 |
# File 'lib/appium_lib/ios/element/alert.rb', line 48 def alert_dismiss # @driver.switch_to.alert.dismiss # ".switch_to.alert" calls getAlertText so use bridge directly driver.send(:bridge).dismissAlert end |
#alert_dismiss_text ⇒ String
Get the text of the alert’s dismiss button. The first button is considered “dismiss.”
57 58 59 60 61 62 |
# File 'lib/appium_lib/ios/element/alert.rb', line 57 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 |
#alert_text ⇒ String
Get the alert message text.
21 22 23 24 25 26 |
# File 'lib/appium_lib/ios/element/alert.rb', line 21 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 |
#all_ele_js(predicate) ⇒ String
Returns the completed JavaScript program.
52 53 54 55 56 |
# File 'lib/appium_lib/ios/element/generic.rb', line 52 def all_ele_js predicate (<<-JS).strip # remove trailing newline au.mainApp.getAllWithPredicate("#{predicate}"); JS end |
#e_textfields ⇒ Array<Textfield>
Get an array of textfield elements.
16 17 18 |
# File 'lib/appium_lib/ios/element/textfield.rb', line 16 def e_textfields execute_script textfield_js end |
#empty(ele) ⇒ Object
93 94 95 |
# File 'lib/appium_lib/ios/helper.rb', line 93 def empty ele (ele['name'] || ele['label'] || ele['value']) == nil end |
#fast_duration ⇒ Float
The fastest duration that can be used on iOS.
157 158 159 |
# File 'lib/appium_lib/ios/helper.rb', line 157 def fast_duration 0.5 end |
#find(text) ⇒ Element
Return the first element matching text.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/appium_lib/ios/element/generic.rb', line 61 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 |
#find_2_eles_attr(tag_name_1, tag_name_2, attribute) ⇒ Array<String>
iOS only. Android doesn’t use find_2_eles_attr. Get an array of attribute values from elements exactly matching tag name.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/appium_lib/ios/helper.rb', line 29 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 |
#find_eles_attr(tag_name, attribute) ⇒ Array<String>
iOS only. Android uses uiautomator instead of uiautomation. Get an array of attribute values from elements exactly matching tag name.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/appium_lib/ios/helper.rb', line 8 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 |
#finds(text) ⇒ Array<Element>
Return all elements matching text.
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/appium_lib/ios/element/generic.rb', line 83 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 |
#first_ele_js(predicate) ⇒ String
returnElems requires a wrapped $(element). set to empty array when length is zero to prevent hang.
UIAElementNil when not matched
-
secureTextFields
-
textFields
-
buttons
-
elements
search takes the window to search. start searching webview first. window 0 is the main window. window 1 is the 1st webview (if it exists) must break instead of return because it’s not a function.
single element length is undefined when found and 0 when not found.
43 44 45 46 47 |
# File 'lib/appium_lib/ios/element/generic.rb', line 43 def first_ele_js predicate (<<-JS).strip # remove trailing newline au.mainApp.getFirstWithPredicateWeighted("#{predicate}"); JS end |
#first_textfield ⇒ Textfield
Get the first textfield element.
22 23 24 25 |
# File 'lib/appium_lib/ios/element/textfield.rb', line 22 def first_textfield js = textfield_js 'r = r.length > 0 ? $(r[0]) : r;' execute_script(js).first end |
#fix_space(s) ⇒ Object
98 99 100 101 102 103 104 105 |
# File 'lib/appium_lib/ios/helper.rb', line 98 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 |
#get_page(element = get_source) ⇒ String
Returns a string of interesting elements. iOS only.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/appium_lib/ios/helper.rb', line 90 def get_page element=get_source # @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 end children = element['children'] children.each { |c| get_page c } if children nil end |
#get_page_class ⇒ Object
Returns a string of class counts.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/appium_lib/ios/helper.rb', line 55 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 |
#last_textfield ⇒ Textfield
Get the last textfield element.
29 30 31 32 |
# File 'lib/appium_lib/ios/element/textfield.rb', line 29 def last_textfield js = textfield_js 'r = r.length > 0 ? $(r[r.length - 1]) : r;' execute_script(js).first end |
#name(name) ⇒ Element
Return the first element matching name. on Android name is content description on iOS name is the accessibility label or the text.
117 118 119 |
# File 'lib/appium_lib/ios/element/generic.rb', line 117 def name name mobile :findElementNameContains, name: name end |
#names(name) ⇒ Array<Element>
Return all elements matching name. on Android name is content description on iOS name is the accessibility label or the text.
126 127 128 129 130 131 |
# File 'lib/appium_lib/ios/element/generic.rb', line 126 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 |
#page ⇒ void
This method returns an undefined value.
Prints a string of interesting elements to the console.
135 136 137 138 |
# File 'lib/appium_lib/ios/helper.rb', line 135 def page get_page nil end |
#page_class ⇒ Object
81 82 83 84 |
# File 'lib/appium_lib/ios/helper.rb', line 81 def page_class puts get_page_class nil end |
#page_window(window_number = 0) ⇒ Object
Prints parsed page source to console. example: page_window 0
150 151 152 153 |
# File 'lib/appium_lib/ios/helper.rb', line 150 def page_window window_number=0 get_page source_window window_number nil end |
#password(length = 1) ⇒ String
iOS only. On Android uiautomator always returns an empty string for EditText password.
Password character returned from value of UIASecureTextField
50 51 52 |
# File 'lib/appium_lib/ios/helper.rb', line 50 def password length=1 '•' * length end |
#patch_webdriver_element ⇒ Object
class_eval inside a method because class Selenium::WebDriver::Element will trigger as soon as the file is required. in contrast a method will trigger only when invoked.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/appium_lib/ios/patch.rb', line 7 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. =begin Find the top left corner of the keyboard and move up 10 pixels (origin.y - 10) now swipe down until the end of the window - 10 pixels. -10 to ensure we're not going outside the window bounds. Swiping inside the keyboard will not dismiss it. =end # type $driver.execute_script %(au.getElement('#{self.ref}').setValue('#{text}');) # wait for keyboard $driver.wait_true { $driver.execute_script %(au.mainApp.keyboard().type () !== 'UIAElementNil') } # 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 |
#source_window(window_number = 0) ⇒ JSON
Gets the JSON source of window number
143 144 145 |
# File 'lib/appium_lib/ios/helper.rb', line 143 def source_window window_number=0 execute_script "UIATarget.localTarget().frontMostApp().windows()[#{window_number}].getTree()" end |
#text(text) ⇒ Element
Return the first element matching text.
97 98 99 100 |
# File 'lib/appium_lib/ios/element/generic.rb', line 97 def text text js = first_ele_js "value contains[c] '#{text}'" execute_script js end |
#textfield(text) ⇒ Textfield
Get the first textfield that matches text.
37 38 39 40 41 42 43 44 45 46 |
# File 'lib/appium_lib/ios/element/textfield.rb', line 37 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 |
#textfield_exact(text) ⇒ Textfield
Get the first textfield that exactly matches text.
64 65 66 67 68 69 70 71 72 73 |
# File 'lib/appium_lib/ios/element/textfield.rb', line 64 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 |
#textfield_include(text) ⇒ Textfield
Get the first textfield that includes text.
51 52 53 54 55 56 57 58 59 |
# File 'lib/appium_lib/ios/element/textfield.rb', line 51 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 |
#textfield_js(filter = '') ⇒ Object
Return combined lookup of textfield and secure with an optional filter. $() wrap is required for .each
78 79 80 81 82 83 84 85 86 |
# File 'lib/appium_lib/ios/element/textfield.rb', line 78 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 |
#textfields ⇒ Array<String>
Get an array of textfield texts.
10 11 12 |
# File 'lib/appium_lib/ios/element/textfield.rb', line 10 def textfields find_2_eles_attr :textfield, :secure, :text end |
#texts(text) ⇒ Array<Element>
Return all elements matching text.
105 106 107 108 109 110 |
# File 'lib/appium_lib/ios/element/generic.rb', line 105 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 |