Skip to content

Commit d9447a9

Browse files
Merge pull request #190 from DylanLacey/xpath
Parse HTML as HTML, not XML.
2 parents a84163b + ac9f5eb commit d9447a9

File tree

5 files changed

+121
-15
lines changed

5 files changed

+121
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Tests specifically for areas where the web_context differs in behaviour
2+
describe 'the web context' do
3+
4+
t 'get_android_inspect' do
5+
scroll_to "Views"
6+
last_text.click
7+
scroll_to 'WebView'
8+
last_text.click
9+
set_context 'WEBVIEW'
10+
current_context.must_equal 'WEBVIEW_1'
11+
get_android_inspect.split("\n").length.must_be :>=, 3
12+
end
13+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Tests specifically for areas where the web_context differs in behaviour
2+
describe 'the web context' do
3+
4+
t 'get_android_inspect' do
5+
text('Web, Use of UIWebView').click
6+
set_context 'WEBVIEW'
7+
current_context.must_equal 'WEBVIEW_1'
8+
sleep 1 #Give a chance to load
9+
page.start_with?("\nhtml\n").must_equal true
10+
end
11+
end

lib/appium_lib/android/helper.rb

+8-4
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,21 @@ def start_element name, attrs = []
8686
# if false (default) then all classes will be inspected
8787
# @return [String]
8888
def get_android_inspect class_name=false
89-
parser = @android_elements_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
90-
89+
source = get_source
90+
if source.start_with? '<html>'
91+
parser = @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Common::HTMLElements.new)
92+
else
93+
parser = @android_webview_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
94+
end
9195
parser.document.reset
9296
parser.document.filter = class_name
93-
parser.parse get_source
94-
97+
parser.parse source
9598
parser.document.result
9699
end
97100

98101
# Intended for use with console.
99102
# Inspects and prints the current page.
103+
# Will return XHTML for Web contexts because of a quirk with Nokogiri.
100104
# @option class [Symbol] the class name to filter on. case insensitive include match.
101105
# if nil (default) then all classes will be inspected
102106
# @return [void]

lib/appium_lib/common/helper.rb

+72-3
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,18 @@ def xpaths xpath_str
106106
# Prints xml of the current page
107107
# @return [void]
108108
def source
109-
doc = Nokogiri::XML(@driver.page_source) do |config|
110-
config.options = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
109+
source = @driver.page_source
110+
if source.start_with? '<html'
111+
doc = Nokogiri::HTML(source) do |config|
112+
config.options = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
113+
end
114+
puts doc.to_xhtml indent: 2
115+
else
116+
doc = Nokogiri::XML(source) do |config|
117+
config.options = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
118+
end
119+
puts doc.to_xml indent: 2
111120
end
112-
puts doc.to_xml indent: 2
113121
end
114122

115123
# Returns XML string for the current page
@@ -208,5 +216,66 @@ def resolve_id id
208216
lazy_load_strings
209217
@strings_xml[id]
210218
end
219+
220+
class HTMLElements < Nokogiri::XML::SAX::Document
221+
def filter
222+
@filter
223+
end
224+
225+
# convert to string to support symbols
226+
def filter= value
227+
# nil and false disable the filter
228+
return @filter = false unless value
229+
@filter = value.to_s.downcase
230+
end
231+
232+
def initialize
233+
reset
234+
@filter = false
235+
end
236+
237+
def reset
238+
@element_stack = []
239+
@elements_in_order = []
240+
@skip_element = false
241+
end
242+
243+
def result
244+
@elements_in_order.reduce('') do |r, e|
245+
name = e.delete :name
246+
attr_string = e.reduce('') do |string, attr|
247+
string += " #{attr[0]}: #{attr[1]}\n"
248+
end
249+
250+
unless attr_string.nil? || attr_string.empty?
251+
r += "\n#{name}\n#{attr_string}"
252+
end
253+
r
254+
end
255+
end
256+
257+
def start_element name, attrs = []
258+
@skip_element = filter && !filter.include?(name.downcase)
259+
unless @skip_element
260+
element = {name: name}
261+
attrs.each {|a| element[a[0]] = a[1]}
262+
@element_stack.push element
263+
@elements_in_order.push element
264+
end
265+
end
266+
267+
def end_element name
268+
return if filter && !filter.include?(name.downcase)
269+
element_index = @element_stack.rindex {|e| e[:name] == name}
270+
@element_stack.delete_at element_index
271+
end
272+
273+
def characters(chars)
274+
unless @skip_element
275+
element = @element_stack.last
276+
element[:text] = chars
277+
end
278+
end
279+
end
211280
end # module Common
212281
end # module Appium

lib/appium_lib/ios/helper.rb

+17-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def ios_password length=1
1818
# @option class_name [String,Symbol] the class name to filter on. case insensitive include match.
1919
# @return [String]
2020
def get_page element=source_window(0), class_name=nil
21+
puts "Called get_page on #{element}"
2122
lazy_load_strings # populate @strings_xml
2223
class_name = class_name.to_s.downcase
2324

@@ -130,16 +131,24 @@ def page opts={}
130131
window_number = -1
131132
class_name = opts
132133
end
133-
134-
if window_number == -1
135-
# if the 0th window has no children, find the next window that does.
136-
target_window = source_window 0
137-
target_window = source_window 1 if target_window['children'].empty?
138-
get_page target_window, class_name
134+
if current_context.start_with? 'WEBVIEW'
135+
s = get_source
136+
parser = @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Common::HTMLElements.new)
137+
parser.document.reset
138+
parser.document.filter = class_name
139+
parser.parse s
140+
parser.document.result
139141
else
140-
get_page source_window(window_number || 0), class_name
142+
if window_number == -1
143+
# if the 0th window has no children, find the next window that does.
144+
target_window = source_window 0
145+
target_window = source_window 1 if target_window['children'].empty?
146+
get_page target_window, class_name
147+
else
148+
get_page source_window(window_number || 0), class_name
149+
end
150+
nil
141151
end
142-
nil
143152
end
144153

145154
# Gets the JSON source of window number

0 commit comments

Comments
 (0)