Skip to content

Commit c352e68

Browse files
Fix uiautomator's invalid xml #243
1 parent c2b5dcb commit c352e68

File tree

3 files changed

+59
-24
lines changed

3 files changed

+59
-24
lines changed

lib/appium_lib/android/helper.rb

+41-5
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ def filter= value
1818

1919
def initialize
2020
reset
21-
@filter = false
21+
@filter = false
2222
@instance = Hash.new -1
2323
end
2424

2525
def reset
26-
@result = ''
27-
@keys = %w[text resource-id content-desc]
26+
@result = ''
27+
@keys = %w[text resource-id content-desc]
2828
@instance = Hash.new -1
2929
end
3030

@@ -83,6 +83,41 @@ def start_element name, attrs = []
8383
end
8484
end # class AndroidElements
8585

86+
# Fix uiautomator's xml dump.
87+
# https://github.com/appium/appium/issues/2822
88+
# https://code.google.com/p/android/issues/detail?id=74143
89+
def _fix_android_native_source source
90+
# <android.app.ActionBar$Tab
91+
# <android.app.ActionBar $ Tab
92+
93+
# find each closing tag that contains a dollar sign.
94+
source.scan(/<\/([^>]*\$[^>]*)>/).flatten.uniq.each do |problem_tag|
95+
# "android.app.ActionBar$Tab"
96+
before, after = problem_tag.split('$')
97+
before.strip!
98+
after.strip!
99+
100+
fixed = "#{before}.#{after}"
101+
102+
# now escape . in before/after because they're used in regex
103+
before.gsub!('.', '\.')
104+
after.gsub!('.', '\.')
105+
106+
# <android.app.ActionBar$Tab => <android.app.ActionBar.Tab
107+
# </android.app.ActionBar$Tab> => </android.app.ActionBar.Tab>
108+
source = source.gsub(/<#{before}\s*\$\s*#{after}/, "<#{fixed}").
109+
gsub(/<\/#{before}\s*\$\s*#{after}>/, "</#{fixed}>")
110+
end
111+
112+
source
113+
end
114+
115+
# Prints xml of the current page
116+
# @return [void]
117+
def source
118+
_print_source _fix_android_native_source get_source
119+
end
120+
86121
# Android only.
87122
# Returns a string containing interesting elements.
88123
# The text, content description, and id are returned.
@@ -91,10 +126,11 @@ def start_element name, attrs = []
91126
# @return [String]
92127
def get_android_inspect class_name=false
93128
source = get_source
94-
if source.start_with? '<html>'
129+
if source.start_with? '<html>' # parse html from webview
95130
parser = @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Common::HTMLElements.new)
96131
else
97-
parser = @android_webview_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
132+
source = _fix_android_native_source source
133+
parser = @android_native_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
98134
end
99135
parser.document.reset # ensure document is reset before parsing
100136
parser.document.filter = class_name

lib/appium_lib/common/helper.rb

+12-19
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,14 @@ def xpaths xpath_str
5353
find_elements :xpath, xpath_str
5454
end
5555

56-
# Prints xml of the current page
57-
# @return [void]
58-
def source
59-
source = @driver.page_source
56+
def _print_source source
57+
opts = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
6058
if source.start_with? '<html'
61-
doc = Nokogiri::HTML(source) do |config|
62-
config.options = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
63-
end
64-
puts doc.to_xhtml indent: 2
59+
doc = Nokogiri::HTML(source) { |cfg| cfg.options = opts }
6560
else
66-
doc = Nokogiri::XML(source) do |config|
67-
config.options = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
68-
end
69-
puts doc.to_xml indent: 2
61+
doc = Nokogiri::XML(source) { |cfg| cfg.options = opts }
7062
end
63+
puts doc.to_xml indent: 2
7164
end
7265

7366
# Returns XML string for the current page
@@ -185,14 +178,14 @@ def initialize
185178
end
186179

187180
def reset
188-
@element_stack = []
181+
@element_stack = []
189182
@elements_in_order = []
190-
@skip_element = false
183+
@skip_element = false
191184
end
192185

193186
def result
194187
@elements_in_order.reduce('') do |r, e|
195-
name = e.delete :name
188+
name = e.delete :name
196189
attr_string = e.reduce('') do |string, attr|
197190
string += " #{attr[0]}: #{attr[1]}\n"
198191
end
@@ -207,22 +200,22 @@ def result
207200
def start_element name, attrs = []
208201
@skip_element = filter && !filter.include?(name.downcase)
209202
unless @skip_element
210-
element = {name: name}
211-
attrs.each {|a| element[a[0]] = a[1]}
203+
element = { name: name }
204+
attrs.each { |a| element[a[0]] = a[1] }
212205
@element_stack.push element
213206
@elements_in_order.push element
214207
end
215208
end
216209

217210
def end_element name
218211
return if filter && !filter.include?(name.downcase)
219-
element_index = @element_stack.rindex {|e| e[:name] == name}
212+
element_index = @element_stack.rindex { |e| e[:name] == name }
220213
@element_stack.delete_at element_index
221214
end
222215

223216
def characters(chars)
224217
unless @skip_element
225-
element = @element_stack.last
218+
element = @element_stack.last
226219
element[:text] = chars
227220
end
228221
end

lib/appium_lib/ios/helper.rb

+6
Original file line numberDiff line numberDiff line change
@@ -419,5 +419,11 @@ def eles_with_pred predicate
419419
find_elements(:uiautomation, _all_visible_pred(predicate))
420420
end
421421

422+
# Prints xml of the current page
423+
# @return [void]
424+
def source
425+
_print_source get_source
426+
end
427+
422428
end # module Ios
423429
end # module Appium

0 commit comments

Comments
 (0)