akephalos2-stable 2.1.1.1

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.
@@ -0,0 +1,118 @@
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.getWebRequest.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/Integer] id the frame's id or index
103
+ # @return [HtmlUnit::HtmlPage] the specified frame
104
+ # @return [nil] if no frame is found
105
+ def find_frame(id_or_index)
106
+ if id_or_index.is_a? Fixnum
107
+ frame = @_page.getFrames.get(id_or_index) rescue nil
108
+ else
109
+ frame = @_page.getFrames.find do |frame|
110
+ frame.getFrameElement.getAttribute("id") == id_or_index
111
+ end
112
+ end
113
+ frame.getEnclosedPage if frame
114
+ end
115
+
116
+ end
117
+
118
+ end
@@ -0,0 +1,93 @@
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
+
19
+ # @return [DRbObject] a new instance of Akephalos::Client from the DRb
20
+ # server
21
+ def self.new(options = {})
22
+ manager.new_client(options)
23
+ end
24
+
25
+ # Starts a remove JRuby DRb server unless already running and returns an
26
+ # instance of Akephalos::ClientManager.
27
+ #
28
+ # @return [DRbObject] an instance of Akephalos::ClientManager
29
+ def self.manager
30
+ return @manager if defined?(@manager)
31
+
32
+ server_port = start!
33
+
34
+ DRb.start_service("druby://127.0.0.1:#{find_available_port}")
35
+ manager = DRbObject.new_with_uri("druby://127.0.0.1:#{server_port}")
36
+
37
+ # We want to share our local configuration with the remote server
38
+ # process, so we share an undumped version of our configuration. This
39
+ # lets us continue to make changes locally and have them reflected in the
40
+ # remote process.
41
+ manager.configuration = Akephalos.configuration.extend(DRbUndumped)
42
+
43
+ @manager = manager
44
+ end
45
+
46
+ # Start a remote server process and return when it is available for use.
47
+ def self.start!
48
+ port = find_available_port
49
+
50
+ remote_client = IO.popen("ruby #{Akephalos::BIN_DIR + 'akephalos'} #{port}")
51
+
52
+ # Set up a monitor thread to detect if the forked server exits
53
+ # prematurely.
54
+ server_monitor = Thread.new { Thread.current[:exited] = Process.wait(remote_client.pid) }
55
+
56
+ # Wait for the server to be accessible on the socket we specified.
57
+ until responsive?(port)
58
+ exit!(1) if server_monitor[:exited]
59
+ sleep 0.5
60
+ end
61
+ server_monitor.kill
62
+
63
+ # Ensure that the remote server shuts down gracefully when we are
64
+ # finished.
65
+ at_exit { Process.kill(:INT, remote_client.pid) }
66
+
67
+ port
68
+ end
69
+
70
+ private
71
+
72
+ # @api private
73
+ # @param [Integer] port the port to check for responsiveness
74
+ # @return [true, false] whether the port is responsive
75
+ def self.responsive?(port)
76
+ socket = TCPSocket.open('127.0.0.1', port)
77
+ true
78
+ rescue Errno::ECONNREFUSED
79
+ false
80
+ ensure
81
+ socket.close if socket
82
+ end
83
+
84
+ # @api private
85
+ # @return [Integer] the next available port
86
+ def self.find_available_port
87
+ server = TCPServer.new('127.0.0.1', 0)
88
+ server.addr[1]
89
+ ensure
90
+ server.close if server
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,79 @@
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
+ # The ClientManager is shared over DRb with the remote process, and
27
+ # facilitates communication between the processes.
28
+ #
29
+ # @api private
30
+ class ClientManager
31
+ include DRbUndumped
32
+
33
+ # @return [Akephalos::Client] a new client instance
34
+ def self.new_client(options = {})
35
+ # Store the client to ensure it isn't prematurely garbage collected.
36
+ @client = Client.new(options)
37
+ end
38
+
39
+ # Set the global configuration settings for Akephalos.
40
+ #
41
+ # @param [Hash] config the configuration settings
42
+ # @return [Hash] the configuration
43
+ def self.configuration=(config)
44
+ Akephalos.configuration = config
45
+ end
46
+
47
+ end
48
+
49
+ # Akephalos::Server is used by `akephalos --server` to start a DRb server
50
+ # serving Akephalos::ClientManager.
51
+ class Server
52
+
53
+ # Start DRb service for Akephalos::ClientManager.
54
+ #
55
+ # @param [String] port attach server to
56
+ def self.start!(port)
57
+ abort_on_parent_exit!
58
+ DRb.start_service("druby://127.0.0.1:#{port}", ClientManager)
59
+ DRb.thread.join
60
+ end
61
+
62
+ private
63
+
64
+ # Exit if STDIN is no longer readable, which corresponds to the process
65
+ # which started the server exiting prematurely.
66
+ #
67
+ # @api private
68
+ def self.abort_on_parent_exit!
69
+ Thread.new do
70
+ begin
71
+ STDIN.read
72
+ rescue IOError
73
+ exit
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,3 @@
1
+ module Akephalos #:nodoc
2
+ VERSION = "2.1.1.1"
3
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: akephalos2-stable
3
+ version: !ruby/object:Gem::Version
4
+ hash: 97
5
+ prerelease:
6
+ segments:
7
+ - 2
8
+ - 1
9
+ - 1
10
+ - 1
11
+ version: 2.1.1.1
12
+ platform: ruby
13
+ authors:
14
+ - Bernerd Schaefer
15
+ - "Gonzalo Rodr\xC3\xADguez-Baltan\xC3\xA1s D\xC3\xADaz"
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2011-12-05 00:00:00 Z
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: capybara
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
33
+ version: "0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: jruby-jars
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ type: :runtime
63
+ version_requirements: *id003
64
+ - !ruby/object:Gem::Dependency
65
+ name: sinatra
66
+ prerelease: false
67
+ requirement: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ type: :development
77
+ version_requirements: *id004
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec
80
+ prerelease: false
81
+ requirement: &id005 !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ type: :development
91
+ version_requirements: *id005
92
+ description: Headless Browser for Integration Testing with Capybara. Includes latest change from nerian which fixes html_unit downloader pulling HTMLUnit on demand instead of having it distributed in code.
93
+ email:
94
+ - bj.schaefer@gmail.com
95
+ - siotopo@gmail.com
96
+ executables:
97
+ - akephalos
98
+ extensions: []
99
+
100
+ extra_rdoc_files: []
101
+
102
+ files:
103
+ - lib/akephalos/capybara.rb
104
+ - lib/akephalos/client/cookies.rb
105
+ - lib/akephalos/client/filter.rb
106
+ - lib/akephalos/client.rb
107
+ - lib/akephalos/configuration.rb
108
+ - lib/akephalos/console.rb
109
+ - lib/akephalos/cucumber.rb
110
+ - lib/akephalos/htmlunit/ext/confirm_handler.rb
111
+ - lib/akephalos/htmlunit/ext/http_method.rb
112
+ - lib/akephalos/htmlunit.rb
113
+ - lib/akephalos/htmlunit_downloader.rb
114
+ - lib/akephalos/node.rb
115
+ - lib/akephalos/page.rb
116
+ - lib/akephalos/remote_client.rb
117
+ - lib/akephalos/server.rb
118
+ - lib/akephalos/version.rb
119
+ - lib/akephalos.rb
120
+ - README.md
121
+ - MIT_LICENSE
122
+ - bin/akephalos
123
+ homepage: https://github.com/Nerian/akephalos2
124
+ licenses: []
125
+
126
+ post_install_message:
127
+ rdoc_options: []
128
+
129
+ require_paths:
130
+ - lib
131
+ - vendor
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ hash: 3
138
+ segments:
139
+ - 0
140
+ version: "0"
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ hash: 3
147
+ segments:
148
+ - 0
149
+ version: "0"
150
+ requirements: []
151
+
152
+ rubyforge_project:
153
+ rubygems_version: 1.8.5
154
+ signing_key:
155
+ specification_version: 3
156
+ summary: Headless Browser for Integration Testing with Capybara. Includes latest change from nerian which fixes html_unit downloader pulling HTMLUnit on demand instead of having it distributed in code.
157
+ test_files: []
158
+