Extract a Connection class for booting the server
This commit is contained in:
parent
4ca10d29ed
commit
665e0fc729
|
@ -1,6 +1,7 @@
|
|||
require "capybara"
|
||||
require "capybara/driver/webkit/version"
|
||||
require "capybara/driver/webkit/node"
|
||||
require "capybara/driver/webkit/connection"
|
||||
require "capybara/driver/webkit/browser"
|
||||
require "capybara/driver/webkit/socket_debugger"
|
||||
require "capybara/driver/webkit/cookie_jar"
|
||||
|
@ -22,7 +23,7 @@ class Capybara::Driver::Webkit
|
|||
@options = options
|
||||
@rack_server = Capybara::Server.new(@app)
|
||||
@rack_server.boot if Capybara.run_server
|
||||
@browser = options[:browser] || Browser.new
|
||||
@browser = options[:browser] || Browser.new(Connection.new(options))
|
||||
@browser.ignore_ssl_errors if options[:ignore_ssl_errors]
|
||||
end
|
||||
|
||||
|
|
|
@ -1,19 +1,9 @@
|
|||
require 'socket'
|
||||
require 'thread'
|
||||
require 'timeout'
|
||||
require 'json'
|
||||
|
||||
class Capybara::Driver::Webkit
|
||||
class Browser
|
||||
attr :server_port
|
||||
|
||||
def initialize(options = {})
|
||||
@socket_class = options[:socket_class] || TCPSocket
|
||||
@stdout = options.has_key?(:stdout) ?
|
||||
options[:stdout] :
|
||||
$stdout
|
||||
start_server
|
||||
connect
|
||||
def initialize(connection)
|
||||
@connection = connection
|
||||
end
|
||||
|
||||
def visit(url)
|
||||
|
@ -88,11 +78,11 @@ class Capybara::Driver::Webkit
|
|||
end
|
||||
|
||||
def command(name, *args)
|
||||
@socket.puts name
|
||||
@socket.puts args.size
|
||||
@connection.puts name
|
||||
@connection.puts args.size
|
||||
args.each do |arg|
|
||||
@socket.puts arg.to_s.bytesize
|
||||
@socket.print arg.to_s
|
||||
@connection.puts arg.to_s.bytesize
|
||||
@connection.print arg.to_s
|
||||
end
|
||||
check
|
||||
read_response
|
||||
|
@ -138,90 +128,8 @@ class Capybara::Driver::Webkit
|
|||
|
||||
private
|
||||
|
||||
def start_server
|
||||
pipe = fork_server
|
||||
@server_port = discover_server_port(pipe)
|
||||
@stdout_thread = Thread.new do
|
||||
Thread.current.abort_on_exception = true
|
||||
forward_stdout(pipe)
|
||||
end
|
||||
end
|
||||
|
||||
def fork_server
|
||||
server_path = File.expand_path("../../../../../bin/webkit_server", __FILE__)
|
||||
pipe, @pid = server_pipe_and_pid(server_path)
|
||||
register_shutdown_hook
|
||||
pipe
|
||||
end
|
||||
|
||||
def kill_process(pid)
|
||||
if RUBY_PLATFORM =~ /mingw32/
|
||||
Process.kill(9, pid)
|
||||
else
|
||||
Process.kill("INT", pid)
|
||||
end
|
||||
end
|
||||
|
||||
def register_shutdown_hook
|
||||
@owner_pid = Process.pid
|
||||
at_exit do
|
||||
if Process.pid == @owner_pid
|
||||
kill_process(@pid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def server_pipe_and_pid(server_path)
|
||||
cmdline = [server_path]
|
||||
pipe = IO.popen(cmdline.join(" "))
|
||||
[pipe, pipe.pid]
|
||||
end
|
||||
|
||||
def discover_server_port(read_pipe)
|
||||
return unless IO.select([read_pipe], nil, nil, 10)
|
||||
((read_pipe.first || '').match(/listening on port: (\d+)/) || [])[1].to_i
|
||||
end
|
||||
|
||||
def forward_stdout(pipe)
|
||||
while pipe_readable?(pipe)
|
||||
line = pipe.readline
|
||||
if @stdout
|
||||
@stdout.write(line)
|
||||
@stdout.flush
|
||||
end
|
||||
end
|
||||
rescue EOFError
|
||||
end
|
||||
|
||||
if !defined?(RUBY_ENGINE) || (RUBY_ENGINE == "ruby" && RUBY_VERSION <= "1.8")
|
||||
# please note the use of IO::select() here, as it is used specifically to
|
||||
# preserve correct signal handling behavior in ruby 1.8.
|
||||
# https://github.com/thibaudgg/rb-fsevent/commit/d1a868bf8dc72dbca102bedbadff76c7e6c2dc21
|
||||
# https://github.com/thibaudgg/rb-fsevent/blob/1ca42b987596f350ee7b19d8f8210b7b6ae8766b/ext/fsevent/fsevent_watch.c#L171
|
||||
def pipe_readable?(pipe)
|
||||
IO.select([pipe])
|
||||
end
|
||||
else
|
||||
def pipe_readable?(pipe)
|
||||
!pipe.eof?
|
||||
end
|
||||
end
|
||||
|
||||
def connect
|
||||
Timeout.timeout(5) do
|
||||
while @socket.nil?
|
||||
attempt_connect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def attempt_connect
|
||||
@socket = @socket_class.open("127.0.0.1", @server_port)
|
||||
rescue Errno::ECONNREFUSED
|
||||
end
|
||||
|
||||
def check
|
||||
result = @socket.gets
|
||||
result = @connection.gets
|
||||
result.strip! if result
|
||||
|
||||
if result.nil?
|
||||
|
@ -234,8 +142,8 @@ class Capybara::Driver::Webkit
|
|||
end
|
||||
|
||||
def read_response
|
||||
response_length = @socket.gets.to_i
|
||||
response = @socket.read(response_length)
|
||||
response_length = @connection.gets.to_i
|
||||
response = @connection.read(response_length)
|
||||
response.force_encoding("UTF-8") if response.respond_to?(:force_encoding)
|
||||
response
|
||||
end
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
require 'socket'
|
||||
require 'timeout'
|
||||
require 'thread'
|
||||
|
||||
class Capybara::Driver::Webkit
|
||||
class Connection
|
||||
attr_reader :port
|
||||
|
||||
def initialize(options = {})
|
||||
@socket_class = options[:socket_class] || TCPSocket
|
||||
@stdout = options.has_key?(:stdout) ?
|
||||
options[:stdout] :
|
||||
$stdout
|
||||
start_server
|
||||
connect
|
||||
end
|
||||
|
||||
def puts(string)
|
||||
@socket.puts string
|
||||
end
|
||||
|
||||
def print(string)
|
||||
@socket.print string
|
||||
end
|
||||
|
||||
def gets
|
||||
@socket.gets
|
||||
end
|
||||
|
||||
def read(length)
|
||||
@socket.read(length)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def start_server
|
||||
pipe = fork_server
|
||||
@port = discover_port(pipe)
|
||||
@stdout_thread = Thread.new do
|
||||
Thread.current.abort_on_exception = true
|
||||
forward_stdout(pipe)
|
||||
end
|
||||
end
|
||||
|
||||
def fork_server
|
||||
server_path = File.expand_path("../../../../../bin/webkit_server", __FILE__)
|
||||
pipe, @pid = server_pipe_and_pid(server_path)
|
||||
register_shutdown_hook
|
||||
pipe
|
||||
end
|
||||
|
||||
def kill_process(pid)
|
||||
if RUBY_PLATFORM =~ /mingw32/
|
||||
Process.kill(9, pid)
|
||||
else
|
||||
Process.kill("INT", pid)
|
||||
end
|
||||
end
|
||||
|
||||
def register_shutdown_hook
|
||||
@owner_pid = Process.pid
|
||||
at_exit do
|
||||
if Process.pid == @owner_pid
|
||||
kill_process(@pid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def server_pipe_and_pid(server_path)
|
||||
cmdline = [server_path]
|
||||
pipe = IO.popen(cmdline.join(" "))
|
||||
[pipe, pipe.pid]
|
||||
end
|
||||
|
||||
def discover_port(read_pipe)
|
||||
return unless IO.select([read_pipe], nil, nil, 10)
|
||||
((read_pipe.first || '').match(/listening on port: (\d+)/) || [])[1].to_i
|
||||
end
|
||||
|
||||
def forward_stdout(pipe)
|
||||
while pipe_readable?(pipe)
|
||||
line = pipe.readline
|
||||
if @stdout
|
||||
@stdout.write(line)
|
||||
@stdout.flush
|
||||
end
|
||||
end
|
||||
rescue EOFError
|
||||
end
|
||||
|
||||
def connect
|
||||
Timeout.timeout(5) do
|
||||
while @socket.nil?
|
||||
attempt_connect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def attempt_connect
|
||||
@socket = @socket_class.open("127.0.0.1", @port)
|
||||
rescue Errno::ECONNREFUSED
|
||||
end
|
||||
|
||||
if !defined?(RUBY_ENGINE) || (RUBY_ENGINE == "ruby" && RUBY_VERSION <= "1.8")
|
||||
# please note the use of IO::select() here, as it is used specifically to
|
||||
# preserve correct signal handling behavior in ruby 1.8.
|
||||
# https://github.com/thibaudgg/rb-fsevent/commit/d1a868bf8dc72dbca102bedbadff76c7e6c2dc21
|
||||
# https://github.com/thibaudgg/rb-fsevent/blob/1ca42b987596f350ee7b19d8f8210b7b6ae8766b/ext/fsevent/fsevent_watch.c#L171
|
||||
def pipe_readable?(pipe)
|
||||
IO.select([pipe])
|
||||
end
|
||||
else
|
||||
def pipe_readable?(pipe)
|
||||
!pipe.eof?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,40 +2,17 @@ require 'spec_helper'
|
|||
require 'self_signed_ssl_cert'
|
||||
require 'stringio'
|
||||
require 'capybara/driver/webkit/browser'
|
||||
require 'capybara/driver/webkit/connection'
|
||||
require 'socket'
|
||||
require 'base64'
|
||||
|
||||
describe Capybara::Driver::Webkit::Browser do
|
||||
|
||||
let(:browser) { Capybara::Driver::Webkit::Browser.new }
|
||||
let(:browser_ignore_ssl_err) {
|
||||
Capybara::Driver::Webkit::Browser.new.tap { |browser| browser.ignore_ssl_errors }
|
||||
}
|
||||
|
||||
describe '#server_port' do
|
||||
subject { browser.server_port }
|
||||
it 'returns a valid port number' do
|
||||
should be_a(Integer)
|
||||
let(:browser) { Capybara::Driver::Webkit::Browser.new(Capybara::Driver::Webkit::Connection.new) }
|
||||
let(:browser_ignore_ssl_err) do
|
||||
Capybara::Driver::Webkit::Browser.new(Capybara::Driver::Webkit::Connection.new).tap do |browser|
|
||||
browser.ignore_ssl_errors
|
||||
end
|
||||
|
||||
it 'returns a port in the allowed range' do
|
||||
should be_between 0x400, 0xffff
|
||||
end
|
||||
end
|
||||
|
||||
context 'random port' do
|
||||
it 'chooses a new port number for a new browser instance' do
|
||||
new_browser = Capybara::Driver::Webkit::Browser.new
|
||||
new_browser.server_port.should_not == browser.server_port
|
||||
end
|
||||
end
|
||||
|
||||
it 'forwards stdout to the given IO object' do
|
||||
io = StringIO.new
|
||||
new_browser = Capybara::Driver::Webkit::Browser.new(:stdout => io)
|
||||
new_browser.execute_script('console.log("hello world")')
|
||||
sleep(0.5)
|
||||
io.string.should include "hello world\n"
|
||||
end
|
||||
|
||||
context 'handling of SSL validation errors' do
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
require 'spec_helper'
|
||||
require 'capybara/driver/webkit/connection'
|
||||
|
||||
describe Capybara::Driver::Webkit::Connection do
|
||||
it "boots a server to talk to" do
|
||||
url = @rack_server.url("/")
|
||||
connection.puts "Visit"
|
||||
connection.puts 1
|
||||
connection.puts url.to_s.bytesize
|
||||
connection.print url
|
||||
connection.gets.should == "ok\n"
|
||||
connection.gets.should == "0\n"
|
||||
connection.puts "Body"
|
||||
connection.puts 0
|
||||
connection.gets.should == "ok\n"
|
||||
response_length = connection.gets.to_i
|
||||
response = connection.read(response_length)
|
||||
response.should include("Hey there")
|
||||
end
|
||||
|
||||
it 'forwards stdout to the given IO object' do
|
||||
io = StringIO.new
|
||||
redirected_connection = Capybara::Driver::Webkit::Connection.new(:stdout => io)
|
||||
script = 'console.log("hello world")'
|
||||
redirected_connection.puts "Execute"
|
||||
redirected_connection.puts 1
|
||||
redirected_connection.puts script.to_s.bytesize
|
||||
redirected_connection.print script
|
||||
sleep(0.5)
|
||||
io.string.should include "hello world\n"
|
||||
end
|
||||
|
||||
it "returns the server port" do
|
||||
connection.port.should be_between 0x400, 0xffff
|
||||
end
|
||||
|
||||
it "chooses a new port number for a new connection" do
|
||||
new_connection = Capybara::Driver::Webkit::Connection.new
|
||||
new_connection.port.should_not == connection.port
|
||||
end
|
||||
|
||||
let(:connection) { Capybara::Driver::Webkit::Connection.new }
|
||||
|
||||
before(:all) do
|
||||
@app = lambda do |env|
|
||||
body = "<html><body>Hey there</body></html>"
|
||||
[200,
|
||||
{ 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s },
|
||||
[body]]
|
||||
end
|
||||
@rack_server = Capybara::Server.new(@app)
|
||||
@rack_server.boot
|
||||
end
|
||||
end
|
|
@ -988,15 +988,15 @@ describe Capybara::Driver::Webkit do
|
|||
end
|
||||
|
||||
def make_the_server_come_back
|
||||
subject.browser.instance_variable_get(:@socket).unstub!(:gets)
|
||||
subject.browser.instance_variable_get(:@socket).unstub!(:puts)
|
||||
subject.browser.instance_variable_get(:@socket).unstub!(:print)
|
||||
subject.browser.instance_variable_get(:@connection).unstub!(:gets)
|
||||
subject.browser.instance_variable_get(:@connection).unstub!(:puts)
|
||||
subject.browser.instance_variable_get(:@connection).unstub!(:print)
|
||||
end
|
||||
|
||||
def make_the_server_go_away
|
||||
subject.browser.instance_variable_get(:@socket).stub!(:gets).and_return(nil)
|
||||
subject.browser.instance_variable_get(:@socket).stub!(:puts)
|
||||
subject.browser.instance_variable_get(:@socket).stub!(:print)
|
||||
subject.browser.instance_variable_get(:@connection).stub!(:gets).and_return(nil)
|
||||
subject.browser.instance_variable_get(:@connection).stub!(:puts)
|
||||
subject.browser.instance_variable_get(:@connection).stub!(:print)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1091,7 +1091,8 @@ describe Capybara::Driver::Webkit do
|
|||
context "with socket debugger" do
|
||||
let(:socket_debugger_class){ Capybara::Driver::Webkit::SocketDebugger }
|
||||
let(:browser_with_debugger){
|
||||
Capybara::Driver::Webkit::Browser.new(:socket_class => socket_debugger_class)
|
||||
connection = Capybara::Driver::Webkit::Connection.new(:socket_class => socket_debugger_class)
|
||||
Capybara::Driver::Webkit::Browser.new(connection)
|
||||
}
|
||||
let(:driver_with_debugger){ Capybara::Driver::Webkit.new(@app, :browser => browser_with_debugger) }
|
||||
|
||||
|
|
|
@ -22,8 +22,10 @@ end
|
|||
|
||||
require File.join(spec_dir, "spec_helper")
|
||||
|
||||
require 'capybara/driver/webkit/connection'
|
||||
require 'capybara/driver/webkit/browser'
|
||||
$webkit_browser = Capybara::Driver::Webkit::Browser.new(:socket_class => TCPSocket, :stdout => nil)
|
||||
connection = Capybara::Driver::Webkit::Connection.new(:socket_class => TCPSocket, :stdout => nil)
|
||||
$webkit_browser = Capybara::Driver::Webkit::Browser.new(connection)
|
||||
|
||||
Capybara.register_driver :reusable_webkit do |app|
|
||||
Capybara::Driver::Webkit.new(app, :browser => $webkit_browser)
|
||||
|
|
Loading…
Reference in New Issue