akephalos-2s 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.md +56 -0
  3. data/bin/akephalos +87 -0
  4. data/lib/akephalos.rb +19 -0
  5. data/lib/akephalos/capybara.rb +293 -0
  6. data/lib/akephalos/client.rb +111 -0
  7. data/lib/akephalos/client/cookies.rb +73 -0
  8. data/lib/akephalos/client/filter.rb +120 -0
  9. data/lib/akephalos/configuration.rb +49 -0
  10. data/lib/akephalos/console.rb +32 -0
  11. data/lib/akephalos/cucumber.rb +6 -0
  12. data/lib/akephalos/htmlunit.rb +38 -0
  13. data/lib/akephalos/htmlunit/ext/http_method.rb +30 -0
  14. data/lib/akephalos/node.rb +132 -0
  15. data/lib/akephalos/page.rb +113 -0
  16. data/lib/akephalos/remote_client.rb +84 -0
  17. data/lib/akephalos/server.rb +56 -0
  18. data/lib/akephalos/version.rb +3 -0
  19. data/src/htmlunit/apache-mime4j-0.6.jar +0 -0
  20. data/src/htmlunit/commons-codec-1.4.jar +0 -0
  21. data/src/htmlunit/commons-collections-3.2.1.jar +0 -0
  22. data/src/htmlunit/commons-io-1.4.jar +0 -0
  23. data/src/htmlunit/commons-lang-2.4.jar +0 -0
  24. data/src/htmlunit/commons-logging-1.1.1.jar +0 -0
  25. data/src/htmlunit/cssparser-0.9.5.jar +0 -0
  26. data/src/htmlunit/htmlunit-2.8.jar +0 -0
  27. data/src/htmlunit/htmlunit-core-js-2.8.jar +0 -0
  28. data/src/htmlunit/httpclient-4.0.1.jar +0 -0
  29. data/src/htmlunit/httpcore-4.0.1.jar +0 -0
  30. data/src/htmlunit/httpmime-4.0.1.jar +0 -0
  31. data/src/htmlunit/nekohtml-1.9.14.jar +0 -0
  32. data/src/htmlunit/sac-1.3.jar +0 -0
  33. data/src/htmlunit/serializer-2.7.1.jar +0 -0
  34. data/src/htmlunit/xalan-2.7.1.jar +0 -0
  35. data/src/htmlunit/xercesImpl-2.9.1.jar +0 -0
  36. data/src/htmlunit/xml-apis-1.3.04.jar +0 -0
  37. metadata +164 -0
@@ -0,0 +1,132 @@
1
+ module Akephalos
2
+
3
+ # Akephalos::Node wraps HtmlUnit's DOMNode class, providing a simple API for
4
+ # interacting with an element on the page.
5
+ class Node
6
+ # @param [HtmlUnit::DOMNode] node
7
+ def initialize(node)
8
+ @nodes = []
9
+ @_node = node
10
+ end
11
+
12
+ # @return [true, false] whether the element is checked
13
+ def checked?
14
+ @_node.isChecked
15
+ end
16
+
17
+ # @return [String] inner text of the node
18
+ def text
19
+ @_node.asText
20
+ end
21
+
22
+ # Return the value of the node's attribute.
23
+ #
24
+ # @param [String] name attribute on node
25
+ # @return [String] the value of the named attribute
26
+ # @return [nil] when the node does not have the named attribute
27
+ def [](name)
28
+ @_node.hasAttribute(name.to_s) ? @_node.getAttribute(name.to_s) : nil
29
+ end
30
+
31
+ # Return the value of a form element. If the element is a select box and
32
+ # has "multiple" declared as an attribute, then all selected options will
33
+ # be returned as an array.
34
+ #
35
+ # @return [String, Array<String>] the node's value
36
+ def value
37
+ case tag_name
38
+ when "select"
39
+ if self[:multiple]
40
+ selected_options.map { |option| option.value }
41
+ else
42
+ selected_option = @_node.selected_options.first
43
+ selected_option ? Node.new(selected_option).value : nil
44
+ end
45
+ when "option"
46
+ self[:value] || text
47
+ when "textarea"
48
+ @_node.getText
49
+ else
50
+ self[:value]
51
+ end
52
+ end
53
+
54
+ # Set the value of the form input.
55
+ #
56
+ # @param [String] value
57
+ def value=(value)
58
+ case tag_name
59
+ when "textarea"
60
+ @_node.setText(value)
61
+ when "input"
62
+ @_node.setValueAttribute(value)
63
+ end
64
+ end
65
+
66
+ # @return [true, false] whether the node allows multiple-option selection (if the node is a select).
67
+ def multiple_select?
68
+ !self[:multiple].nil?
69
+ end
70
+
71
+ # Unselect an option.
72
+ #
73
+ # @return [true, false] whether the unselection was successful
74
+ def unselect
75
+ @_node.setSelected(false)
76
+ end
77
+
78
+ # Return the option elements for a select box.
79
+ #
80
+ # @return [Array<Node>] the options
81
+ def options
82
+ @_node.getOptions.map { |node| Node.new(node) }
83
+ end
84
+
85
+ # Return the selected option elements for a select box.
86
+ #
87
+ # @return [Array<Node>] the selected options
88
+ def selected_options
89
+ @_node.getSelectedOptions.map { |node| Node.new(node) }
90
+ end
91
+
92
+ # Fire a JavaScript event on the current node. Note that you should not
93
+ # prefix event names with "on", so:
94
+ #
95
+ # link.fire_event('mousedown')
96
+ #
97
+ # @param [String] JavaScript event name
98
+ def fire_event(name)
99
+ @_node.fireEvent(name)
100
+ end
101
+
102
+ # @return [String] the node's tag name
103
+ def tag_name
104
+ @_node.getNodeName
105
+ end
106
+
107
+ # @return [true, false] whether the node is visible to the user accounting
108
+ # for CSS.
109
+ def visible?
110
+ @_node.isDisplayed
111
+ end
112
+
113
+ # Click the node and then wait for any triggered JavaScript callbacks to
114
+ # fire.
115
+ def click
116
+ @_node.click
117
+ @_node.getPage.getEnclosingWindow.getJobManager.waitForJobs(1000)
118
+ @_node.getPage.getEnclosingWindow.getJobManager.waitForJobsStartingBefore(1000)
119
+ end
120
+
121
+ # Search for child nodes which match the given XPath selector.
122
+ #
123
+ # @param [String] selector an XPath selector
124
+ # @return [Array<Node>] the matched nodes
125
+ def find(selector)
126
+ nodes = @_node.getByXPath(selector).map { |node| Node.new(node) }
127
+ @nodes << nodes
128
+ nodes
129
+ end
130
+ end
131
+
132
+ end
@@ -0,0 +1,113 @@
1
+ module Akephalos
2
+
3
+ # Akephalos::Page wraps HtmlUnit's HtmlPage class, exposing an API for
4
+ # interacting with a page in the browser.
5
+ class Page
6
+ # @param [HtmlUnit::HtmlPage] page
7
+ def initialize(page)
8
+ @nodes = []
9
+ @_page = page
10
+ end
11
+
12
+ # Search for nodes which match the given XPath selector.
13
+ #
14
+ # @param [String] selector an XPath selector
15
+ # @return [Array<Node>] the matched nodes
16
+ def find(selector)
17
+ nodes = current_frame.getByXPath(selector).map { |node| Node.new(node) }
18
+ @nodes << nodes
19
+ nodes
20
+ end
21
+
22
+ # Return the page's source, including any JavaScript-triggered DOM changes.
23
+ #
24
+ # @return [String] the page's modified source
25
+ def modified_source
26
+ current_frame.asXml
27
+ end
28
+
29
+ # Return the page's source as returned by the web server.
30
+ #
31
+ # @return [String] the page's original source
32
+ def source
33
+ current_frame.getWebResponse.getContentAsString
34
+ end
35
+
36
+ # @return [Hash{String => String}] the page's response headers
37
+ def response_headers
38
+ headers = current_frame.getWebResponse.getResponseHeaders.map do |header|
39
+ [header.getName, header.getValue]
40
+ end
41
+ Hash[*headers.flatten]
42
+ end
43
+
44
+ # @return [Integer] the response's status code
45
+ def status_code
46
+ current_frame.getWebResponse.getStatusCode
47
+ end
48
+
49
+ # Execute the given block in the context of the frame specified.
50
+ #
51
+ # @param [String] frame_id the frame's id
52
+ # @return [true] if the frame is found
53
+ # @return [nil] if the frame is not found
54
+ def within_frame(frame_id)
55
+ return unless @current_frame = find_frame(frame_id)
56
+ yield
57
+ true
58
+ ensure
59
+ @current_frame = nil
60
+ end
61
+
62
+ # @return [String] the current page's URL.
63
+ def current_url
64
+ current_frame.getWebResponse.getRequestSettings.getUrl.toString
65
+ end
66
+
67
+ # Execute JavaScript against the current page, discarding any return value.
68
+ #
69
+ # @param [String] script the JavaScript to be executed
70
+ # @return [nil]
71
+ def execute_script(script)
72
+ current_frame.executeJavaScript(script)
73
+ nil
74
+ end
75
+
76
+ # Execute JavaScript against the current page and return the results.
77
+ #
78
+ # @param [String] script the JavaScript to be executed
79
+ # @return the result of the JavaScript
80
+ def evaluate_script(script)
81
+ current_frame.executeJavaScript(script).getJavaScriptResult
82
+ end
83
+
84
+ # Compare this page with an HtmlUnit page.
85
+ #
86
+ # @param [HtmlUnit::HtmlPage] other an HtmlUnit page
87
+ # @return [true, false]
88
+ def ==(other)
89
+ @_page == other
90
+ end
91
+
92
+ private
93
+
94
+ # Return the current frame. Usually just @_page, except when inside of the
95
+ # within_frame block.
96
+ #
97
+ # @return [HtmlUnit::HtmlPage] the current frame
98
+ def current_frame
99
+ @current_frame || @_page
100
+ end
101
+
102
+ # @param [String] id the frame's id
103
+ # @return [HtmlUnit::HtmlPage] the specified frame
104
+ # @return [nil] if no frame is found
105
+ def find_frame(id)
106
+ frame = @_page.getFrames.find do |frame|
107
+ frame.getFrameElement.getAttribute("id") == id
108
+ end
109
+ frame.getEnclosedPage if frame
110
+ end
111
+ end
112
+
113
+ end
@@ -0,0 +1,84 @@
1
+ require 'socket'
2
+ require 'drb/drb'
3
+
4
+ # We need to define our own NativeException class for the cases when a native
5
+ # exception is raised by the JRuby DRb server.
6
+ class NativeException < StandardError; end
7
+
8
+ module Akephalos
9
+
10
+ # The +RemoteClient+ class provides an interface to an +Akephalos::Client+
11
+ # isntance on a remote DRb server.
12
+ #
13
+ # == Usage
14
+ # client = Akephalos::RemoteClient.new
15
+ # client.visit "http://www.oinopa.com"
16
+ # client.page.source # => "<!DOCTYPE html PUBLIC..."
17
+ class RemoteClient
18
+ # Start a remote akephalos server and return the remote Akephalos::Client
19
+ # instance.
20
+ #
21
+ # @return [DRbObject] the remote client instance
22
+ def self.new
23
+ server_port = start!
24
+
25
+ DRb.start_service("druby://127.0.0.1:#{find_available_port}")
26
+ client = DRbObject.new_with_uri("druby://127.0.0.1:#{server_port}")
27
+
28
+ # We want to share our local configuration with the remote server
29
+ # process, so we share an undumped version of our configuration. This
30
+ # lets us continue to make changes locally and have them reflected in the
31
+ # remote process.
32
+ client.configuration = Akephalos.configuration.extend(DRbUndumped)
33
+
34
+ client
35
+ end
36
+
37
+ # Start a remote server process and return when it is available for use.
38
+ def self.start!
39
+ port = find_available_port
40
+
41
+ remote_client = IO.popen("#{Akephalos::BIN_DIR + 'akephalos'} #{port}")
42
+
43
+ # Set up a monitor thread to detect if the forked server exits
44
+ # prematurely.
45
+ server_monitor = Thread.new { Thread.current[:exited] = Process.wait(remote_client.pid) }
46
+
47
+ # Wait for the server to be accessible on the socket we specified.
48
+ until responsive?(port)
49
+ exit!(1) if server_monitor[:exited]
50
+ sleep 0.5
51
+ end
52
+ server_monitor.kill
53
+
54
+ # Ensure that the remote server shuts down gracefully when we are
55
+ # finished.
56
+ at_exit { Process.kill(:INT, remote_client.pid) }
57
+
58
+ port
59
+ end
60
+
61
+ private
62
+
63
+ # @api private
64
+ # @param [Integer] port the port to check for responsiveness
65
+ # @return [true, false] whether the port is responsive
66
+ def self.responsive?(port)
67
+ socket = TCPSocket.open('127.0.0.1', port)
68
+ true
69
+ rescue Errno::ECONNREFUSED
70
+ false
71
+ ensure
72
+ socket.close if socket
73
+ end
74
+
75
+ # @api private
76
+ # @return [Integer] the next available port
77
+ def self.find_available_port
78
+ server = TCPServer.new('127.0.0.1', 0)
79
+ server.addr[1]
80
+ ensure
81
+ server.close if server
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,56 @@
1
+ require "pathname"
2
+ require "drb/drb"
3
+ require "akephalos/client"
4
+
5
+ # In ruby-1.8.7 and later, the message for a NameError exception is lazily
6
+ # evaluated. There are, however, different implementations of this between ruby
7
+ # and jruby, so we realize these messages when sending over DRb.
8
+ class NameError::Message
9
+ # @note This method is called by DRb before sending the error to the remote
10
+ # connection.
11
+ # @return [String] the inner message.
12
+ def _dump
13
+ to_s
14
+ end
15
+ end
16
+
17
+ [
18
+ Akephalos::Page,
19
+ Akephalos::Node,
20
+ Akephalos::Client::Cookies,
21
+ Akephalos::Client::Cookies::Cookie
22
+ ].each { |klass| klass.send(:include, DRbUndumped) }
23
+
24
+ module Akephalos
25
+
26
+ # Akephalos::Server is used by `akephalos --server` to start a DRb server
27
+ # serving an instance of Akephalos::Client.
28
+ class Server
29
+ # Start DRb service for an Akephalos::Client.
30
+ #
31
+ # @param [String] port attach server to
32
+ def self.start!(port)
33
+ abort_on_parent_exit!
34
+ client = Client.new
35
+ DRb.start_service("druby://127.0.0.1:#{port}", client)
36
+ DRb.thread.join
37
+ end
38
+
39
+ private
40
+
41
+ # Exit if STDIN is no longer readable, which corresponds to the process
42
+ # which started the server exiting prematurely.
43
+ #
44
+ # @api private
45
+ def self.abort_on_parent_exit!
46
+ Thread.new do
47
+ begin
48
+ STDIN.read
49
+ rescue IOError
50
+ exit
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,3 @@
1
+ module Akephalos #:nodoc
2
+ VERSION = "0.2.4"
3
+ end
Binary file
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: akephalos-2s
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 4
10
+ version: 0.2.4
11
+ platform: ruby
12
+ authors:
13
+ - Bernerd Schaefer
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-10 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: capybara
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 15
30
+ segments:
31
+ - 0
32
+ - 4
33
+ - 0
34
+ version: 0.4.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: jruby-jars
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: sinatra
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: rspec
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ hash: 7
74
+ segments:
75
+ - 2
76
+ - 2
77
+ - 0
78
+ version: 2.2.0
79
+ type: :development
80
+ version_requirements: *id004
81
+ description: Headless Browser for Integration Testing with Capybara
82
+ email: bj.schaefer@gmail.com
83
+ executables:
84
+ - akephalos
85
+ extensions: []
86
+
87
+ extra_rdoc_files: []
88
+
89
+ files:
90
+ - lib/akephalos/node.rb
91
+ - lib/akephalos/version.rb
92
+ - lib/akephalos/client/filter.rb
93
+ - lib/akephalos/client/cookies.rb
94
+ - lib/akephalos/htmlunit.rb
95
+ - lib/akephalos/htmlunit/ext/http_method.rb
96
+ - lib/akephalos/configuration.rb
97
+ - lib/akephalos/page.rb
98
+ - lib/akephalos/remote_client.rb
99
+ - lib/akephalos/cucumber.rb
100
+ - lib/akephalos/console.rb
101
+ - lib/akephalos/capybara.rb
102
+ - lib/akephalos/server.rb
103
+ - lib/akephalos/client.rb
104
+ - lib/akephalos.rb
105
+ - src/htmlunit/htmlunit-2.8.jar
106
+ - src/htmlunit/httpclient-4.0.1.jar
107
+ - src/htmlunit/xalan-2.7.1.jar
108
+ - src/htmlunit/commons-logging-1.1.1.jar
109
+ - src/htmlunit/serializer-2.7.1.jar
110
+ - src/htmlunit/xml-apis-1.3.04.jar
111
+ - src/htmlunit/htmlunit-core-js-2.8.jar
112
+ - src/htmlunit/apache-mime4j-0.6.jar
113
+ - src/htmlunit/cssparser-0.9.5.jar
114
+ - src/htmlunit/nekohtml-1.9.14.jar
115
+ - src/htmlunit/httpmime-4.0.1.jar
116
+ - src/htmlunit/sac-1.3.jar
117
+ - src/htmlunit/commons-io-1.4.jar
118
+ - src/htmlunit/httpcore-4.0.1.jar
119
+ - src/htmlunit/xercesImpl-2.9.1.jar
120
+ - src/htmlunit/commons-codec-1.4.jar
121
+ - src/htmlunit/commons-collections-3.2.1.jar
122
+ - src/htmlunit/commons-lang-2.4.jar
123
+ - README.md
124
+ - MIT_LICENSE
125
+ - bin/akephalos
126
+ has_rdoc: true
127
+ homepage: http://bernerdschaefer.github.com/akephalos
128
+ licenses: []
129
+
130
+ post_install_message:
131
+ rdoc_options: []
132
+
133
+ require_paths:
134
+ - lib
135
+ - src
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ hash: 3
142
+ segments:
143
+ - 0
144
+ version: "0"
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ hash: 23
151
+ segments:
152
+ - 1
153
+ - 3
154
+ - 6
155
+ version: 1.3.6
156
+ requirements: []
157
+
158
+ rubyforge_project: akephalos-2s
159
+ rubygems_version: 1.4.2
160
+ signing_key:
161
+ specification_version: 3
162
+ summary: Headless Browser for Integration Testing with Capybara
163
+ test_files: []
164
+