# Driver class exposed to Capybara. It implements Capybara's full driver API, # and is the entry point for interaction between the test suites and HtmlUnit. # # This class and +Capybara::Driver::Akephalos::Node+ are written to run on both # MRI and JRuby, and is agnostic whether the Akephalos::Client instance is used # directly or over DRb. class Capybara::Driver::Akephalos < Capybara::Driver::Base # Akephalos-specific implementation for Capybara's Node class. class Node < Capybara::Node # @api capybara # @param [String] name attribute name # @return [String] the attribute value def [](name) name = name.to_s case name when 'checked' node.checked? else node[name.to_s] end end # @api capybara # @return [String] the inner text of the node def text node.text end # @api capybara # @return [String] the form element's value def value node.value end # Set the form element's value. # # @api capybara # @param [String] value the form element's new value def set(value) if tag_name == 'textarea' node.value = value.to_s elsif tag_name == 'input' and type == 'radio' click elsif tag_name == 'input' and type == 'checkbox' if value != self['checked'] click end elsif tag_name == 'input' node.value = value.to_s end end # Select an option from a select box. # # @api capybara # @param [String] option the option to select def select(option) result = node.select_option(option) if result == nil options = node.options.map(&:text).join(", ") raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}" else result end end # Unselect an option from a select box. # # @api capybara # @param [String] option the option to unselect def unselect(option) unless self[:multiple] raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box." end result = node.unselect_option(option) if result == nil options = node.options.map(&:text).join(", ") raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}" else result end end # Trigger an event on the element. # # @api capybara # @param [String] event the event to trigger def trigger(event) node.fire_event(event.to_s) end # @api capybara # @return [String] the element's tag name def tag_name node.tag_name end # @api capybara # @return [true, false] the element's visiblity def visible? node.visible? end # Drag the element on top of the target element. # # @api capybara # @param [Node] element the target element def drag_to(element) trigger('mousedown') element.trigger('mousemove') element.trigger('mouseup') end # Click the element. def click node.click end private # Return all child nodes which match the selector criteria. # # @api capybara # @return [Array<Node>] the matched nodes def all_unfiltered(selector) nodes = [] node.find(selector).each { |node| nodes << Node.new(driver, node) } nodes end # @return [String] the node's type attribute def type node[:type] end end attr_reader :app, :rack_server # @return [Client] an instance of Akephalos::Client def self.driver @driver ||= Akephalos::Client.new end def initialize(app) @app = app @rack_server = Capybara::Server.new(@app) @rack_server.boot if Capybara.run_server end # Visit the given path in the browser. # # @param [String] path relative path to visit def visit(path) browser.visit(url(path)) end # @return [String] the page's original source def source page.source end # @return [String] the page's modified source def body page.modified_source end # @return [Hash{String => String}] the page's response headers def response_headers page.response_headers end # @return [Integer] the response's status code def status_code page.status_code end # Execute the given block within the context of a specified frame. # # @param [String] frame_id the frame's id # @raise [Capybara::ElementNotFound] if the frame is not found def within_frame(frame_id, &block) unless page.within_frame(frame_id, &block) raise Capybara::ElementNotFound, "Unable to find frame with id '#{frame_id}'" end end # Clear all cookie session data. def cleanup! browser.clear_cookies end # @return [String] the page's current URL def current_url page.current_url end # Search for nodes which match the given XPath selector. # # @param [String] selector XPath query # @return [Array<Node>] the matched nodes def find(selector) nodes = [] page.find(selector).each { |node| nodes << Node.new(self, node) } nodes 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) page.execute_script script 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) page.evaluate_script script end # @return the current page def page browser.page end # @return the browser def browser self.class.driver end # Disable waiting in Capybara, since waiting is handled directly by # Akephalos. # # @return [false] def wait false end private # @param [String] path # @return [String] the absolute URL for the given path def url(path) rack_server.url(path) end end