module Akephalos # Akephalos::Node wraps HtmlUnit's DOMNode class, providing a simple API for # interacting with an element on the page. class Node # @param [HtmlUnit::DOMNode] node def initialize(node) @nodes = [] @_node = node end # @return [true, false] whether the element is checked def checked? if @_node.respond_to?(:isChecked) @_node.isChecked else !! self[:checked] end end # @return [String] inner text of the node # Returns a textual representation of this element that represents what would # be visible to the user if this page was shown in a web browser. # For example, a single-selection select element would return the currently # selected value as text. # Note: This will cleanup/reduce whitespace def text @_node.asText end # Returns the raw text content of this node and its descendants... def text_content @_node.getTextContent end # Returns a string representation of the XML document from this element and # all it's children (recursively). The charset used is the current page encoding. def xml @_node.asXml end # Return the value of the node's attribute. # # @param [String] name attribute on node # @return [String] the value of the named attribute # @return [nil] when the node does not have the named attribute def [](name) @_node.hasAttribute(name.to_s) ? @_node.getAttribute(name.to_s) : nil end # Return the value of a form element. If the element is a select box and # has "multiple" declared as an attribute, then all selected options will # be returned as an array. # # @return [String, Array] the node's value def value case tag_name when "select" if self[:multiple] selected_options.map { |option| option.value } else selected_option = @_node.selected_options.first selected_option ? Node.new(selected_option).value : nil end when "option" self[:value] || text when "textarea" @_node.getText else self[:value] end end # Set the value of the form input. # # @param [String] value def value=(value) case tag_name when "textarea" @_node.setText("") type(value) when "input" if file_input? @_node.setValueAttribute(value) else @_node.setValueAttribute("") type(value) end end end # Types each character into a text or input field. # # @param [String] value the string to type def type(value) value.each_char do |c| @_node.type(c) end end # @return [true, false] whether the node allows multiple-option selection (if the node is a select). def multiple_select? !self[:multiple].nil? end # @return [true, false] whether the node is a file input def file_input? tag_name == "input" && @_node.getAttribute("type") == "file" end # Unselect an option. # # @return [true, false] whether the unselection was successful def unselect @_node.setSelected(false) end # Return the option elements for a select box. # # @return [Array] the options def options @_node.getOptions.map { |node| Node.new(node) } end # Return the selected option elements for a select box. # # @return [Array] the selected options def selected_options @_node.getSelectedOptions.map { |node| Node.new(node) } end # Fire a JavaScript event on the current node. Note that you should not # prefix event names with "on", so: # # link.fire_event('mousedown') # # @param [String] JavaScript event name def fire_event(name) @_node.fireEvent(name) end # @return [String] the node's tag name def tag_name @_node.getNodeName end # @return [true, false] whether the node is visible to the user accounting # for CSS. def visible? @_node.isDisplayed end # @return [true, false] whether the node is selected to the user accounting # for CSS. def selected? if @_node.respond_to?(:isSelected) @_node.isSelected else !! self[:selected] end end # Click the node and then wait for any triggered JavaScript callbacks to # fire. def click begin @_node.click @_node.getPage.getEnclosingWindow.getJobManager.waitForJobs(1000) @_node.getPage.getEnclosingWindow.getJobManager.waitForJobsStartingBefore(1000) rescue Exception => e raise e unless e.message == 'java.lang.NullPointerException: null' end end # Search for child nodes which match the given XPath selector. # # @param [String] selector an XPath selector # @return [Array] the matched nodes def find(selector) nodes = @_node.getByXPath(selector).map { |node| Node.new(node) } @nodes << nodes nodes end # @return [String] the XPath expression for this node def xpath @_node.getCanonicalXPath end end end