module Akephalos
# Akephalos::Page wraps HtmlUnit's HtmlPage class, exposing an API for
# interacting with a page in the browser.
class Page
# @param [HtmlUnit::HtmlPage] page
def initialize(page)
@nodes = []
@_page = page
end
# Search for nodes which match the given XPath selector.
#
# @param [String] selector an XPath selector
# @return [Array] the matched nodes
def find(selector)
nodes = current_frame.getByXPath(selector).map { |node| Node.new(node) }
@nodes << nodes
nodes
end
# Return the page's source, including any JavaScript-triggered DOM changes.
#
# @return [String] the page's modified source
def modified_source
current_frame.asXml
end
# Return the page's source as returned by the web server.
#
# @return [String] the page's original source
def source
current_frame.getWebResponse.getContentAsString
end
# @return [Hash{String => String}] the page's response headers
def response_headers
headers = current_frame.getWebResponse.getResponseHeaders.map do |header|
[header.getName, header.getValue]
end
Hash[*headers.flatten]
end
# @return [Integer] the response's status code
def status_code
current_frame.getWebResponse.getStatusCode
end
# Execute the given block in the context of the frame specified.
#
# @param [String] frame_id the frame's id
# @return [true] if the frame is found
# @return [nil] if the frame is not found
def within_frame(frame_id)
return unless @current_frame = find_frame(frame_id)
yield
true
ensure
@current_frame = nil
end
# @return [String] the current page's URL.
def current_url
current_frame.getWebResponse.getWebRequest.getUrl.toString
end
# Execute JavaScript against the current page, discarding any return value.
#
# @param [String] script the JavaScript to be executed
# @return [nil]
def execute_script(script)
current_frame.executeJavaScript(script)
nil
end
# Execute JavaScript against the current page and return the results.
#
# @param [String] script the JavaScript to be executed
# @return the result of the JavaScript
def evaluate_script(script)
current_frame.executeJavaScript(script).getJavaScriptResult
end
# Compare this page with an HtmlUnit page.
#
# @param [HtmlUnit::HtmlPage] other an HtmlUnit page
# @return [true, false]
def ==(other)
@_page == other
end
private
# Return the current frame. Usually just @_page, except when inside of the
# within_frame block.
#
# @return [HtmlUnit::HtmlPage] the current frame
def current_frame
@current_frame || @_page
end
# @param [String/Integer] id the frame's id or index
# @return [HtmlUnit::HtmlPage] the specified frame
# @return [nil] if no frame is found
def find_frame(id_or_index)
if id_or_index.is_a? Fixnum
frame = @_page.getFrames.get(id_or_index) rescue nil
else
frame = @_page.getFrames.find do |frame|
frame.getFrameElement.getAttribute("id") == id_or_index
end
end
frame.getEnclosedPage if frame
end
end
end